mirror of
https://github.com/konvajs/konva.git
synced 2025-09-18 09:50:05 +08:00
merged and tweaked pull request 314
This commit is contained in:
@@ -18,11 +18,14 @@
|
||||
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,
|
||||
dummyCanvas = document.createElement(CANVAS);
|
||||
dummyContext = document.createElement(CANVAS).getContext(CONTEXT_2D);
|
||||
|
||||
/**
|
||||
* Text constructor
|
||||
@@ -38,6 +41,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}}
|
||||
*/
|
||||
@@ -171,7 +175,7 @@
|
||||
return this.textHeight;
|
||||
},
|
||||
_getTextSize: function(text) {
|
||||
var context = dummyCanvas.getContext(CONTEXT_2D),
|
||||
var context = dummyContext,
|
||||
fontSize = this.getFontSize(),
|
||||
metrics;
|
||||
|
||||
@@ -204,76 +208,110 @@
|
||||
|
||||
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();
|
||||
|
||||
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;
|
||||
_setTextData: function () {
|
||||
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;
|
||||
|
||||
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 width is fixed and line does not fit entirely
|
||||
* break the line into multiple fitting lines
|
||||
*/
|
||||
while (line.length > 0) {
|
||||
/*
|
||||
* if a single character is too large to fit inside
|
||||
* the text box width, then break out of the loop
|
||||
* and stop processing
|
||||
* use binary search to find the longest substring that
|
||||
* that would fit in the specified width
|
||||
*/
|
||||
if(index == 0) {
|
||||
break;
|
||||
}
|
||||
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
|
||||
*/
|
||||
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);
|
||||
}
|
||||
}
|
||||
this.textWidth = Math.max(this.textWidth, this._getTextSize(line).width);
|
||||
if(line !== undefined) {
|
||||
arr.push(line);
|
||||
addLine = true;
|
||||
}
|
||||
row++;
|
||||
}
|
||||
this.textArr = this._expandTextData(arr);
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* '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
|
||||
*/
|
||||
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);
|
||||
|
||||
@@ -284,11 +322,13 @@
|
||||
Kinetic.Node.addGetterSetter(Kinetic.Text, 'padding', 0);
|
||||
Kinetic.Node.addGetterSetter(Kinetic.Text, 'align', LEFT);
|
||||
Kinetic.Node.addGetterSetter(Kinetic.Text, 'lineHeight', 1);
|
||||
Kinetic.Node.addGetterSetter(Kinetic.Text, 'wrapping', NONE);
|
||||
|
||||
Kinetic.Node.addGetter(Kinetic.Text, TEXT, EMPTY_STRING);
|
||||
|
||||
Kinetic.Node.addSetter(Kinetic.Text, 'width');
|
||||
Kinetic.Node.addSetter(Kinetic.Text, 'height');
|
||||
|
||||
/**
|
||||
* set font family
|
||||
* @name setFontFamily
|
||||
|
File diff suppressed because one or more lines are too long
@@ -155,7 +155,7 @@ Test.Modules.Text = {
|
||||
layer.drawHit();
|
||||
|
||||
},
|
||||
'*text multi line': function(containerId) {
|
||||
'text multi line': function(containerId) {
|
||||
var stage = new Kinetic.Stage({
|
||||
container: containerId,
|
||||
width: 578,
|
||||
@@ -185,7 +185,8 @@ Test.Modules.Text = {
|
||||
//width: 200,
|
||||
padding: 0,
|
||||
align: 'center',
|
||||
draggable: true
|
||||
draggable: true,
|
||||
wrapping: 'WORD'
|
||||
});
|
||||
|
||||
// center text box
|
||||
@@ -208,6 +209,7 @@ Test.Modules.Text = {
|
||||
});
|
||||
*/
|
||||
|
||||
|
||||
},
|
||||
'text multi line with shadows': function(containerId) {
|
||||
var stage = new Kinetic.Stage({
|
||||
@@ -374,5 +376,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