mirror of
https://github.com/konvajs/konva.git
synced 2025-09-19 10:47: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 ',
|
PX_SPACE = 'px ',
|
||||||
SPACE = ' ',
|
SPACE = ' ',
|
||||||
RIGHT = 'right',
|
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
|
// cached variables
|
||||||
attrChangeListLen = ATTR_CHANGE_LIST.length,
|
attrChangeListLen = ATTR_CHANGE_LIST.length,
|
||||||
@@ -40,6 +43,7 @@
|
|||||||
* @param {Number} [config.width] default is auto
|
* @param {Number} [config.width] default is auto
|
||||||
* @param {Number} [config.height] default is auto
|
* @param {Number} [config.height] default is auto
|
||||||
* @param {Number} [config.lineHeight] default is 1
|
* @param {Number} [config.lineHeight] default is 1
|
||||||
|
* @param {String} [config.wrapping] can be word, char, or none. Default is word
|
||||||
* {{ShapeParams}}
|
* {{ShapeParams}}
|
||||||
* {{NodeParams}}
|
* {{NodeParams}}
|
||||||
*/
|
*/
|
||||||
@@ -66,7 +70,8 @@
|
|||||||
padding: 0,
|
padding: 0,
|
||||||
width: AUTO,
|
width: AUTO,
|
||||||
height: AUTO,
|
height: AUTO,
|
||||||
lineHeight: 1
|
lineHeight: 1,
|
||||||
|
wrapping: WORD
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!dummyContext) {
|
if (!dummyContext) {
|
||||||
@@ -211,81 +216,115 @@
|
|||||||
|
|
||||||
return newArr;
|
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
|
* set text data. wrap logic and width and height setting occurs
|
||||||
* here
|
* here
|
||||||
*/
|
*/
|
||||||
_setTextData: function() {
|
_setTextData: function () {
|
||||||
var charArr = this.getText().split(EMPTY_STRING),
|
var lines = this.getText().split('\n'),
|
||||||
arr = [],
|
fontSize = +this.getFontSize(),
|
||||||
row = 0;
|
textWidth = 0,
|
||||||
addLine = true,
|
lineHeightPx = this.getLineHeight() * fontSize,
|
||||||
lineHeightPx = 0,
|
width = this.attrs.width,
|
||||||
padding = this.getPadding();
|
height = this.attrs.height,
|
||||||
|
fixedWidth = width !== AUTO,
|
||||||
this.textWidth = 0;
|
fixedHeight = height !== AUTO,
|
||||||
this.textHeight = this._getTextSize(this.getText()).height;
|
maxHeightPx = height - this.getPadding() * 2,
|
||||||
lineHeightPx = this.getLineHeight() * this.textHeight;
|
currentHeightPx = 0,
|
||||||
|
wrapping = this.getWrapping(),
|
||||||
while(charArr.length > 0 && addLine && (this.attrs.height === AUTO || lineHeightPx * (row + 1) < this.attrs.height - padding * 2)) {
|
shouldWrap = wrapping !== NONE,
|
||||||
var index = 0;
|
wrapAtWord = wrapping !== CHAR && shouldWrap;
|
||||||
var line = undefined;
|
|
||||||
addLine = false;
|
|
||||||
|
|
||||||
while(index < charArr.length) {
|
this.textArr = [];
|
||||||
if(charArr.indexOf(NEW_LINE) === index) {
|
dummyContext.save();
|
||||||
// remove newline char
|
dummyContext.font = this.getFontStyle() + SPACE + fontSize + PX_SPACE + this.getFontFamily();
|
||||||
charArr.splice(index, 1);
|
for (var i = 0, max = lines.length; i < max; ++i) {
|
||||||
line = charArr.splice(0, index).join(EMPTY_STRING);
|
var line = lines[i],
|
||||||
break;
|
lineWidth = this._getTextWidth(line);
|
||||||
}
|
if (fixedWidth && lineWidth > width) {
|
||||||
|
/*
|
||||||
// if line exceeds inner box width
|
* if width is fixed and line does not fit entirely
|
||||||
var lineArr = charArr.slice(0, index);
|
* break the line into multiple fitting lines
|
||||||
if(this.attrs.width !== AUTO && this._getTextSize(lineArr.join(EMPTY_STRING)).width > this.attrs.width - padding * 2) {
|
*/
|
||||||
|
while (line.length > 0) {
|
||||||
/*
|
/*
|
||||||
* if a single character is too large to fit inside
|
* use binary search to find the longest substring that
|
||||||
* the text box width, then break out of the loop
|
* that would fit in the specified width
|
||||||
* and stop processing
|
|
||||||
*/
|
*/
|
||||||
if(index == 0) {
|
var low = 0, high = line.length,
|
||||||
break;
|
match = '', matchWidth = 0;
|
||||||
}
|
while (low < high) {
|
||||||
var lastSpace = lineArr.lastIndexOf(SPACE);
|
var mid = (low + high) >>> 1,
|
||||||
var lastDash = lineArr.lastIndexOf(DASH);
|
substr = line.slice(0, mid + 1),
|
||||||
var wrapIndex = Math.max(lastSpace, lastDash);
|
substrWidth = this._getTextWidth(substr);
|
||||||
if(wrapIndex >= 0) {
|
if (substrWidth <= width) {
|
||||||
line = charArr.splice(0, 1 + wrapIndex).join(EMPTY_STRING);
|
low = mid + 1;
|
||||||
break;
|
match = substr;
|
||||||
}
|
matchWidth = substrWidth;
|
||||||
/*
|
} else {
|
||||||
* if not able to word wrap based on space or dash,
|
high = mid;
|
||||||
* go ahead and wrap in the middle of a word if needed
|
}
|
||||||
*/
|
}
|
||||||
line = charArr.splice(0, index).join(EMPTY_STRING);
|
/*
|
||||||
break;
|
* 'low' is now the index of the substring end
|
||||||
}
|
* 'match' is the substring
|
||||||
index++;
|
* 'matchWidth' is the substring width in px
|
||||||
|
*/
|
||||||
// if the end is reached
|
if (match) {
|
||||||
if(index === charArr.length) {
|
// a fitting substring was found
|
||||||
line = charArr.splice(0, index).join(EMPTY_STRING);
|
if (wrapAtWord) {
|
||||||
}
|
// try to find a space or dash where wrapping could be done
|
||||||
}
|
var wrapIndex = Math.max(match.lastIndexOf(SPACE),
|
||||||
this.textWidth = Math.max(this.textWidth, this._getTextSize(line).width);
|
match.lastIndexOf(DASH)) + 1;
|
||||||
if(line !== undefined) {
|
if (wrapIndex > 0) {
|
||||||
arr.push(line);
|
// re-cut the substring found at the space/dash position
|
||||||
addLine = true;
|
low = wrapIndex;
|
||||||
}
|
match = match.slice(0, low);
|
||||||
row++;
|
matchWidth = this._getTextWidth(match);
|
||||||
}
|
}
|
||||||
this.textArr = this._expandTextData(arr);
|
}
|
||||||
}
|
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
|
||||||
|
*/
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
line = line.slice(low);
|
||||||
|
} else {
|
||||||
|
// not even one character could fit in the element, abort
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// element width is automatically adjusted to max line width
|
||||||
|
this._addTextLine(line, lineWidth);
|
||||||
|
currentHeightPx += lineHeightPx;
|
||||||
|
textWidth = Math.max(textWidth, lineWidth);
|
||||||
|
}
|
||||||
|
// if element height is fixed, abort if adding one more line would overflow
|
||||||
|
if (fixedHeight && currentHeightPx + lineHeightPx > maxHeightPx) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dummyContext.restore();
|
||||||
|
this.textHeight = fontSize;
|
||||||
|
this.textWidth = textWidth;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
Kinetic.Global.extend(Kinetic.Text, Kinetic.Shape);
|
Kinetic.Global.extend(Kinetic.Text, Kinetic.Shape);
|
||||||
|
|
||||||
// add getters setters
|
// 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]);
|
Kinetic.Node.addGetters(Kinetic.Text, [TEXT]);
|
||||||
/**
|
/**
|
||||||
* set font family
|
* 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());
|
//console.log(layer.toDataURL());
|
||||||
warn(layer.toDataURL() === dataUrls['text stroke disabled'], 'should be text with blue fill and no stroke');
|
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