konva/src/shapes/Text.js

353 lines
11 KiB
JavaScript
Raw Normal View History

(function() {
2013-02-11 07:42:48 +08:00
// constants
var AUTO = 'auto',
CALIBRI = 'Calibri',
CANVAS = 'canvas',
CENTER = 'center',
CHANGE_KINETIC = 'Change.kinetic',
CONTEXT_2D = '2d',
DASH = '\n',
EMPTY_STRING = '',
LEFT = 'left',
NEW_LINE = '\n',
TEXT = 'text',
TEXT_UPPER = 'Text',
TOP = 'top',
MIDDLE = 'middle',
NORMAL = 'normal',
PX_SPACE = 'px ',
SPACE = ' ',
RIGHT = 'right',
ATTR_CHANGE_LIST = ['fontFamily', 'fontSize', 'fontStyle', 'padding', 'align', 'lineHeight', 'text', 'width', 'height'],
// cached variables
attrChangeListLen = ATTR_CHANGE_LIST.length;
/**
* Text constructor
* @constructor
* @augments Kinetic.Shape
* @param {Object} config
* @param {String} [config.fontFamily] default is Calibri
* @param {Number} [config.fontSize] in pixels. Default is 12
* @param {String} [config.fontStyle] can be normal, bold, or italic. Default is normal
* @param {String} config.text
* @param {String} [config.align] can be left, center, or right
* @param {Number} [config.padding]
* @param {Number} [config.width] default is auto
* @param {Number} [config.height] default is auto
* @param {Number} [config.lineHeight] default is 1
* {{ShapeParams}}
* {{NodeParams}}
*/
Kinetic.Text = function(config) {
this._initText(config);
};
function _fillFunc(context) {
context.fillText(this.partialText, 0, 0);
}
function _strokeFunc(context) {
context.strokeText(this.partialText, 0, 0);
}
Kinetic.Text.prototype = {
_initText: function(config) {
2013-02-11 07:42:48 +08:00
var that = this;
this.setDefaultAttrs({
2013-02-11 07:42:48 +08:00
fontFamily: CALIBRI,
text: EMPTY_STRING,
fontSize: 12,
2013-02-11 07:42:48 +08:00
align: LEFT,
verticalAlign: TOP,
fontStyle: NORMAL,
padding: 0,
2013-02-11 07:42:48 +08:00
width: AUTO,
height: AUTO,
lineHeight: 1
});
2013-02-11 07:42:48 +08:00
this.dummyCanvas = document.createElement(CANVAS);
// call super constructor
Kinetic.Shape.call(this, config);
this._fillFunc = _fillFunc;
this._strokeFunc = _strokeFunc;
2013-02-11 07:42:48 +08:00
this.shapeType = TEXT_UPPER;
this._setDrawFuncs();
// update text data for certain attr changes
2013-02-11 07:42:48 +08:00
for(var n = 0; n < attrChangeListLen; n++) {
this.on(ATTR_CHANGE_LIST[n] + CHANGE_KINETIC, that._setTextData);
}
2013-02-11 07:42:48 +08:00
this._setTextData();
},
drawFunc: function(canvas) {
2013-02-11 07:42:48 +08:00
var context = canvas.getContext(),
p = this.getPadding(),
fontStyle = this.getFontStyle(),
fontSize = this.getFontSize(),
fontFamily = this.getFontFamily(),
textHeight = this.getTextHeight(),
lineHeightPx = this.getLineHeight() * textHeight,
textArr = this.textArr,
textArrLen = textArr.length,
width = this.getWidth();
2013-02-11 07:42:48 +08:00
context.font = fontStyle + SPACE + fontSize + PX_SPACE + fontFamily;
context.textBaseline = MIDDLE;
context.textAlign = LEFT;
context.save();
context.translate(p, 0);
2013-02-11 07:42:48 +08:00
context.translate(0, p + textHeight / 2);
// draw text lines
2013-02-11 07:42:48 +08:00
for(var n = 0; n < textArrLen; n++) {
var text = textArr[n];
// horizontal alignment
context.save();
2013-02-11 07:42:48 +08:00
if(this.getAlign() === RIGHT) {
context.translate(width - this._getTextSize(text).width - p * 2, 0);
}
2013-02-11 07:42:48 +08:00
else if(this.getAlign() === CENTER) {
context.translate((width - this._getTextSize(text).width - p * 2) / 2, 0);
}
this.partialText = text;
canvas.fillStroke(this);
context.restore();
context.translate(0, lineHeightPx);
}
context.restore();
},
drawHitFunc: function(canvas) {
2013-02-11 07:42:48 +08:00
var context = canvas.getContext(),
width = this.getWidth(),
height = this.getHeight();
context.beginPath();
context.rect(0, 0, width, height);
context.closePath();
canvas.fillStroke(this);
},
/**
* set text
* @name setText
* @methodOf Kinetic.Text.prototype
* @param {String} text
*/
setText: function(text) {
var str = Kinetic.Type._isString(text) ? text : text.toString();
2013-02-11 07:42:48 +08:00
this.setAttr(TEXT, str);
},
/**
* get width
* @name getWidth
* @methodOf Kinetic.Text.prototype
*/
getWidth: function() {
2013-02-11 07:42:48 +08:00
return this.attrs.width === AUTO ? this.getTextWidth() + this.getPadding() * 2 : this.attrs.width;
},
/**
* get height
* @name getHeight
* @methodOf Kinetic.Text.prototype
*/
getHeight: function() {
2013-02-11 07:42:48 +08:00
return this.attrs.height === AUTO ? (this.getTextHeight() * this.textArr.length * this.attrs.lineHeight) + this.attrs.padding * 2 : this.attrs.height;
},
/**
* get text width
* @name getTextWidth
* @methodOf Kinetic.Text.prototype
*/
getTextWidth: function() {
return this.textWidth;
},
/**
* get text height
* @name getTextHeight
* @methodOf Kinetic.Text.prototype
*/
getTextHeight: function() {
return this.textHeight;
},
_getTextSize: function(text) {
2013-02-11 07:42:48 +08:00
var dummyCanvas = this.dummyCanvas,
context = dummyCanvas.getContext(CONTEXT_2D),
fontSize = this.getFontSize(),
metrics;
context.save();
2013-02-11 07:42:48 +08:00
context.font = this.getFontStyle() + SPACE + fontSize + PX_SPACE + this.getFontFamily();
metrics = context.measureText(text);
context.restore();
return {
width: metrics.width,
2013-02-11 07:42:48 +08:00
height: parseInt(fontSize, 10)
};
},
/**
* set text data. wrap logic and width and height setting occurs
* here
*/
_setTextData: function() {
2013-02-11 07:42:48 +08:00
var charArr = this.getText().split(EMPTY_STRING),
arr = [],
row = 0;
addLine = true,
lineHeightPx = 0,
padding = this.getPadding();
this.textWidth = 0;
2013-02-11 07:42:48 +08:00
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) {
2013-02-11 07:42:48 +08:00
if(charArr.indexOf(NEW_LINE) === index) {
// remove newline char
charArr.splice(index, 1);
2013-02-11 07:42:48 +08:00
line = charArr.splice(0, index).join(EMPTY_STRING);
break;
}
// if line exceeds inner box width
var lineArr = charArr.slice(0, index);
2013-02-11 07:42:48 +08:00
if(this.attrs.width !== AUTO && this._getTextSize(lineArr.join(EMPTY_STRING)).width > this.attrs.width - padding * 2) {
/*
* if a single character is too large to fit inside
* the text box width, then break out of the loop
* and stop processing
*/
if(index == 0) {
break;
}
2013-02-11 07:42:48 +08:00
var lastSpace = lineArr.lastIndexOf(SPACE);
var lastDash = lineArr.lastIndexOf(DASH);
var wrapIndex = Math.max(lastSpace, lastDash);
if(wrapIndex >= 0) {
2013-02-11 07:42:48 +08:00
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
*/
2013-02-11 07:42:48 +08:00
line = charArr.splice(0, index).join(EMPTY_STRING);
break;
}
index++;
// if the end is reached
if(index === charArr.length) {
2013-02-11 07:42:48 +08:00
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 = arr;
}
};
Kinetic.Global.extend(Kinetic.Text, Kinetic.Shape);
// add getters setters
Kinetic.Node.addGettersSetters(Kinetic.Text, ['fontFamily', 'fontSize', 'fontStyle', 'padding', 'align', 'lineHeight']);
2013-02-11 07:42:48 +08:00
Kinetic.Node.addGetters(Kinetic.Text, [TEXT]);
/**
* set font family
* @name setFontFamily
* @methodOf Kinetic.Text.prototype
* @param {String} fontFamily
*/
/**
* set font size in pixels
* @name setFontSize
* @methodOf Kinetic.Text.prototype
* @param {int} fontSize
*/
/**
* set font style. Can be 'normal', 'italic', or 'bold'. 'normal' is the default.
* @name setFontStyle
* @methodOf Kinetic.Text.prototype
* @param {String} fontStyle
*/
/**
* set padding
* @name setPadding
* @methodOf Kinetic.Text.prototype
* @param {int} padding
*/
/**
* set horizontal align of text
* @name setAlign
* @methodOf Kinetic.Text.prototype
* @param {String} align align can be 'left', 'center', or 'right'
*/
/**
* set line height
* @name setLineHeight
* @methodOf Kinetic.Text.prototype
* @param {Number} lineHeight default is 1
*/
/**
* get font family
* @name getFontFamily
* @methodOf Kinetic.Text.prototype
*/
/**
* get font size
* @name getFontSize
* @methodOf Kinetic.Text.prototype
*/
/**
* get font style
* @name getFontStyle
* @methodOf Kinetic.Text.prototype
*/
/**
* get padding
* @name getPadding
* @methodOf Kinetic.Text.prototype
*/
/**
* get horizontal align
* @name getAlign
* @methodOf Kinetic.Text.prototype
*/
/**
* get line height
* @name getLineHeight
* @methodOf Kinetic.Text.prototype
*/
/**
* get text
* @name getText
* @methodOf Kinetic.Text.prototype
*/
})();