merged and tweaked pull request 314

This commit is contained in:
Eric Rowell
2013-03-20 09:17:21 -07:00
3 changed files with 151 additions and 69 deletions

View File

@@ -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,75 +208,109 @@
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);
@@ -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

View File

@@ -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');
}
};