mirror of
https://github.com/konvajs/konva.git
synced 2025-06-28 06:24:42 +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',
|
||||
'shadowOffsetX',
|
||||
'shadowOffsetY',
|
||||
'letterSpacing',
|
||||
'lineCap',
|
||||
'lineDashOffset',
|
||||
'lineJoin',
|
||||
'lineWidth',
|
||||
'miterLimit',
|
||||
'direction',
|
||||
'font',
|
||||
'textAlign',
|
||||
'textBaseline',
|
||||
@ -91,6 +93,11 @@ var CONTEXT_PROPERTIES = [
|
||||
] as const;
|
||||
|
||||
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.
|
||||
* 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
|
||||
type CanvasContextProps = Pick<
|
||||
CanvasRenderingContext2D,
|
||||
ExtendedCanvasRenderingContext2D,
|
||||
(typeof CONTEXT_PROPERTIES)[number]
|
||||
>;
|
||||
|
||||
|
@ -22,6 +22,7 @@ export function stringToArray(string: string) {
|
||||
}
|
||||
|
||||
export interface TextConfig extends ShapeConfig {
|
||||
direction?: string;
|
||||
text?: string;
|
||||
fontFamily?: string;
|
||||
fontSize?: number;
|
||||
@ -41,11 +42,13 @@ export interface TextConfig extends ShapeConfig {
|
||||
var AUTO = 'auto',
|
||||
//CANVAS = 'canvas',
|
||||
CENTER = 'center',
|
||||
INHERIT = 'inherit',
|
||||
JUSTIFY = 'justify',
|
||||
CHANGE_KONVA = 'Change.konva',
|
||||
CONTEXT_2D = '2d',
|
||||
DASH = '-',
|
||||
LEFT = 'left',
|
||||
LTR = 'ltr',
|
||||
TEXT = 'text',
|
||||
TEXT_UPPER = 'Text',
|
||||
TOP = 'top',
|
||||
@ -55,11 +58,13 @@ var AUTO = 'auto',
|
||||
PX_SPACE = 'px ',
|
||||
SPACE = ' ',
|
||||
RIGHT = 'right',
|
||||
RTL = 'rtl',
|
||||
WORD = 'word',
|
||||
CHAR = 'char',
|
||||
NONE = 'none',
|
||||
ELLIPSIS = '…',
|
||||
ATTR_CHANGE_LIST = [
|
||||
'direction',
|
||||
'fontFamily',
|
||||
'fontSize',
|
||||
'fontStyle',
|
||||
@ -132,6 +137,7 @@ function checkDefaultFill(config?: TextConfig) {
|
||||
* @memberof Konva
|
||||
* @augments Konva.Shape
|
||||
* @param {Object} config
|
||||
* @param {String} [config.direction] default is inherit
|
||||
* @param {String} [config.fontFamily] default is Arial
|
||||
* @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.
|
||||
@ -185,6 +191,7 @@ export class Text extends Shape<TextConfig> {
|
||||
fontSize = this.fontSize(),
|
||||
lineHeightPx = this.lineHeight() * fontSize,
|
||||
verticalAlign = this.verticalAlign(),
|
||||
direction = this.direction(),
|
||||
alignY = 0,
|
||||
align = this.align(),
|
||||
totalWidth = this.getWidth(),
|
||||
@ -195,18 +202,25 @@ export class Text extends Shape<TextConfig> {
|
||||
shouldLineThrough = textDecoration.indexOf('line-through') !== -1,
|
||||
n;
|
||||
|
||||
direction = direction === INHERIT ? context.direction : direction;
|
||||
|
||||
var translateY = 0;
|
||||
var translateY = lineHeightPx / 2;
|
||||
|
||||
var lineTranslateX = 0;
|
||||
var lineTranslateY = 0;
|
||||
|
||||
if (direction === RTL) {
|
||||
context.setAttr('direction', direction);
|
||||
}
|
||||
|
||||
context.setAttr('font', this._getContextFont());
|
||||
|
||||
context.setAttr('textBaseline', MIDDLE);
|
||||
|
||||
context.setAttr('textAlign', LEFT);
|
||||
|
||||
|
||||
// handle vertical alignment
|
||||
if (verticalAlign === MIDDLE) {
|
||||
alignY = (this.getHeight() - textArrLen * lineHeightPx - padding * 2) / 2;
|
||||
@ -282,7 +296,10 @@ export class Text extends Shape<TextConfig> {
|
||||
context.stroke();
|
||||
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(' ');
|
||||
spacesNumber = text.split(' ').length - 1;
|
||||
var array = stringToArray(text);
|
||||
@ -303,6 +320,9 @@ export class Text extends Shape<TextConfig> {
|
||||
lineTranslateX += this.measureSize(letter).width + letterSpacing;
|
||||
}
|
||||
} else {
|
||||
if (letterSpacing !== 0) {
|
||||
context.setAttr('letterSpacing', `${letterSpacing}px`);
|
||||
}
|
||||
this._partialTextX = lineTranslateX;
|
||||
this._partialTextY = translateY + lineTranslateY;
|
||||
this._partialText = text;
|
||||
@ -615,6 +635,7 @@ export class Text extends Shape<TextConfig> {
|
||||
return super._useBufferCanvas();
|
||||
}
|
||||
|
||||
direction: GetSet<string, this>;
|
||||
fontFamily: GetSet<string, this>;
|
||||
fontSize: GetSet<number, this>;
|
||||
fontStyle: GetSet<string, this>;
|
||||
@ -682,6 +703,22 @@ Factory.overWriteSetter(Text, 'width', 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
|
||||
* @name Konva.Text#fontFamily
|
||||
|
@ -1654,4 +1654,63 @@ describe('Text', function () {
|
||||
|
||||
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