mirror of
https://github.com/konvajs/konva.git
synced 2025-10-20 19:01:04 +08:00
justify align for Text and TextPath
This commit is contained in:
@@ -5,6 +5,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
|
|||||||
## [Not released][Not released]
|
## [Not released][Not released]
|
||||||
|
|
||||||
## Added
|
## Added
|
||||||
|
- new align value for `Konva.Text` and `Konva.TextPath`: `justify`
|
||||||
- new property for `Konva.Text` and `Konva.TextPath`: `textDecoration`. Right now it sports only '' (no decoration) and 'underline' values.
|
- new property for `Konva.Text` and `Konva.TextPath`: `textDecoration`. Right now it sports only '' (no decoration) and 'underline' values.
|
||||||
- new property for `Konva.Text`: `letterSpacing`
|
- new property for `Konva.Text`: `letterSpacing`
|
||||||
- new event `contentContextmenu` for `Konva.Stage`
|
- new event `contentContextmenu` for `Konva.Stage`
|
||||||
|
30
konva.js
30
konva.js
@@ -3,7 +3,7 @@
|
|||||||
* Konva JavaScript Framework v1.2.2
|
* Konva JavaScript Framework v1.2.2
|
||||||
* http://konvajs.github.io/
|
* http://konvajs.github.io/
|
||||||
* Licensed under the MIT or GPL Version 2 licenses.
|
* Licensed under the MIT or GPL Version 2 licenses.
|
||||||
* Date: Wed Dec 07 2016
|
* Date: Thu Dec 08 2016
|
||||||
*
|
*
|
||||||
* Original work Copyright (C) 2011 - 2013 by Eric Rowell (KineticJS)
|
* Original work Copyright (C) 2011 - 2013 by Eric Rowell (KineticJS)
|
||||||
* Modified work Copyright (C) 2014 - 2015 by Anton Lavrenov (Konva)
|
* Modified work Copyright (C) 2014 - 2015 by Anton Lavrenov (Konva)
|
||||||
@@ -12918,6 +12918,7 @@
|
|||||||
var AUTO = 'auto',
|
var AUTO = 'auto',
|
||||||
//CANVAS = 'canvas',
|
//CANVAS = 'canvas',
|
||||||
CENTER = 'center',
|
CENTER = 'center',
|
||||||
|
JUSTIFY = 'justify',
|
||||||
CHANGE_KONVA = 'Change.konva',
|
CHANGE_KONVA = 'Change.konva',
|
||||||
CONTEXT_2D = '2d',
|
CONTEXT_2D = '2d',
|
||||||
DASH = '-',
|
DASH = '-',
|
||||||
@@ -13083,6 +13084,7 @@
|
|||||||
lineHeightPx = this.getLineHeight() * textHeight,
|
lineHeightPx = this.getLineHeight() * textHeight,
|
||||||
textArr = this.textArr,
|
textArr = this.textArr,
|
||||||
textArrLen = textArr.length,
|
textArrLen = textArr.length,
|
||||||
|
align = this.getAlign(),
|
||||||
totalWidth = this.getWidth(),
|
totalWidth = this.getWidth(),
|
||||||
letterSpacing = this.getLetterSpacing(),
|
letterSpacing = this.getLetterSpacing(),
|
||||||
textDecoration = this.textDecoration(),
|
textDecoration = this.textDecoration(),
|
||||||
@@ -13112,10 +13114,10 @@
|
|||||||
|
|
||||||
// horizontal alignment
|
// horizontal alignment
|
||||||
context.save();
|
context.save();
|
||||||
if(this.getAlign() === RIGHT) {
|
if(align === RIGHT) {
|
||||||
context.translate(totalWidth - width - p * 2, 0);
|
context.translate(totalWidth - width - p * 2, 0);
|
||||||
}
|
}
|
||||||
else if(this.getAlign() === CENTER) {
|
else if(align === CENTER) {
|
||||||
context.translate((totalWidth - width - p * 2) / 2, 0);
|
context.translate((totalWidth - width - p * 2) / 2, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -13131,9 +13133,15 @@
|
|||||||
context.stroke();
|
context.stroke();
|
||||||
context.restore();
|
context.restore();
|
||||||
}
|
}
|
||||||
if (letterSpacing !== 0) {
|
if ((letterSpacing !== 0) || align === JUSTIFY) {
|
||||||
|
// var words = text.split(' ');
|
||||||
|
var spacesNumber = text.split(' ').length - 1;
|
||||||
for(var li = 0; li < text.length; li++) {
|
for(var li = 0; li < text.length; li++) {
|
||||||
var letter = text[li];
|
var letter = text[li];
|
||||||
|
// skip justify for the last line
|
||||||
|
if ((letter === ' ') && (n !== textArrLen - 1)) {
|
||||||
|
context.translate(Math.ceil((totalWidth - width) / spacesNumber), 0);
|
||||||
|
}
|
||||||
this.partialText = letter;
|
this.partialText = letter;
|
||||||
context.fillStrokeShape(this);
|
context.fillStrokeShape(this);
|
||||||
context.translate(Math.round(this._getTextSize(letter).width) + letterSpacing, 0);
|
context.translate(Math.round(this._getTextSize(letter).width) + letterSpacing, 0);
|
||||||
@@ -13445,7 +13453,7 @@
|
|||||||
Konva.Factory.addGetterSetter(Konva.Text, 'align', LEFT);
|
Konva.Factory.addGetterSetter(Konva.Text, 'align', LEFT);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* get/set horizontal align of text. Can be 'left', 'center', or 'right'
|
* get/set horizontal align of text. Can be 'left', 'center', 'right' or 'justify'
|
||||||
* @name align
|
* @name align
|
||||||
* @method
|
* @method
|
||||||
* @memberof Konva.Text.prototype
|
* @memberof Konva.Text.prototype
|
||||||
@@ -15241,6 +15249,7 @@
|
|||||||
var that = this;
|
var that = this;
|
||||||
var size = this._getTextSize(this.attrs.text);
|
var size = this._getTextSize(this.attrs.text);
|
||||||
var letterSpacing = this.getLetterSpacing();
|
var letterSpacing = this.getLetterSpacing();
|
||||||
|
var align = this.align();
|
||||||
|
|
||||||
this.textWidth = size.width;
|
this.textWidth = size.width;
|
||||||
this.textHeight = size.height;
|
this.textHeight = size.height;
|
||||||
@@ -15258,14 +15267,15 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
var offset = 0;
|
var offset = 0;
|
||||||
if (this.align() === 'center') {
|
if (align === 'center') {
|
||||||
offset = Math.max(0, fullPathWidth / 2 - textFullWidth / 2);
|
offset = Math.max(0, fullPathWidth / 2 - textFullWidth / 2);
|
||||||
}
|
}
|
||||||
if (this.align() === 'right') {
|
if (align === 'right') {
|
||||||
offset = Math.max(0, fullPathWidth - textFullWidth);
|
offset = Math.max(0, fullPathWidth - textFullWidth);
|
||||||
}
|
}
|
||||||
|
|
||||||
var charArr = this.getText().split('');
|
var charArr = this.getText().split('');
|
||||||
|
var spacesNumber = this.getText().split(' ').length - 1;
|
||||||
|
|
||||||
var p0, p1, pathCmd;
|
var p0, p1, pathCmd;
|
||||||
|
|
||||||
@@ -15307,6 +15317,10 @@
|
|||||||
|
|
||||||
var glyphWidth = that._getTextSize(c).width + letterSpacing;
|
var glyphWidth = that._getTextSize(c).width + letterSpacing;
|
||||||
|
|
||||||
|
if ((c === ' ') && (align === 'justify')) {
|
||||||
|
glyphWidth += (fullPathWidth - textFullWidth) / spacesNumber;
|
||||||
|
}
|
||||||
|
|
||||||
var currLen = 0;
|
var currLen = 0;
|
||||||
var attempts = 0;
|
var attempts = 0;
|
||||||
|
|
||||||
@@ -15545,7 +15559,7 @@
|
|||||||
Konva.Factory.addGetterSetter(Konva.TextPath, 'align', 'left');
|
Konva.Factory.addGetterSetter(Konva.TextPath, 'align', 'left');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* get/set horizontal align of text. Can be 'left', 'center', or 'right'
|
* get/set horizontal align of text. Can be 'left', 'center', 'right' or 'justify'
|
||||||
* @name align
|
* @name align
|
||||||
* @method
|
* @method
|
||||||
* @memberof Konva.Text.prototype
|
* @memberof Konva.Text.prototype
|
||||||
|
10
konva.min.js
vendored
10
konva.min.js
vendored
File diff suppressed because one or more lines are too long
@@ -7,6 +7,7 @@
|
|||||||
var AUTO = 'auto',
|
var AUTO = 'auto',
|
||||||
//CANVAS = 'canvas',
|
//CANVAS = 'canvas',
|
||||||
CENTER = 'center',
|
CENTER = 'center',
|
||||||
|
JUSTIFY = 'justify',
|
||||||
CHANGE_KONVA = 'Change.konva',
|
CHANGE_KONVA = 'Change.konva',
|
||||||
CONTEXT_2D = '2d',
|
CONTEXT_2D = '2d',
|
||||||
DASH = '-',
|
DASH = '-',
|
||||||
@@ -103,6 +104,7 @@
|
|||||||
lineHeightPx = this.getLineHeight() * textHeight,
|
lineHeightPx = this.getLineHeight() * textHeight,
|
||||||
textArr = this.textArr,
|
textArr = this.textArr,
|
||||||
textArrLen = textArr.length,
|
textArrLen = textArr.length,
|
||||||
|
align = this.getAlign(),
|
||||||
totalWidth = this.getWidth(),
|
totalWidth = this.getWidth(),
|
||||||
letterSpacing = this.getLetterSpacing(),
|
letterSpacing = this.getLetterSpacing(),
|
||||||
textDecoration = this.textDecoration(),
|
textDecoration = this.textDecoration(),
|
||||||
@@ -132,10 +134,10 @@
|
|||||||
|
|
||||||
// horizontal alignment
|
// horizontal alignment
|
||||||
context.save();
|
context.save();
|
||||||
if(this.getAlign() === RIGHT) {
|
if(align === RIGHT) {
|
||||||
context.translate(totalWidth - width - p * 2, 0);
|
context.translate(totalWidth - width - p * 2, 0);
|
||||||
}
|
}
|
||||||
else if(this.getAlign() === CENTER) {
|
else if(align === CENTER) {
|
||||||
context.translate((totalWidth - width - p * 2) / 2, 0);
|
context.translate((totalWidth - width - p * 2) / 2, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -151,9 +153,15 @@
|
|||||||
context.stroke();
|
context.stroke();
|
||||||
context.restore();
|
context.restore();
|
||||||
}
|
}
|
||||||
if (letterSpacing !== 0) {
|
if ((letterSpacing !== 0) || align === JUSTIFY) {
|
||||||
|
// var words = text.split(' ');
|
||||||
|
var spacesNumber = text.split(' ').length - 1;
|
||||||
for(var li = 0; li < text.length; li++) {
|
for(var li = 0; li < text.length; li++) {
|
||||||
var letter = text[li];
|
var letter = text[li];
|
||||||
|
// skip justify for the last line
|
||||||
|
if ((letter === ' ') && (n !== textArrLen - 1)) {
|
||||||
|
context.translate(Math.ceil((totalWidth - width) / spacesNumber), 0);
|
||||||
|
}
|
||||||
this.partialText = letter;
|
this.partialText = letter;
|
||||||
context.fillStrokeShape(this);
|
context.fillStrokeShape(this);
|
||||||
context.translate(Math.round(this._getTextSize(letter).width) + letterSpacing, 0);
|
context.translate(Math.round(this._getTextSize(letter).width) + letterSpacing, 0);
|
||||||
@@ -465,7 +473,7 @@
|
|||||||
Konva.Factory.addGetterSetter(Konva.Text, 'align', LEFT);
|
Konva.Factory.addGetterSetter(Konva.Text, 'align', LEFT);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* get/set horizontal align of text. Can be 'left', 'center', or 'right'
|
* get/set horizontal align of text. Can be 'left', 'center', 'right' or 'justify'
|
||||||
* @name align
|
* @name align
|
||||||
* @method
|
* @method
|
||||||
* @memberof Konva.Text.prototype
|
* @memberof Konva.Text.prototype
|
||||||
|
@@ -186,6 +186,7 @@
|
|||||||
var that = this;
|
var that = this;
|
||||||
var size = this._getTextSize(this.attrs.text);
|
var size = this._getTextSize(this.attrs.text);
|
||||||
var letterSpacing = this.getLetterSpacing();
|
var letterSpacing = this.getLetterSpacing();
|
||||||
|
var align = this.align();
|
||||||
|
|
||||||
this.textWidth = size.width;
|
this.textWidth = size.width;
|
||||||
this.textHeight = size.height;
|
this.textHeight = size.height;
|
||||||
@@ -203,14 +204,15 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
var offset = 0;
|
var offset = 0;
|
||||||
if (this.align() === 'center') {
|
if (align === 'center') {
|
||||||
offset = Math.max(0, fullPathWidth / 2 - textFullWidth / 2);
|
offset = Math.max(0, fullPathWidth / 2 - textFullWidth / 2);
|
||||||
}
|
}
|
||||||
if (this.align() === 'right') {
|
if (align === 'right') {
|
||||||
offset = Math.max(0, fullPathWidth - textFullWidth);
|
offset = Math.max(0, fullPathWidth - textFullWidth);
|
||||||
}
|
}
|
||||||
|
|
||||||
var charArr = this.getText().split('');
|
var charArr = this.getText().split('');
|
||||||
|
var spacesNumber = this.getText().split(' ').length - 1;
|
||||||
|
|
||||||
var p0, p1, pathCmd;
|
var p0, p1, pathCmd;
|
||||||
|
|
||||||
@@ -252,6 +254,10 @@
|
|||||||
|
|
||||||
var glyphWidth = that._getTextSize(c).width + letterSpacing;
|
var glyphWidth = that._getTextSize(c).width + letterSpacing;
|
||||||
|
|
||||||
|
if ((c === ' ') && (align === 'justify')) {
|
||||||
|
glyphWidth += (fullPathWidth - textFullWidth) / spacesNumber;
|
||||||
|
}
|
||||||
|
|
||||||
var currLen = 0;
|
var currLen = 0;
|
||||||
var attempts = 0;
|
var attempts = 0;
|
||||||
|
|
||||||
@@ -490,7 +496,7 @@
|
|||||||
Konva.Factory.addGetterSetter(Konva.TextPath, 'align', 'left');
|
Konva.Factory.addGetterSetter(Konva.TextPath, 'align', 'left');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* get/set horizontal align of text. Can be 'left', 'center', or 'right'
|
* get/set horizontal align of text. Can be 'left', 'center', 'right' or 'justify'
|
||||||
* @name align
|
* @name align
|
||||||
* @method
|
* @method
|
||||||
* @memberof Konva.Text.prototype
|
* @memberof Konva.Text.prototype
|
||||||
|
@@ -295,6 +295,43 @@ suite('Text', function(){
|
|||||||
assert.equal(text.getLineHeight(), 20);
|
assert.equal(text.getLineHeight(), 20);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// ======================================================
|
||||||
|
test('text multi line with justify align', function() {
|
||||||
|
var stage = addStage();
|
||||||
|
var layer = new Konva.Layer();
|
||||||
|
|
||||||
|
var rect = new Konva.Rect({
|
||||||
|
x: 10,
|
||||||
|
y: 10,
|
||||||
|
width: 380,
|
||||||
|
height: 300,
|
||||||
|
fill: 'yellow'
|
||||||
|
});
|
||||||
|
|
||||||
|
var text = new Konva.Text({
|
||||||
|
x: 10,
|
||||||
|
y: 10,
|
||||||
|
text: 'HEADING\n\n All the world\'s a stage, merely players. They have their exits and their entrances; And one man in his time plays many parts.',
|
||||||
|
fontSize: 14,
|
||||||
|
fontFamily: 'Calibri',
|
||||||
|
fontStyle: 'normal',
|
||||||
|
fill: '#555',
|
||||||
|
width: 380,
|
||||||
|
align: 'justify',
|
||||||
|
letterSpacing: 5,
|
||||||
|
draggable: true
|
||||||
|
});
|
||||||
|
|
||||||
|
rect.height(text.getHeight());
|
||||||
|
layer.add(rect).add(text);
|
||||||
|
stage.add(layer);
|
||||||
|
|
||||||
|
var trace = 'fillStyle=#555;fillText( ,0,0);translate(9,0);fillStyle=#555;fillText(i,0,0);translate(9,0);fillStyle=#555;fillText(n,0,0);translate(12,0);translate(1,0);fillStyle=#555;fillText( ,0,0);translate(9,0);fillStyle=#555;fillText(h,0,0);translate(12,0);fillStyle=#555;fillText(i,0,0);translate(9,0);fillStyle=#555;fillText(s,0,0);translate(10,0);translate(1,0);fillStyle=#555;fillText( ,0,0);translate(9,0);restore();translate(0,14);save();fillStyle=#555;fillText(t,0,0);translate(9,0);fillStyle=#555;fillText(i,0,0);translate(9,0);fillStyle=#555;fillText(m,0,0);translate(16,0);fillStyle=#555;fillText(e,0,0);translate(11,0);fillStyle=#555;fillText( ,0,0);translate(9,0);fillStyle=#555;fillText(p,0,0);translate(12,0);fillStyle=#555;fillText(l,0,0);translate(9,0);fillStyle=#555;fillText(a,0,0);translate(11,0);fillStyle=#555;fillText(y,0,0);translate(12,0);fillStyle=#555;fillText(s,0,0);translate(10,0);fillStyle=#555;fillText( ,0,0);translate(9,0);fillStyle=#555;fillText(m,0,0);translate(16,0);fillStyle=#555;fillText(a,0,0);translate(11,0);fillStyle=#555;fillText(n,0,0);translate(12,0);fillStyle=#555;fillText(y,0,0);translate(12,0);fillStyle=#555;fillText( ,0,0);translate(9,0);fillStyle=#555;fillText(p,0,0);translate(12,0);fillStyle=#555;fillText(a,0,0);translate(11,0);fillStyle=#555;fillText(r,0,0);translate(10,0);fillStyle=#555;fillText(t,0,0);translate(9,0);fillStyle=#555;fillText(s,0,0);translate(10,0);fillStyle=#555;fillText(.,0,0);translate(9,0);restore();translate(0,14);restore();restore();';
|
||||||
|
|
||||||
|
assert.equal(layer.getContext().getTrace(), trace);
|
||||||
|
});
|
||||||
|
|
||||||
// ======================================================
|
// ======================================================
|
||||||
test('text multi line with shadows', function() {
|
test('text multi line with shadows', function() {
|
||||||
var stage = addStage();
|
var stage = addStage();
|
||||||
|
@@ -267,6 +267,36 @@ suite('TextPath', function() {
|
|||||||
stage.add(layer);
|
stage.add(layer);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('Text path with justify align', function() {
|
||||||
|
var stage = addStage();
|
||||||
|
var layer = new Konva.Layer();
|
||||||
|
|
||||||
|
var c = "M10,10 C0,0 10,150 100,100 S300,150 400,50";
|
||||||
|
|
||||||
|
var textpath = new Konva.TextPath({
|
||||||
|
stroke: 'black',
|
||||||
|
strokeWidth: 1,
|
||||||
|
fill: 'orange',
|
||||||
|
fontSize: 10,
|
||||||
|
fontFamily: 'Arial',
|
||||||
|
letterSpacing: 5,
|
||||||
|
text: 'All the worlds a stage.',
|
||||||
|
align: 'justify',
|
||||||
|
data: c
|
||||||
|
});
|
||||||
|
|
||||||
|
// TODO: add test case
|
||||||
|
|
||||||
|
layer.add(textpath);
|
||||||
|
stage.add(layer);
|
||||||
|
|
||||||
|
|
||||||
|
// console.log(layer.getContext().getTrace(true));
|
||||||
|
var trace = 'rotate();fillStyle;fillText();lineWidth;strokeStyle;strokeText();restore();save();translate();rotate();fillStyle;fillText();lineWidth;strokeStyle;strokeText();restore();save();translate();rotate();fillStyle;fillText();lineWidth;strokeStyle;strokeText();restore();save();translate();rotate();fillStyle;fillText();lineWidth;strokeStyle;strokeText();restore();save();translate();rotate();fillStyle;fillText();lineWidth;strokeStyle;strokeText();restore();save();translate();rotate();fillStyle;fillText();lineWidth;strokeStyle;strokeText();restore();save();translate();rotate();fillStyle;fillText();lineWidth;strokeStyle;strokeText();restore();save();translate();rotate();fillStyle;fillText();lineWidth;strokeStyle;strokeText();restore();save();translate();rotate();fillStyle;fillText();lineWidth;strokeStyle;strokeText();restore();save();translate();rotate();fillStyle;fillText();lineWidth;strokeStyle;strokeText();restore();save();translate();rotate();fillStyle;fillText();lineWidth;strokeStyle;strokeText();restore();restore();restore();';
|
||||||
|
|
||||||
|
assert.equal(layer.getContext().getTrace(true), trace);
|
||||||
|
});
|
||||||
|
|
||||||
test('Text path with underline', function() {
|
test('Text path with underline', function() {
|
||||||
var stage = addStage();
|
var stage = addStage();
|
||||||
var layer = new Konva.Layer();
|
var layer = new Konva.Layer();
|
||||||
|
Reference in New Issue
Block a user