mirror of
https://github.com/konvajs/konva.git
synced 2025-09-19 02:37:59 +08:00
refactored the whole _setTextData method of Kinetic.Text, added a 'wrapping' option.
Performances should improve. Also, the text should not overflow anymore from the defined width like it sometimes did.
This commit is contained in:
@@ -18,7 +18,10 @@
|
||||
PX_SPACE = 'px ',
|
||||
SPACE = ' ',
|
||||
RIGHT = 'right',
|
||||
ATTR_CHANGE_LIST = ['fontFamily', 'fontSize', 'fontStyle', 'padding', 'align', 'lineHeight', 'text', 'width', 'height'],
|
||||
WORD = 'word',
|
||||
CHAR = 'char',
|
||||
NONE = 'none',
|
||||
ATTR_CHANGE_LIST = ['fontFamily', 'fontSize', 'fontStyle', 'padding', 'align', 'lineHeight', 'text', 'width', 'height', 'wrapping'],
|
||||
|
||||
// cached variables
|
||||
attrChangeListLen = ATTR_CHANGE_LIST.length,
|
||||
@@ -40,6 +43,7 @@
|
||||
* @param {Number} [config.width] default is auto
|
||||
* @param {Number} [config.height] default is auto
|
||||
* @param {Number} [config.lineHeight] default is 1
|
||||
* @param {String} [config.wrapping] can be word, char, or none. Default is word
|
||||
* {{ShapeParams}}
|
||||
* {{NodeParams}}
|
||||
*/
|
||||
@@ -66,7 +70,8 @@
|
||||
padding: 0,
|
||||
width: AUTO,
|
||||
height: AUTO,
|
||||
lineHeight: 1
|
||||
lineHeight: 1,
|
||||
wrapping: WORD
|
||||
});
|
||||
|
||||
if (!dummyContext) {
|
||||
@@ -211,81 +216,115 @@
|
||||
|
||||
return newArr;
|
||||
},
|
||||
_addTextLine: function (line, width, height) {
|
||||
return this.textArr.push({text: line, width: width});
|
||||
},
|
||||
_getTextWidth: function (text) {
|
||||
return dummyContext.measureText(text).width;
|
||||
},
|
||||
/**
|
||||
* set text data. wrap logic and width and height setting occurs
|
||||
* here
|
||||
*/
|
||||
_setTextData: function () {
|
||||
var charArr = this.getText().split(EMPTY_STRING),
|
||||
arr = [],
|
||||
row = 0;
|
||||
addLine = true,
|
||||
lineHeightPx = 0,
|
||||
padding = this.getPadding();
|
||||
var lines = this.getText().split('\n'),
|
||||
fontSize = +this.getFontSize(),
|
||||
textWidth = 0,
|
||||
lineHeightPx = this.getLineHeight() * fontSize,
|
||||
width = this.attrs.width,
|
||||
height = this.attrs.height,
|
||||
fixedWidth = width !== AUTO,
|
||||
fixedHeight = height !== AUTO,
|
||||
maxHeightPx = height - this.getPadding() * 2,
|
||||
currentHeightPx = 0,
|
||||
wrapping = this.getWrapping(),
|
||||
shouldWrap = wrapping !== NONE,
|
||||
wrapAtWord = wrapping !== CHAR && shouldWrap;
|
||||
|
||||
this.textWidth = 0;
|
||||
this.textHeight = this._getTextSize(this.getText()).height;
|
||||
lineHeightPx = this.getLineHeight() * this.textHeight;
|
||||
|
||||
while(charArr.length > 0 && addLine && (this.attrs.height === AUTO || lineHeightPx * (row + 1) < this.attrs.height - padding * 2)) {
|
||||
var index = 0;
|
||||
var line = undefined;
|
||||
addLine = false;
|
||||
|
||||
while(index < charArr.length) {
|
||||
if(charArr.indexOf(NEW_LINE) === index) {
|
||||
// remove newline char
|
||||
charArr.splice(index, 1);
|
||||
line = charArr.splice(0, index).join(EMPTY_STRING);
|
||||
break;
|
||||
}
|
||||
|
||||
// if line exceeds inner box width
|
||||
var lineArr = charArr.slice(0, index);
|
||||
if(this.attrs.width !== AUTO && this._getTextSize(lineArr.join(EMPTY_STRING)).width > this.attrs.width - padding * 2) {
|
||||
this.textArr = [];
|
||||
dummyContext.save();
|
||||
dummyContext.font = this.getFontStyle() + SPACE + fontSize + PX_SPACE + this.getFontFamily();
|
||||
for (var i = 0, max = lines.length; i < max; ++i) {
|
||||
var line = lines[i],
|
||||
lineWidth = this._getTextWidth(line);
|
||||
if (fixedWidth && lineWidth > width) {
|
||||
/*
|
||||
* if a single character is too large to fit inside
|
||||
* the text box width, then break out of the loop
|
||||
* and stop processing
|
||||
* if width is fixed and line does not fit entirely
|
||||
* break the line into multiple fitting lines
|
||||
*/
|
||||
if(index == 0) {
|
||||
break;
|
||||
while (line.length > 0) {
|
||||
/*
|
||||
* use binary search to find the longest substring that
|
||||
* that would fit in the specified width
|
||||
*/
|
||||
var low = 0, high = line.length,
|
||||
match = '', matchWidth = 0;
|
||||
while (low < high) {
|
||||
var mid = (low + high) >>> 1,
|
||||
substr = line.slice(0, mid + 1),
|
||||
substrWidth = this._getTextWidth(substr);
|
||||
if (substrWidth <= width) {
|
||||
low = mid + 1;
|
||||
match = substr;
|
||||
matchWidth = substrWidth;
|
||||
} else {
|
||||
high = mid;
|
||||
}
|
||||
var lastSpace = lineArr.lastIndexOf(SPACE);
|
||||
var lastDash = lineArr.lastIndexOf(DASH);
|
||||
var wrapIndex = Math.max(lastSpace, lastDash);
|
||||
if(wrapIndex >= 0) {
|
||||
line = charArr.splice(0, 1 + wrapIndex).join(EMPTY_STRING);
|
||||
break;
|
||||
}
|
||||
/*
|
||||
* if not able to word wrap based on space or dash,
|
||||
* go ahead and wrap in the middle of a word if needed
|
||||
* 'low' is now the index of the substring end
|
||||
* 'match' is the substring
|
||||
* 'matchWidth' is the substring width in px
|
||||
*/
|
||||
if (match) {
|
||||
// a fitting substring was found
|
||||
if (wrapAtWord) {
|
||||
// try to find a space or dash where wrapping could be done
|
||||
var wrapIndex = Math.max(match.lastIndexOf(SPACE),
|
||||
match.lastIndexOf(DASH)) + 1;
|
||||
if (wrapIndex > 0) {
|
||||
// re-cut the substring found at the space/dash position
|
||||
low = wrapIndex;
|
||||
match = match.slice(0, low);
|
||||
matchWidth = this._getTextWidth(match);
|
||||
}
|
||||
}
|
||||
this._addTextLine(match, matchWidth);
|
||||
currentHeightPx += lineHeightPx;
|
||||
if (!shouldWrap ||
|
||||
(fixedHeight && currentHeightPx + lineHeightPx > maxHeightPx)) {
|
||||
/*
|
||||
* stop wrapping if wrapping is disabled or if adding
|
||||
* one more line would overflow the fixed height
|
||||
*/
|
||||
line = charArr.splice(0, index).join(EMPTY_STRING);
|
||||
break;
|
||||
}
|
||||
index++;
|
||||
|
||||
// if the end is reached
|
||||
if(index === charArr.length) {
|
||||
line = charArr.splice(0, index).join(EMPTY_STRING);
|
||||
line = line.slice(low);
|
||||
} else {
|
||||
// not even one character could fit in the element, abort
|
||||
break;
|
||||
}
|
||||
}
|
||||
this.textWidth = Math.max(this.textWidth, this._getTextSize(line).width);
|
||||
if(line !== undefined) {
|
||||
arr.push(line);
|
||||
addLine = true;
|
||||
} else {
|
||||
// element width is automatically adjusted to max line width
|
||||
this._addTextLine(line, lineWidth);
|
||||
currentHeightPx += lineHeightPx;
|
||||
textWidth = Math.max(textWidth, lineWidth);
|
||||
}
|
||||
row++;
|
||||
// if element height is fixed, abort if adding one more line would overflow
|
||||
if (fixedHeight && currentHeightPx + lineHeightPx > maxHeightPx) {
|
||||
break;
|
||||
}
|
||||
this.textArr = this._expandTextData(arr);
|
||||
}
|
||||
dummyContext.restore();
|
||||
this.textHeight = fontSize;
|
||||
this.textWidth = textWidth;
|
||||
}
|
||||
};
|
||||
Kinetic.Global.extend(Kinetic.Text, Kinetic.Shape);
|
||||
|
||||
// add getters setters
|
||||
Kinetic.Node.addGettersSetters(Kinetic.Text, ['fontFamily', 'fontSize', 'fontStyle', 'padding', 'align', 'lineHeight']);
|
||||
Kinetic.Node.addGettersSetters(Kinetic.Text, ['fontFamily', 'fontSize', 'fontStyle', 'padding', 'align', 'lineHeight', 'wrapping']);
|
||||
Kinetic.Node.addGetters(Kinetic.Text, [TEXT]);
|
||||
/**
|
||||
* set font family
|
||||
|
File diff suppressed because one or more lines are too long
@@ -356,5 +356,40 @@ Test.Modules.Text = {
|
||||
|
||||
//console.log(layer.toDataURL());
|
||||
warn(layer.toDataURL() === dataUrls['text stroke disabled'], 'should be text with blue fill and no stroke');
|
||||
},
|
||||
'wrapped text': function (containerId) {
|
||||
var stage = new Kinetic.Stage({
|
||||
container: containerId,
|
||||
width: 578,
|
||||
height: 200
|
||||
});
|
||||
var txt = 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.',
|
||||
arr = [txt, txt];
|
||||
|
||||
var layer = new Kinetic.Layer();
|
||||
var text = new Kinetic.Text({
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: 578,
|
||||
text: arr.join(''),
|
||||
fontSize: 15,
|
||||
fontFamily: 'Calibri',
|
||||
fill: '#000',
|
||||
wrapping: 'word'
|
||||
});
|
||||
|
||||
layer.add(text);
|
||||
stage.add(layer);
|
||||
|
||||
warn(layer.toDataURL() === dataUrls['wrapped text']['wrapping to words'], 'text should be wrapped to words');
|
||||
|
||||
text.setWrapping('none');
|
||||
layer.draw();
|
||||
warn(layer.toDataURL() === dataUrls['wrapped text']['no wrapping'], 'text should not be wrapped');
|
||||
|
||||
text.setWrapping('char');
|
||||
layer.draw();
|
||||
warn(layer.toDataURL() === dataUrls['wrapped text']['wrapping to chars'], 'text should be wrapped to chars');
|
||||
|
||||
}
|
||||
};
|
||||
|
Reference in New Issue
Block a user