mirror of
https://github.com/konvajs/konva.git
synced 2025-06-28 15:23:44 +08:00
completely rewrote the Text shape. Kinetic.Text is now a Group that's composed of a Rect and a custom shape that draws text. align and vertical align now apply to the text alignment inside the text box. box rounded corners are now possible since Text uses Rect. Shadow logic improved. Beefed up the text getter and setter unit tests
This commit is contained in:
parent
3b6dffe6e1
commit
07ef653441
211
dist/kinetic-core.js
vendored
211
dist/kinetic-core.js
vendored
@ -3,7 +3,7 @@
|
|||||||
* http://www.kineticjs.com/
|
* http://www.kineticjs.com/
|
||||||
* Copyright 2012, Eric Rowell
|
* Copyright 2012, Eric Rowell
|
||||||
* Licensed under the MIT or GPL Version 2 licenses.
|
* Licensed under the MIT or GPL Version 2 licenses.
|
||||||
* Date: Jun 24 2012
|
* Date: Jun 27 2012
|
||||||
*
|
*
|
||||||
* Copyright (C) 2011 - 2012 by Eric Rowell
|
* Copyright (C) 2011 - 2012 by Eric Rowell
|
||||||
*
|
*
|
||||||
@ -483,7 +483,6 @@ Kinetic.Node.prototype = {
|
|||||||
*/
|
*/
|
||||||
for(var n = 0; n < types.length; n++) {
|
for(var n = 0; n < types.length; n++) {
|
||||||
var type = types[n];
|
var type = types[n];
|
||||||
//var event = (type.indexOf('touch') === -1) ? 'on' + type : type;
|
|
||||||
var event = type;
|
var event = type;
|
||||||
var parts = event.split('.');
|
var parts = event.split('.');
|
||||||
var baseEvent = parts[0];
|
var baseEvent = parts[0];
|
||||||
@ -570,8 +569,11 @@ Kinetic.Node.prototype = {
|
|||||||
/**
|
/**
|
||||||
* set attrs
|
* set attrs
|
||||||
* @param {Object} config
|
* @param {Object} config
|
||||||
|
* @param {Boolean} skipPublish set the second argument to true
|
||||||
|
* to skip publication of the attr change event in case you want
|
||||||
|
* to silently update an attribute
|
||||||
*/
|
*/
|
||||||
setAttrs: function(config) {
|
setAttrs: function(config, skipPublish) {
|
||||||
var go = Kinetic.GlobalObject;
|
var go = Kinetic.GlobalObject;
|
||||||
var that = this;
|
var that = this;
|
||||||
|
|
||||||
@ -582,7 +584,7 @@ Kinetic.Node.prototype = {
|
|||||||
var val = c[key];
|
var val = c[key];
|
||||||
|
|
||||||
// if obj doesn't have the val property, then create it
|
// if obj doesn't have the val property, then create it
|
||||||
if(obj[key] === undefined) {
|
if(obj[key] === undefined && val !== undefined) {
|
||||||
obj[key] = {};
|
obj[key] = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -650,7 +652,7 @@ Kinetic.Node.prototype = {
|
|||||||
* only fire change event for root
|
* only fire change event for root
|
||||||
* level attrs
|
* level attrs
|
||||||
*/
|
*/
|
||||||
if(level === 0) {
|
if(level === 0 && !skipPublish) {
|
||||||
that._fireChangeEvent(key);
|
that._fireChangeEvent(key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1092,6 +1094,9 @@ Kinetic.Node.prototype = {
|
|||||||
},
|
},
|
||||||
_setAttr: function(obj, attr, val) {
|
_setAttr: function(obj, attr, val) {
|
||||||
if(val !== undefined) {
|
if(val !== undefined) {
|
||||||
|
if(obj === undefined) {
|
||||||
|
obj = {};
|
||||||
|
}
|
||||||
obj[attr] = val;
|
obj[attr] = val;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -2965,10 +2970,8 @@ Kinetic.Shape.prototype = {
|
|||||||
/**
|
/**
|
||||||
* helper method to fill text and appy shadows if needed
|
* helper method to fill text and appy shadows if needed
|
||||||
* @param {String} text
|
* @param {String} text
|
||||||
* @param {Number} x
|
|
||||||
* @param {Number} y
|
|
||||||
*/
|
*/
|
||||||
fillText: function(text, x, y) {
|
fillText: function(text) {
|
||||||
var appliedShadow = false;
|
var appliedShadow = false;
|
||||||
var context = this.getContext();
|
var context = this.getContext();
|
||||||
context.save();
|
context.save();
|
||||||
@ -2977,12 +2980,12 @@ Kinetic.Shape.prototype = {
|
|||||||
appliedShadow = this._applyShadow();
|
appliedShadow = this._applyShadow();
|
||||||
}
|
}
|
||||||
context.fillStyle = this.attrs.textFill;
|
context.fillStyle = this.attrs.textFill;
|
||||||
context.fillText(text, x, y);
|
context.fillText(text, 0, 0);
|
||||||
}
|
}
|
||||||
context.restore();
|
context.restore();
|
||||||
|
|
||||||
if(appliedShadow) {
|
if(appliedShadow) {
|
||||||
this.fillText(text, x, y);
|
this.fillText(text, 0, 0);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
@ -2992,7 +2995,7 @@ Kinetic.Shape.prototype = {
|
|||||||
* @param {Number} x
|
* @param {Number} x
|
||||||
* @param {Number} y
|
* @param {Number} y
|
||||||
*/
|
*/
|
||||||
strokeText: function(text, x, y) {
|
strokeText: function(text) {
|
||||||
var appliedShadow = false;
|
var appliedShadow = false;
|
||||||
var context = this.getContext();
|
var context = this.getContext();
|
||||||
context.save();
|
context.save();
|
||||||
@ -3010,12 +3013,12 @@ Kinetic.Shape.prototype = {
|
|||||||
}
|
}
|
||||||
context.lineWidth = this.attrs.textStrokeWidth;
|
context.lineWidth = this.attrs.textStrokeWidth;
|
||||||
context.strokeStyle = this.attrs.textStroke;
|
context.strokeStyle = this.attrs.textStroke;
|
||||||
context.strokeText(text, x, y);
|
context.strokeText(text, 0, 0);
|
||||||
}
|
}
|
||||||
context.restore();
|
context.restore();
|
||||||
|
|
||||||
if(appliedShadow) {
|
if(appliedShadow) {
|
||||||
this.strokeText(text, x, y);
|
this.strokeText(text, 0, 0);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
@ -3503,7 +3506,7 @@ Kinetic.Image.prototype = {
|
|||||||
|
|
||||||
if(a.crop) {
|
if(a.crop) {
|
||||||
scale = [a.width / a.crop.width, a.height / a.crop.height];
|
scale = [a.width / a.crop.width, a.height / a.crop.height];
|
||||||
offset = [-1 * a.crop.x, -7];
|
offset = [-1 * a.crop.x, -1 * a.crop.y];
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
scale = [a.width / a.image.width, a.height / a.image.height];
|
scale = [a.width / a.image.width, a.height / a.image.height];
|
||||||
@ -3873,7 +3876,7 @@ Kinetic.GlobalObject.addSettersGetters(Kinetic.Star, ['numPoints', 'innerRadius'
|
|||||||
/**
|
/**
|
||||||
* Text constructor
|
* Text constructor
|
||||||
* @constructor
|
* @constructor
|
||||||
* @augments Kinetic.Shape
|
* @augments Kinetic.Group
|
||||||
* @param {Object} config
|
* @param {Object} config
|
||||||
*/
|
*/
|
||||||
Kinetic.Text = function(config) {
|
Kinetic.Text = function(config) {
|
||||||
@ -3883,74 +3886,87 @@ Kinetic.Text = function(config) {
|
|||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
align: 'left',
|
align: 'left',
|
||||||
verticalAlign: 'top',
|
verticalAlign: 'top',
|
||||||
padding: 0,
|
|
||||||
fontStyle: 'normal',
|
fontStyle: 'normal',
|
||||||
|
padding: 0,
|
||||||
width: 'auto',
|
width: 'auto',
|
||||||
detectionType: 'pixel'
|
height: 'auto',
|
||||||
|
detectionType: 'path',
|
||||||
|
cornerRadius: 0
|
||||||
});
|
});
|
||||||
|
|
||||||
this.shapeType = "Text";
|
this.shapeType = "Text";
|
||||||
|
this.boxShape = new Kinetic.Rect({});
|
||||||
|
|
||||||
config.drawFunc = function() {
|
|
||||||
var context = this.getContext();
|
|
||||||
context.font = this.attrs.fontStyle + ' ' + this.attrs.fontSize + 'pt ' + this.attrs.fontFamily;
|
|
||||||
context.textBaseline = 'middle';
|
|
||||||
var textHeight = this.getTextHeight();
|
|
||||||
var textWidth = this.attrs.width === 'auto' ? this.getTextWidth() : this.attrs.width;
|
|
||||||
var p = this.attrs.padding;
|
|
||||||
var x = 0;
|
|
||||||
var y = 0;
|
|
||||||
var that = this;
|
var that = this;
|
||||||
|
|
||||||
switch (this.attrs.align) {
|
this.textShape = new Kinetic.Shape({
|
||||||
case 'center':
|
drawFunc: function() {
|
||||||
x = textWidth / -2 - p;
|
var context = this.getContext();
|
||||||
break;
|
|
||||||
case 'right':
|
|
||||||
x = -1 * textWidth - p;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (this.attrs.verticalAlign) {
|
// sync appliedShadow flag with boxShape
|
||||||
case 'middle':
|
this.appliedShadow = that.boxShape.appliedShadow;
|
||||||
y = textHeight / -2 - p;
|
|
||||||
break;
|
|
||||||
case 'bottom':
|
|
||||||
y = -1 * textHeight - p;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// draw path
|
context.font = that.attrs.fontStyle + ' ' + that.attrs.fontSize + 'pt ' + that.attrs.fontFamily;
|
||||||
|
context.textBaseline = 'middle';
|
||||||
|
context.textAlign = 'left';
|
||||||
context.save();
|
context.save();
|
||||||
|
|
||||||
|
var p = that.attrs.padding;
|
||||||
|
|
||||||
context.beginPath();
|
context.beginPath();
|
||||||
context.rect(x, y, textWidth + p * 2, textHeight + p * 2);
|
context.rect(p / 2, p / 2, that.getBoxWidth() - p, that.getBoxHeight() - p);
|
||||||
context.closePath();
|
context.closePath();
|
||||||
|
|
||||||
this.fill();
|
if(that.attrs.width !== 'auto' && (that.getTextWidth() > that.getBoxWidth() - p || that.getTextHeight() > that.getBoxHeight() - p)) {
|
||||||
this.stroke();
|
|
||||||
|
|
||||||
context.restore();
|
|
||||||
|
|
||||||
var tx = p + x;
|
|
||||||
var ty = textHeight / 2 + p + y;
|
|
||||||
|
|
||||||
// clipping region for max width
|
|
||||||
context.save();
|
|
||||||
if(this.attrs.width !== 'auto') {
|
|
||||||
context.beginPath();
|
|
||||||
context.rect(x, y, textWidth + p, textHeight + p * 2);
|
|
||||||
context.closePath();
|
|
||||||
context.clip();
|
context.clip();
|
||||||
}
|
}
|
||||||
|
|
||||||
// draw text
|
// horizontal align
|
||||||
this.fillText(this.attrs.text, tx, ty);
|
if(that.attrs.align === 'left') {
|
||||||
this.strokeText(this.attrs.text, tx, ty);
|
context.translate(p / 2, 0);
|
||||||
|
}
|
||||||
|
else if(that.attrs.align === 'center') {
|
||||||
|
context.translate((that.getBoxWidth() - that.getTextWidth()) / 2, 0);
|
||||||
|
}
|
||||||
|
// right
|
||||||
|
else {
|
||||||
|
context.translate(that.getBoxWidth() - that.getTextWidth() - p / 2, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// vertical align
|
||||||
|
if(that.attrs.verticalAlign === 'top') {
|
||||||
|
context.translate(0, (p + that.getTextHeight()) / 2);
|
||||||
|
}
|
||||||
|
else if(that.attrs.verticalAlign === 'middle') {
|
||||||
|
context.translate(0, that.getBoxHeight() / 2);
|
||||||
|
}
|
||||||
|
// bottom
|
||||||
|
else {
|
||||||
|
context.translate(0, that.getBoxHeight() - (p + that.getTextHeight()) / 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.fillText(that.attrs.text);
|
||||||
|
this.strokeText(that.attrs.text);
|
||||||
context.restore();
|
context.restore();
|
||||||
};
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// call super constructor
|
// call super constructor
|
||||||
Kinetic.Shape.apply(this, [config]);
|
Kinetic.Group.apply(this, [config]);
|
||||||
|
|
||||||
|
// add shapes to group
|
||||||
|
this.add(this.boxShape);
|
||||||
|
this.add(this.textShape);
|
||||||
|
|
||||||
|
// bind events to sync attrs
|
||||||
|
var attrs = ['width', 'height', 'cornerRadius', 'stroke', 'strokeWidth', 'fill', 'shadow', 'detectionType', 'textFill', 'textStroke', 'textStrokeWidth'];
|
||||||
|
|
||||||
|
for(var n = 0; n < attrs.length; n++) {
|
||||||
|
var attr = attrs[n];
|
||||||
|
this.on(attr + 'Change', this._syncAttrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
this._syncAttrs();
|
||||||
};
|
};
|
||||||
/*
|
/*
|
||||||
* Text methods
|
* Text methods
|
||||||
@ -3983,13 +3999,48 @@ Kinetic.Text.prototype = {
|
|||||||
width: metrics.width,
|
width: metrics.width,
|
||||||
height: parseInt(this.attrs.fontSize, 10)
|
height: parseInt(this.attrs.fontSize, 10)
|
||||||
};
|
};
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* get box width
|
||||||
|
*/
|
||||||
|
getBoxWidth: function() {
|
||||||
|
return (this.attrs.width === 'auto' ? this.getTextWidth() : this.attrs.width) + this.attrs.padding;
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* get box height
|
||||||
|
*/
|
||||||
|
getBoxHeight: function() {
|
||||||
|
return (this.attrs.height === 'auto' ? this.getTextHeight() : this.attrs.height) + this.attrs.padding;
|
||||||
|
},
|
||||||
|
_syncAttrs: function() {
|
||||||
|
this.boxShape.setAttrs({
|
||||||
|
width: this.getBoxWidth(),
|
||||||
|
height: this.getBoxHeight(),
|
||||||
|
cornerRadius: this.attrs.cornerRadius,
|
||||||
|
stroke: this.attrs.stroke,
|
||||||
|
strokeWidth: this.attrs.strokeWidth,
|
||||||
|
fill: this.attrs.fill,
|
||||||
|
shadow: this.attrs.shadow,
|
||||||
|
detectionType: this.attrs.detectionType
|
||||||
|
}, true);
|
||||||
|
/*
|
||||||
|
* sync attrs accessed by fillText and strokeText
|
||||||
|
* in Shape class, and also the detectionType
|
||||||
|
*/
|
||||||
|
this.textShape.setAttrs({
|
||||||
|
textFill: this.attrs.textFill,
|
||||||
|
textStroke: this.attrs.textStroke,
|
||||||
|
textStrokeWidth: this.attrs.textStrokeWidth,
|
||||||
|
shadow: this.attrs.shadow,
|
||||||
|
detectionType: this.attrs.detectionType
|
||||||
|
}, true);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
// extend Shape
|
// extend Shape
|
||||||
Kinetic.GlobalObject.extend(Kinetic.Text, Kinetic.Shape);
|
Kinetic.GlobalObject.extend(Kinetic.Text, Kinetic.Group);
|
||||||
|
|
||||||
// add setters and getters
|
// add setters and getters
|
||||||
Kinetic.GlobalObject.addSettersGetters(Kinetic.Text, ['fontFamily', 'fontSize', 'fontStyle', 'textFill', 'textStroke', 'textStrokeWidth', 'padding', 'align', 'verticalAlign', 'text', 'width']);
|
Kinetic.GlobalObject.addSettersGetters(Kinetic.Text, ['fontFamily', 'fontSize', 'fontStyle', 'textFill', 'textStroke', 'textStrokeWidth', 'padding', 'align', 'verticalAlign', 'text', 'width', 'height', 'cornerRadius', 'fill', 'stroke', 'strokeWidth', 'shadow']);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* set font family
|
* set font family
|
||||||
@ -4062,12 +4113,26 @@ Kinetic.GlobalObject.addSettersGetters(Kinetic.Text, ['fontFamily', 'fontSize',
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* set width
|
* set width of text box
|
||||||
* @name setWidth
|
* @name setWidth
|
||||||
* @methodOf Kinetic.Text.prototype
|
* @methodOf Kinetic.Text.prototype
|
||||||
* @param {Number} width
|
* @param {Number} width
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* set height of text box
|
||||||
|
* @name setHeight
|
||||||
|
* @methodOf Kinetic.Text.prototype
|
||||||
|
* @param {Number} height
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* set shadow of text or textbox
|
||||||
|
* @name setShadow
|
||||||
|
* @methodOf Kinetic.Text.prototype
|
||||||
|
* @param {Object} config
|
||||||
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* get font family
|
* get font family
|
||||||
* @name getFontFamily
|
* @name getFontFamily
|
||||||
@ -4129,10 +4194,22 @@ Kinetic.GlobalObject.addSettersGetters(Kinetic.Text, ['fontFamily', 'fontSize',
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* get total width including padding and borders
|
* get width of text box
|
||||||
* @name getWidth
|
* @name getWidth
|
||||||
* @methodOf Kinetic.Text.prototype
|
* @methodOf Kinetic.Text.prototype
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get height of text box
|
||||||
|
* @name getHeight
|
||||||
|
* @methodOf Kinetic.Text.prototype
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get shadow of text or textbox
|
||||||
|
* @name getShadow
|
||||||
|
* @methodOf Kinetic.Text.prototype
|
||||||
|
*/
|
||||||
///////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////
|
||||||
// Line
|
// Line
|
||||||
///////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////
|
||||||
|
6
dist/kinetic-core.min.js
vendored
6
dist/kinetic-core.min.js
vendored
File diff suppressed because one or more lines are too long
13
src/Node.js
13
src/Node.js
@ -86,7 +86,6 @@ Kinetic.Node.prototype = {
|
|||||||
*/
|
*/
|
||||||
for(var n = 0; n < types.length; n++) {
|
for(var n = 0; n < types.length; n++) {
|
||||||
var type = types[n];
|
var type = types[n];
|
||||||
//var event = (type.indexOf('touch') === -1) ? 'on' + type : type;
|
|
||||||
var event = type;
|
var event = type;
|
||||||
var parts = event.split('.');
|
var parts = event.split('.');
|
||||||
var baseEvent = parts[0];
|
var baseEvent = parts[0];
|
||||||
@ -173,8 +172,11 @@ Kinetic.Node.prototype = {
|
|||||||
/**
|
/**
|
||||||
* set attrs
|
* set attrs
|
||||||
* @param {Object} config
|
* @param {Object} config
|
||||||
|
* @param {Boolean} skipPublish set the second argument to true
|
||||||
|
* to skip publication of the attr change event in case you want
|
||||||
|
* to silently update an attribute
|
||||||
*/
|
*/
|
||||||
setAttrs: function(config) {
|
setAttrs: function(config, skipPublish) {
|
||||||
var go = Kinetic.GlobalObject;
|
var go = Kinetic.GlobalObject;
|
||||||
var that = this;
|
var that = this;
|
||||||
|
|
||||||
@ -185,7 +187,7 @@ Kinetic.Node.prototype = {
|
|||||||
var val = c[key];
|
var val = c[key];
|
||||||
|
|
||||||
// if obj doesn't have the val property, then create it
|
// if obj doesn't have the val property, then create it
|
||||||
if(obj[key] === undefined) {
|
if(obj[key] === undefined && val !== undefined) {
|
||||||
obj[key] = {};
|
obj[key] = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -253,7 +255,7 @@ Kinetic.Node.prototype = {
|
|||||||
* only fire change event for root
|
* only fire change event for root
|
||||||
* level attrs
|
* level attrs
|
||||||
*/
|
*/
|
||||||
if(level === 0) {
|
if(level === 0 && !skipPublish) {
|
||||||
that._fireChangeEvent(key);
|
that._fireChangeEvent(key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -695,6 +697,9 @@ Kinetic.Node.prototype = {
|
|||||||
},
|
},
|
||||||
_setAttr: function(obj, attr, val) {
|
_setAttr: function(obj, attr, val) {
|
||||||
if(val !== undefined) {
|
if(val !== undefined) {
|
||||||
|
if(obj === undefined) {
|
||||||
|
obj = {};
|
||||||
|
}
|
||||||
obj[attr] = val;
|
obj[attr] = val;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
14
src/Shape.js
14
src/Shape.js
@ -164,10 +164,8 @@ Kinetic.Shape.prototype = {
|
|||||||
/**
|
/**
|
||||||
* helper method to fill text and appy shadows if needed
|
* helper method to fill text and appy shadows if needed
|
||||||
* @param {String} text
|
* @param {String} text
|
||||||
* @param {Number} x
|
|
||||||
* @param {Number} y
|
|
||||||
*/
|
*/
|
||||||
fillText: function(text, x, y) {
|
fillText: function(text) {
|
||||||
var appliedShadow = false;
|
var appliedShadow = false;
|
||||||
var context = this.getContext();
|
var context = this.getContext();
|
||||||
context.save();
|
context.save();
|
||||||
@ -176,12 +174,12 @@ Kinetic.Shape.prototype = {
|
|||||||
appliedShadow = this._applyShadow();
|
appliedShadow = this._applyShadow();
|
||||||
}
|
}
|
||||||
context.fillStyle = this.attrs.textFill;
|
context.fillStyle = this.attrs.textFill;
|
||||||
context.fillText(text, x, y);
|
context.fillText(text, 0, 0);
|
||||||
}
|
}
|
||||||
context.restore();
|
context.restore();
|
||||||
|
|
||||||
if(appliedShadow) {
|
if(appliedShadow) {
|
||||||
this.fillText(text, x, y);
|
this.fillText(text, 0, 0);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
@ -191,7 +189,7 @@ Kinetic.Shape.prototype = {
|
|||||||
* @param {Number} x
|
* @param {Number} x
|
||||||
* @param {Number} y
|
* @param {Number} y
|
||||||
*/
|
*/
|
||||||
strokeText: function(text, x, y) {
|
strokeText: function(text) {
|
||||||
var appliedShadow = false;
|
var appliedShadow = false;
|
||||||
var context = this.getContext();
|
var context = this.getContext();
|
||||||
context.save();
|
context.save();
|
||||||
@ -209,12 +207,12 @@ Kinetic.Shape.prototype = {
|
|||||||
}
|
}
|
||||||
context.lineWidth = this.attrs.textStrokeWidth;
|
context.lineWidth = this.attrs.textStrokeWidth;
|
||||||
context.strokeStyle = this.attrs.textStroke;
|
context.strokeStyle = this.attrs.textStroke;
|
||||||
context.strokeText(text, x, y);
|
context.strokeText(text, 0, 0);
|
||||||
}
|
}
|
||||||
context.restore();
|
context.restore();
|
||||||
|
|
||||||
if(appliedShadow) {
|
if(appliedShadow) {
|
||||||
this.strokeText(text, x, y);
|
this.strokeText(text, 0, 0);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
|
@ -38,7 +38,7 @@ Kinetic.Image.prototype = {
|
|||||||
|
|
||||||
if(a.crop) {
|
if(a.crop) {
|
||||||
scale = [a.width / a.crop.width, a.height / a.crop.height];
|
scale = [a.width / a.crop.width, a.height / a.crop.height];
|
||||||
offset = [-1 * a.crop.x, -7];
|
offset = [-1 * a.crop.x, -1 * a.crop.y];
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
scale = [a.width / a.image.width, a.height / a.image.height];
|
scale = [a.width / a.image.width, a.height / a.image.height];
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
/**
|
/**
|
||||||
* Text constructor
|
* Text constructor
|
||||||
* @constructor
|
* @constructor
|
||||||
* @augments Kinetic.Shape
|
* @augments Kinetic.Group
|
||||||
* @param {Object} config
|
* @param {Object} config
|
||||||
*/
|
*/
|
||||||
Kinetic.Text = function(config) {
|
Kinetic.Text = function(config) {
|
||||||
@ -14,74 +14,87 @@ Kinetic.Text = function(config) {
|
|||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
align: 'left',
|
align: 'left',
|
||||||
verticalAlign: 'top',
|
verticalAlign: 'top',
|
||||||
padding: 0,
|
|
||||||
fontStyle: 'normal',
|
fontStyle: 'normal',
|
||||||
|
padding: 0,
|
||||||
width: 'auto',
|
width: 'auto',
|
||||||
detectionType: 'pixel'
|
height: 'auto',
|
||||||
|
detectionType: 'path',
|
||||||
|
cornerRadius: 0
|
||||||
});
|
});
|
||||||
|
|
||||||
this.shapeType = "Text";
|
this.shapeType = "Text";
|
||||||
|
this.boxShape = new Kinetic.Rect({});
|
||||||
|
|
||||||
config.drawFunc = function() {
|
|
||||||
var context = this.getContext();
|
|
||||||
context.font = this.attrs.fontStyle + ' ' + this.attrs.fontSize + 'pt ' + this.attrs.fontFamily;
|
|
||||||
context.textBaseline = 'middle';
|
|
||||||
var textHeight = this.getTextHeight();
|
|
||||||
var textWidth = this.attrs.width === 'auto' ? this.getTextWidth() : this.attrs.width;
|
|
||||||
var p = this.attrs.padding;
|
|
||||||
var x = 0;
|
|
||||||
var y = 0;
|
|
||||||
var that = this;
|
var that = this;
|
||||||
|
|
||||||
switch (this.attrs.align) {
|
this.textShape = new Kinetic.Shape({
|
||||||
case 'center':
|
drawFunc: function() {
|
||||||
x = textWidth / -2 - p;
|
var context = this.getContext();
|
||||||
break;
|
|
||||||
case 'right':
|
|
||||||
x = -1 * textWidth - p;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (this.attrs.verticalAlign) {
|
// sync appliedShadow flag with boxShape
|
||||||
case 'middle':
|
this.appliedShadow = that.boxShape.appliedShadow;
|
||||||
y = textHeight / -2 - p;
|
|
||||||
break;
|
|
||||||
case 'bottom':
|
|
||||||
y = -1 * textHeight - p;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// draw path
|
context.font = that.attrs.fontStyle + ' ' + that.attrs.fontSize + 'pt ' + that.attrs.fontFamily;
|
||||||
|
context.textBaseline = 'middle';
|
||||||
|
context.textAlign = 'left';
|
||||||
context.save();
|
context.save();
|
||||||
|
|
||||||
|
var p = that.attrs.padding;
|
||||||
|
|
||||||
context.beginPath();
|
context.beginPath();
|
||||||
context.rect(x, y, textWidth + p * 2, textHeight + p * 2);
|
context.rect(p / 2, p / 2, that.getBoxWidth() - p, that.getBoxHeight() - p);
|
||||||
context.closePath();
|
context.closePath();
|
||||||
|
|
||||||
this.fill();
|
if(that.attrs.width !== 'auto' && (that.getTextWidth() > that.getBoxWidth() - p || that.getTextHeight() > that.getBoxHeight() - p)) {
|
||||||
this.stroke();
|
|
||||||
|
|
||||||
context.restore();
|
|
||||||
|
|
||||||
var tx = p + x;
|
|
||||||
var ty = textHeight / 2 + p + y;
|
|
||||||
|
|
||||||
// clipping region for max width
|
|
||||||
context.save();
|
|
||||||
if(this.attrs.width !== 'auto') {
|
|
||||||
context.beginPath();
|
|
||||||
context.rect(x, y, textWidth + p, textHeight + p * 2);
|
|
||||||
context.closePath();
|
|
||||||
context.clip();
|
context.clip();
|
||||||
}
|
}
|
||||||
|
|
||||||
// draw text
|
// horizontal align
|
||||||
this.fillText(this.attrs.text, tx, ty);
|
if(that.attrs.align === 'left') {
|
||||||
this.strokeText(this.attrs.text, tx, ty);
|
context.translate(p / 2, 0);
|
||||||
|
}
|
||||||
|
else if(that.attrs.align === 'center') {
|
||||||
|
context.translate((that.getBoxWidth() - that.getTextWidth()) / 2, 0);
|
||||||
|
}
|
||||||
|
// right
|
||||||
|
else {
|
||||||
|
context.translate(that.getBoxWidth() - that.getTextWidth() - p / 2, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// vertical align
|
||||||
|
if(that.attrs.verticalAlign === 'top') {
|
||||||
|
context.translate(0, (p + that.getTextHeight()) / 2);
|
||||||
|
}
|
||||||
|
else if(that.attrs.verticalAlign === 'middle') {
|
||||||
|
context.translate(0, that.getBoxHeight() / 2);
|
||||||
|
}
|
||||||
|
// bottom
|
||||||
|
else {
|
||||||
|
context.translate(0, that.getBoxHeight() - (p + that.getTextHeight()) / 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.fillText(that.attrs.text);
|
||||||
|
this.strokeText(that.attrs.text);
|
||||||
context.restore();
|
context.restore();
|
||||||
};
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// call super constructor
|
// call super constructor
|
||||||
Kinetic.Shape.apply(this, [config]);
|
Kinetic.Group.apply(this, [config]);
|
||||||
|
|
||||||
|
// add shapes to group
|
||||||
|
this.add(this.boxShape);
|
||||||
|
this.add(this.textShape);
|
||||||
|
|
||||||
|
// bind events to sync attrs
|
||||||
|
var attrs = ['width', 'height', 'cornerRadius', 'stroke', 'strokeWidth', 'fill', 'shadow', 'detectionType', 'textFill', 'textStroke', 'textStrokeWidth'];
|
||||||
|
|
||||||
|
for(var n = 0; n < attrs.length; n++) {
|
||||||
|
var attr = attrs[n];
|
||||||
|
this.on(attr + 'Change', this._syncAttrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
this._syncAttrs();
|
||||||
};
|
};
|
||||||
/*
|
/*
|
||||||
* Text methods
|
* Text methods
|
||||||
@ -114,13 +127,48 @@ Kinetic.Text.prototype = {
|
|||||||
width: metrics.width,
|
width: metrics.width,
|
||||||
height: parseInt(this.attrs.fontSize, 10)
|
height: parseInt(this.attrs.fontSize, 10)
|
||||||
};
|
};
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* get box width
|
||||||
|
*/
|
||||||
|
getBoxWidth: function() {
|
||||||
|
return (this.attrs.width === 'auto' ? this.getTextWidth() : this.attrs.width) + this.attrs.padding;
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* get box height
|
||||||
|
*/
|
||||||
|
getBoxHeight: function() {
|
||||||
|
return (this.attrs.height === 'auto' ? this.getTextHeight() : this.attrs.height) + this.attrs.padding;
|
||||||
|
},
|
||||||
|
_syncAttrs: function() {
|
||||||
|
this.boxShape.setAttrs({
|
||||||
|
width: this.getBoxWidth(),
|
||||||
|
height: this.getBoxHeight(),
|
||||||
|
cornerRadius: this.attrs.cornerRadius,
|
||||||
|
stroke: this.attrs.stroke,
|
||||||
|
strokeWidth: this.attrs.strokeWidth,
|
||||||
|
fill: this.attrs.fill,
|
||||||
|
shadow: this.attrs.shadow,
|
||||||
|
detectionType: this.attrs.detectionType
|
||||||
|
}, true);
|
||||||
|
/*
|
||||||
|
* sync attrs accessed by fillText and strokeText
|
||||||
|
* in Shape class, and also the detectionType
|
||||||
|
*/
|
||||||
|
this.textShape.setAttrs({
|
||||||
|
textFill: this.attrs.textFill,
|
||||||
|
textStroke: this.attrs.textStroke,
|
||||||
|
textStrokeWidth: this.attrs.textStrokeWidth,
|
||||||
|
shadow: this.attrs.shadow,
|
||||||
|
detectionType: this.attrs.detectionType
|
||||||
|
}, true);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
// extend Shape
|
// extend Shape
|
||||||
Kinetic.GlobalObject.extend(Kinetic.Text, Kinetic.Shape);
|
Kinetic.GlobalObject.extend(Kinetic.Text, Kinetic.Group);
|
||||||
|
|
||||||
// add setters and getters
|
// add setters and getters
|
||||||
Kinetic.GlobalObject.addSettersGetters(Kinetic.Text, ['fontFamily', 'fontSize', 'fontStyle', 'textFill', 'textStroke', 'textStrokeWidth', 'padding', 'align', 'verticalAlign', 'text', 'width']);
|
Kinetic.GlobalObject.addSettersGetters(Kinetic.Text, ['fontFamily', 'fontSize', 'fontStyle', 'textFill', 'textStroke', 'textStrokeWidth', 'padding', 'align', 'verticalAlign', 'text', 'width', 'height', 'cornerRadius', 'fill', 'stroke', 'strokeWidth', 'shadow']);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* set font family
|
* set font family
|
||||||
@ -193,12 +241,26 @@ Kinetic.GlobalObject.addSettersGetters(Kinetic.Text, ['fontFamily', 'fontSize',
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* set width
|
* set width of text box
|
||||||
* @name setWidth
|
* @name setWidth
|
||||||
* @methodOf Kinetic.Text.prototype
|
* @methodOf Kinetic.Text.prototype
|
||||||
* @param {Number} width
|
* @param {Number} width
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* set height of text box
|
||||||
|
* @name setHeight
|
||||||
|
* @methodOf Kinetic.Text.prototype
|
||||||
|
* @param {Number} height
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* set shadow of text or textbox
|
||||||
|
* @name setShadow
|
||||||
|
* @methodOf Kinetic.Text.prototype
|
||||||
|
* @param {Object} config
|
||||||
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* get font family
|
* get font family
|
||||||
* @name getFontFamily
|
* @name getFontFamily
|
||||||
@ -260,7 +322,19 @@ Kinetic.GlobalObject.addSettersGetters(Kinetic.Text, ['fontFamily', 'fontSize',
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* get total width including padding and borders
|
* get width of text box
|
||||||
* @name getWidth
|
* @name getWidth
|
||||||
* @methodOf Kinetic.Text.prototype
|
* @methodOf Kinetic.Text.prototype
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get height of text box
|
||||||
|
* @name getHeight
|
||||||
|
* @methodOf Kinetic.Text.prototype
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get shadow of text or textbox
|
||||||
|
* @name getShadow
|
||||||
|
* @methodOf Kinetic.Text.prototype
|
||||||
|
*/
|
@ -2827,7 +2827,7 @@ Test.prototype.tests = {
|
|||||||
test(group.get('Group').length === 0, 'group should have 0 groups');
|
test(group.get('Group').length === 0, 'group should have 0 groups');
|
||||||
|
|
||||||
},
|
},
|
||||||
'SHAPE - add text': function(containerId) {
|
'SHAPE - text getters and setters': function(containerId) {
|
||||||
var stage = new Kinetic.Stage({
|
var stage = new Kinetic.Stage({
|
||||||
container: containerId,
|
container: containerId,
|
||||||
width: 578,
|
width: 578,
|
||||||
@ -2838,60 +2838,105 @@ Test.prototype.tests = {
|
|||||||
var text = new Kinetic.Text({
|
var text = new Kinetic.Text({
|
||||||
x: stage.getWidth() / 2,
|
x: stage.getWidth() / 2,
|
||||||
y: stage.getHeight() / 2,
|
y: stage.getHeight() / 2,
|
||||||
stroke: 'green',
|
stroke: '#555',
|
||||||
strokeWidth: 5,
|
strokeWidth: 5,
|
||||||
fill: '#ddd',
|
fill: '#ddd',
|
||||||
text: 'Hello World!',
|
text: 'Hello World!',
|
||||||
fontSize: 60,
|
fontSize: 50,
|
||||||
fontFamily: 'Calibri',
|
fontFamily: 'Calibri',
|
||||||
fontStyle: 'normal',
|
fontStyle: 'normal',
|
||||||
textFill: '#888',
|
textFill: '#888',
|
||||||
textStroke: '#333',
|
textStroke: '#333',
|
||||||
padding: 10,
|
align: 'right',
|
||||||
fontStyle: 'normal',
|
verticalAlign: 'bottom',
|
||||||
align: 'center',
|
width: 400,
|
||||||
verticalAlign: 'middle',
|
height: 100,
|
||||||
width: 200,
|
padding: 40,
|
||||||
shadow: {
|
shadow: {
|
||||||
color: 'black',
|
color: 'black',
|
||||||
blur: 1,
|
blur: 1,
|
||||||
offset: [10, 10],
|
offset: [10, 10],
|
||||||
alpha: 0.2
|
alpha: 0.2
|
||||||
}
|
},
|
||||||
|
cornerRadius: 10,
|
||||||
|
draggable: true,
|
||||||
|
detectionType: 'path'
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// center text box
|
||||||
|
text.setOffset(text.getBoxWidth() / 2, text.getBoxHeight() / 2);
|
||||||
|
|
||||||
layer.add(text);
|
layer.add(text);
|
||||||
|
stage.add(layer);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* test getters and setters
|
* test getters and setters
|
||||||
*/
|
*/
|
||||||
text.setText('Bye World!');
|
|
||||||
test(text.getText() === 'Bye World!', 'text should be Bye World!');
|
test(text.getX() === stage.getWidth() / 2, 'text box x should be in center of stage');
|
||||||
test(text.getPadding() === 10, 'padding should be 10');
|
test(text.getY() === stage.getHeight() / 2, 'text box y should be in center of stage');
|
||||||
|
test(text.getStroke() === '#555', 'text box stroke should be #555');
|
||||||
|
test(text.getStrokeWidth() === 5, 'text box stroke width should be 5');
|
||||||
|
test(text.getFill() === '#ddd', 'text box fill should be #ddd');
|
||||||
|
test(text.getText() === 'Hello World!', 'text should be Hello World!');
|
||||||
|
test(text.getFontSize() == 50, 'font size should 50');
|
||||||
|
test(text.getFontFamily() == 'Calibri', 'font family should be Calibri');
|
||||||
test(text.getFontStyle() == 'normal', 'font style should be normal');
|
test(text.getFontStyle() == 'normal', 'font style should be normal');
|
||||||
text.setPadding(20);
|
test(text.getTextFill() == '#888', 'text fill should be #888');
|
||||||
test(text.getPadding() === 20, 'padding should be 20');
|
test(text.getTextStroke() == '#333', 'text fill should be #333');
|
||||||
test(text.getWidth() === 200, 'width should be 200');
|
test(text.getAlign() === 'right', 'text should be aligned right');
|
||||||
|
test(text.getVerticalAlign() === 'bottom', 'text should be vertically aligned at the bottom');
|
||||||
stage.add(layer);
|
test(text.getWidth() === 400, 'width should be 400');
|
||||||
|
test(text.getHeight() === 100, 'height should be 100');
|
||||||
|
test(text.getPadding() === 40, 'padding should be 40');
|
||||||
|
test(text.getShadow().color === 'black', 'text box shadow color should be black');
|
||||||
|
test(text.getCornerRadius() === 10, 'text box corner radius should be 10');
|
||||||
|
test(text.getDraggable() === true, 'text should be draggable');
|
||||||
|
test(text.getDetectionType() === 'path', 'text detection type should be path');
|
||||||
|
|
||||||
|
text.setX(1);
|
||||||
|
text.setY(2);
|
||||||
|
text.setStroke('orange');
|
||||||
|
text.setStrokeWidth(20);
|
||||||
|
text.setFill('red');
|
||||||
|
text.setText('bye world!');
|
||||||
|
text.setFontSize(10);
|
||||||
text.setFontFamily('Arial');
|
text.setFontFamily('Arial');
|
||||||
text.setFontSize(30);
|
text.setFontStyle('bold');
|
||||||
text.setFontStyle('italic');
|
text.setTextFill('green');
|
||||||
text.setAlign('right');
|
text.setTextStroke('yellow');
|
||||||
|
text.setAlign('left');
|
||||||
text.setVerticalAlign('top');
|
text.setVerticalAlign('top');
|
||||||
text.setTextFill('blue');
|
text.setWidth(300);
|
||||||
text.setTextStroke('red');
|
text.setHeight(75);
|
||||||
text.setTextStrokeWidth(10);
|
text.setPadding(20);
|
||||||
|
text.setShadow({
|
||||||
|
color: 'green'
|
||||||
|
});
|
||||||
|
text.setCornerRadius(20);
|
||||||
|
text.setDraggable(false);
|
||||||
|
text.setDetectionType('pixel');
|
||||||
|
|
||||||
test(text.getFontFamily() === 'Arial', 'font family should be Arial');
|
test(text.getX() === 1, 'text box x should be 1');
|
||||||
test(text.getFontSize() === 30, 'text size should be 30');
|
test(text.getY() === 2, 'text box y should be 2');
|
||||||
test(text.getFontStyle() == 'italic', 'font style should be italic');
|
test(text.getStroke() === 'orange', 'text box stroke should be orange');
|
||||||
test(text.getAlign() === 'right', 'text align should be right');
|
test(text.getStrokeWidth() === 20, 'text box stroke width should be 20');
|
||||||
test(text.getVerticalAlign() === 'top', 'vertical align should be top');
|
test(text.getFill() === 'red', 'text box fill should be red');
|
||||||
test(text.getTextFill() === 'blue', 'text fill should be blue');
|
test(text.getText() === 'bye world!', 'text should be bye world!');
|
||||||
test(text.getTextStroke() === 'red', 'text stroke should be red');
|
test(text.getFontSize() == 10, 'font size should 10');
|
||||||
test(text.getTextStrokeWidth() === 10, 'test stroke width should be 10');
|
test(text.getFontFamily() == 'Arial', 'font family should be Arial');
|
||||||
|
test(text.getFontStyle() == 'bold', 'font style should be bold');
|
||||||
|
test(text.getTextFill() == 'green', 'text fill should be green');
|
||||||
|
test(text.getTextStroke() == 'yellow', 'text fill should be yellow');
|
||||||
|
test(text.getAlign() === 'left', 'text should be aligned left');
|
||||||
|
test(text.getVerticalAlign() === 'top', 'text should be vertically aligned at the top');
|
||||||
|
test(text.getWidth() === 300, 'width should be 300');
|
||||||
|
test(text.getHeight() === 75, 'height should be 75');
|
||||||
|
test(text.getPadding() === 20, 'padding should be 20');
|
||||||
|
test(text.getShadow().color === 'green', 'text box shadow color should be green');
|
||||||
|
test(text.getCornerRadius() === 20, 'text box corner radius should be 20');
|
||||||
|
test(text.getDraggable() === false, 'text draggable should be false');
|
||||||
|
test(text.getDetectionType() === 'pixel', 'text detection type should be pixel');
|
||||||
|
|
||||||
},
|
},
|
||||||
'SHAPE - get text metrics': function(containerId) {
|
'SHAPE - get text metrics': function(containerId) {
|
||||||
@ -2921,16 +2966,11 @@ Test.prototype.tests = {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
text.on('mouseover', function() {
|
|
||||||
console.log('mouseover text');
|
|
||||||
});
|
|
||||||
// test text width before adding it to stage
|
// test text width before adding it to stage
|
||||||
test(text.getTextWidth() > 0, 'text width should have a value');
|
test(text.getTextWidth() > 0, 'text width should have a value');
|
||||||
layer.add(text);
|
layer.add(text);
|
||||||
stage.add(layer);
|
stage.add(layer);
|
||||||
|
|
||||||
text.saveData();
|
|
||||||
|
|
||||||
test(text.getTextSize().width > 0, 'text width should have a value');
|
test(text.getTextSize().width > 0, 'text width should have a value');
|
||||||
test(text.getTextSize().height > 0, 'text height should have a value');
|
test(text.getTextSize().height > 0, 'text height should have a value');
|
||||||
test(text.getTextWidth() > 0, 'text width should have a value');
|
test(text.getTextWidth() > 0, 'text width should have a value');
|
||||||
|
Loading…
Reference in New Issue
Block a user