mirror of
https://github.com/konvajs/konva.git
synced 2025-06-28 15:23:44 +08:00
Merge pull request #1637 from xkxx/master
feat: Add basic support for RTL text direction.
This commit is contained in:
commit
23cbea2dc5
@ -77,11 +77,13 @@ var CONTEXT_PROPERTIES = [
|
|||||||
'shadowBlur',
|
'shadowBlur',
|
||||||
'shadowOffsetX',
|
'shadowOffsetX',
|
||||||
'shadowOffsetY',
|
'shadowOffsetY',
|
||||||
|
'letterSpacing',
|
||||||
'lineCap',
|
'lineCap',
|
||||||
'lineDashOffset',
|
'lineDashOffset',
|
||||||
'lineJoin',
|
'lineJoin',
|
||||||
'lineWidth',
|
'lineWidth',
|
||||||
'miterLimit',
|
'miterLimit',
|
||||||
|
'direction',
|
||||||
'font',
|
'font',
|
||||||
'textAlign',
|
'textAlign',
|
||||||
'textBaseline',
|
'textBaseline',
|
||||||
@ -91,6 +93,11 @@ var CONTEXT_PROPERTIES = [
|
|||||||
] as const;
|
] as const;
|
||||||
|
|
||||||
const traceArrMax = 100;
|
const traceArrMax = 100;
|
||||||
|
|
||||||
|
interface ExtendedCanvasRenderingContext2D extends CanvasRenderingContext2D {
|
||||||
|
letterSpacing: string;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Konva wrapper around native 2d canvas context. It has almost the same API of 2d context with some additional functions.
|
* Konva wrapper around native 2d canvas context. It has almost the same API of 2d context with some additional functions.
|
||||||
* With core Konva shapes you don't need to use this object. But you will use it if you want to create
|
* With core Konva shapes you don't need to use this object. But you will use it if you want to create
|
||||||
@ -763,7 +770,7 @@ export class Context {
|
|||||||
|
|
||||||
// supported context properties
|
// supported context properties
|
||||||
type CanvasContextProps = Pick<
|
type CanvasContextProps = Pick<
|
||||||
CanvasRenderingContext2D,
|
ExtendedCanvasRenderingContext2D,
|
||||||
(typeof CONTEXT_PROPERTIES)[number]
|
(typeof CONTEXT_PROPERTIES)[number]
|
||||||
>;
|
>;
|
||||||
|
|
||||||
|
@ -22,6 +22,7 @@ export function stringToArray(string: string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface TextConfig extends ShapeConfig {
|
export interface TextConfig extends ShapeConfig {
|
||||||
|
direction?: string;
|
||||||
text?: string;
|
text?: string;
|
||||||
fontFamily?: string;
|
fontFamily?: string;
|
||||||
fontSize?: number;
|
fontSize?: number;
|
||||||
@ -41,11 +42,13 @@ export interface TextConfig extends ShapeConfig {
|
|||||||
var AUTO = 'auto',
|
var AUTO = 'auto',
|
||||||
//CANVAS = 'canvas',
|
//CANVAS = 'canvas',
|
||||||
CENTER = 'center',
|
CENTER = 'center',
|
||||||
|
INHERIT = 'inherit',
|
||||||
JUSTIFY = 'justify',
|
JUSTIFY = 'justify',
|
||||||
CHANGE_KONVA = 'Change.konva',
|
CHANGE_KONVA = 'Change.konva',
|
||||||
CONTEXT_2D = '2d',
|
CONTEXT_2D = '2d',
|
||||||
DASH = '-',
|
DASH = '-',
|
||||||
LEFT = 'left',
|
LEFT = 'left',
|
||||||
|
LTR = 'ltr',
|
||||||
TEXT = 'text',
|
TEXT = 'text',
|
||||||
TEXT_UPPER = 'Text',
|
TEXT_UPPER = 'Text',
|
||||||
TOP = 'top',
|
TOP = 'top',
|
||||||
@ -55,11 +58,13 @@ var AUTO = 'auto',
|
|||||||
PX_SPACE = 'px ',
|
PX_SPACE = 'px ',
|
||||||
SPACE = ' ',
|
SPACE = ' ',
|
||||||
RIGHT = 'right',
|
RIGHT = 'right',
|
||||||
|
RTL = 'rtl',
|
||||||
WORD = 'word',
|
WORD = 'word',
|
||||||
CHAR = 'char',
|
CHAR = 'char',
|
||||||
NONE = 'none',
|
NONE = 'none',
|
||||||
ELLIPSIS = '…',
|
ELLIPSIS = '…',
|
||||||
ATTR_CHANGE_LIST = [
|
ATTR_CHANGE_LIST = [
|
||||||
|
'direction',
|
||||||
'fontFamily',
|
'fontFamily',
|
||||||
'fontSize',
|
'fontSize',
|
||||||
'fontStyle',
|
'fontStyle',
|
||||||
@ -132,6 +137,7 @@ function checkDefaultFill(config?: TextConfig) {
|
|||||||
* @memberof Konva
|
* @memberof Konva
|
||||||
* @augments Konva.Shape
|
* @augments Konva.Shape
|
||||||
* @param {Object} config
|
* @param {Object} config
|
||||||
|
* @param {String} [config.direction] default is inherit
|
||||||
* @param {String} [config.fontFamily] default is Arial
|
* @param {String} [config.fontFamily] default is Arial
|
||||||
* @param {Number} [config.fontSize] in pixels. Default is 12
|
* @param {Number} [config.fontSize] in pixels. Default is 12
|
||||||
* @param {String} [config.fontStyle] can be 'normal', 'italic', or 'bold', '500' or even 'italic bold'. 'normal' is the default.
|
* @param {String} [config.fontStyle] can be 'normal', 'italic', or 'bold', '500' or even 'italic bold'. 'normal' is the default.
|
||||||
@ -185,6 +191,7 @@ export class Text extends Shape<TextConfig> {
|
|||||||
fontSize = this.fontSize(),
|
fontSize = this.fontSize(),
|
||||||
lineHeightPx = this.lineHeight() * fontSize,
|
lineHeightPx = this.lineHeight() * fontSize,
|
||||||
verticalAlign = this.verticalAlign(),
|
verticalAlign = this.verticalAlign(),
|
||||||
|
direction = this.direction(),
|
||||||
alignY = 0,
|
alignY = 0,
|
||||||
align = this.align(),
|
align = this.align(),
|
||||||
totalWidth = this.getWidth(),
|
totalWidth = this.getWidth(),
|
||||||
@ -195,18 +202,25 @@ export class Text extends Shape<TextConfig> {
|
|||||||
shouldLineThrough = textDecoration.indexOf('line-through') !== -1,
|
shouldLineThrough = textDecoration.indexOf('line-through') !== -1,
|
||||||
n;
|
n;
|
||||||
|
|
||||||
|
direction = direction === INHERIT ? context.direction : direction;
|
||||||
|
|
||||||
var translateY = 0;
|
var translateY = 0;
|
||||||
var translateY = lineHeightPx / 2;
|
var translateY = lineHeightPx / 2;
|
||||||
|
|
||||||
var lineTranslateX = 0;
|
var lineTranslateX = 0;
|
||||||
var lineTranslateY = 0;
|
var lineTranslateY = 0;
|
||||||
|
|
||||||
|
if (direction === RTL) {
|
||||||
|
context.setAttr('direction', direction);
|
||||||
|
}
|
||||||
|
|
||||||
context.setAttr('font', this._getContextFont());
|
context.setAttr('font', this._getContextFont());
|
||||||
|
|
||||||
context.setAttr('textBaseline', MIDDLE);
|
context.setAttr('textBaseline', MIDDLE);
|
||||||
|
|
||||||
context.setAttr('textAlign', LEFT);
|
context.setAttr('textAlign', LEFT);
|
||||||
|
|
||||||
|
|
||||||
// handle vertical alignment
|
// handle vertical alignment
|
||||||
if (verticalAlign === MIDDLE) {
|
if (verticalAlign === MIDDLE) {
|
||||||
alignY = (this.getHeight() - textArrLen * lineHeightPx - padding * 2) / 2;
|
alignY = (this.getHeight() - textArrLen * lineHeightPx - padding * 2) / 2;
|
||||||
@ -282,7 +296,10 @@ export class Text extends Shape<TextConfig> {
|
|||||||
context.stroke();
|
context.stroke();
|
||||||
context.restore();
|
context.restore();
|
||||||
}
|
}
|
||||||
if (letterSpacing !== 0 || align === JUSTIFY) {
|
// As `letterSpacing` isn't supported on Safari, we use this polyfill.
|
||||||
|
// The exception is for RTL text, which we rely on native as it cannot
|
||||||
|
// be supported otherwise.
|
||||||
|
if (direction !== RTL && (letterSpacing !== 0 || align === JUSTIFY)) {
|
||||||
// var words = text.split(' ');
|
// var words = text.split(' ');
|
||||||
spacesNumber = text.split(' ').length - 1;
|
spacesNumber = text.split(' ').length - 1;
|
||||||
var array = stringToArray(text);
|
var array = stringToArray(text);
|
||||||
@ -303,6 +320,9 @@ export class Text extends Shape<TextConfig> {
|
|||||||
lineTranslateX += this.measureSize(letter).width + letterSpacing;
|
lineTranslateX += this.measureSize(letter).width + letterSpacing;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
if (letterSpacing !== 0) {
|
||||||
|
context.setAttr('letterSpacing', `${letterSpacing}px`);
|
||||||
|
}
|
||||||
this._partialTextX = lineTranslateX;
|
this._partialTextX = lineTranslateX;
|
||||||
this._partialTextY = translateY + lineTranslateY;
|
this._partialTextY = translateY + lineTranslateY;
|
||||||
this._partialText = text;
|
this._partialText = text;
|
||||||
@ -615,6 +635,7 @@ export class Text extends Shape<TextConfig> {
|
|||||||
return super._useBufferCanvas();
|
return super._useBufferCanvas();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
direction: GetSet<string, this>;
|
||||||
fontFamily: GetSet<string, this>;
|
fontFamily: GetSet<string, this>;
|
||||||
fontSize: GetSet<number, this>;
|
fontSize: GetSet<number, this>;
|
||||||
fontStyle: GetSet<string, this>;
|
fontStyle: GetSet<string, this>;
|
||||||
@ -682,6 +703,22 @@ Factory.overWriteSetter(Text, 'width', getNumberOrAutoValidator());
|
|||||||
|
|
||||||
Factory.overWriteSetter(Text, 'height', getNumberOrAutoValidator());
|
Factory.overWriteSetter(Text, 'height', getNumberOrAutoValidator());
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get/set direction
|
||||||
|
* @name Konva.Text#direction
|
||||||
|
* @method
|
||||||
|
* @param {String} direction
|
||||||
|
* @returns {String}
|
||||||
|
* @example
|
||||||
|
* // get direction
|
||||||
|
* var direction = text.direction();
|
||||||
|
*
|
||||||
|
* // set direction
|
||||||
|
* text.direction('rtl');
|
||||||
|
*/
|
||||||
|
Factory.addGetterSetter(Text, 'direction', INHERIT);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* get/set font family
|
* get/set font family
|
||||||
* @name Konva.Text#fontFamily
|
* @name Konva.Text#fontFamily
|
||||||
|
@ -1654,4 +1654,63 @@ describe('Text', function () {
|
|||||||
|
|
||||||
assert.equal(layer.getContext().getTrace(), trace);
|
assert.equal(layer.getContext().getTrace(), trace);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('sets ltr text direction', function () {
|
||||||
|
var stage = addStage();
|
||||||
|
var layer = new Konva.Layer();
|
||||||
|
|
||||||
|
stage.add(layer);
|
||||||
|
var text = new Konva.Text({
|
||||||
|
text: 'ltr text',
|
||||||
|
direction: 'ltr',
|
||||||
|
});
|
||||||
|
|
||||||
|
layer.add(text);
|
||||||
|
layer.draw();
|
||||||
|
|
||||||
|
var trace =
|
||||||
|
'clearRect(0,0,578,200);clearRect(0,0,578,200);save();transform(1,0,0,1,0,0);font=normal normal 12px Arial;textBaseline=middle;textAlign=left;translate(0,0);save();fillStyle=black;fillText(ltr text,0,6);restore();restore();';
|
||||||
|
|
||||||
|
assert.equal(layer.getContext().getTrace(), trace);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it('sets rtl text direction', function () {
|
||||||
|
var stage = addStage();
|
||||||
|
var layer = new Konva.Layer();
|
||||||
|
|
||||||
|
stage.add(layer);
|
||||||
|
var text = new Konva.Text({
|
||||||
|
text: 'rtl text',
|
||||||
|
direction: 'rtl',
|
||||||
|
});
|
||||||
|
|
||||||
|
layer.add(text);
|
||||||
|
layer.draw();
|
||||||
|
|
||||||
|
var trace =
|
||||||
|
'clearRect(0,0,578,200);clearRect(0,0,578,200);save();transform(1,0,0,1,0,0);direction=rtl;font=normal normal 12px Arial;textBaseline=middle;textAlign=left;translate(0,0);save();fillStyle=black;fillText(rtl text,0,6);restore();restore();';
|
||||||
|
|
||||||
|
assert.equal(layer.getContext().getTrace(), trace);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('sets rtl text direction with letterSpacing', function () {
|
||||||
|
var stage = addStage();
|
||||||
|
var layer = new Konva.Layer();
|
||||||
|
|
||||||
|
stage.add(layer);
|
||||||
|
var text = new Konva.Text({
|
||||||
|
text: 'rtl text',
|
||||||
|
direction: 'rtl',
|
||||||
|
letterSpacing: 2,
|
||||||
|
});
|
||||||
|
|
||||||
|
layer.add(text);
|
||||||
|
layer.draw();
|
||||||
|
|
||||||
|
var trace =
|
||||||
|
'clearRect(0,0,578,200);clearRect(0,0,578,200);save();transform(1,0,0,1,0,0);direction=rtl;font=normal normal 12px Arial;textBaseline=middle;textAlign=left;translate(0,0);save();letterSpacing=2px;fillStyle=black;fillText(rtl text,0,6);restore();restore();';
|
||||||
|
|
||||||
|
assert.equal(layer.getContext().getTrace(), trace);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user