This commit is contained in:
Лаврёнов Антон
2014-02-27 09:13:55 +08:00
13 changed files with 652 additions and 488 deletions

View File

@@ -2,14 +2,21 @@
// calculate pixel ratio
var canvas = document.createElement('canvas'),
context = canvas.getContext('2d'),
devicePixelRatio = window.devicePixelRatio || 1,
backingStoreRatio = context.webkitBackingStorePixelRatio
|| context.mozBackingStorePixelRatio
|| context.msBackingStorePixelRatio
|| context.oBackingStorePixelRatio
|| context.backingStorePixelRatio
|| 1,
_pixelRatio = devicePixelRatio / backingStoreRatio;
// if using a mobile device, calculate the pixel ratio. Otherwise, just use
// 1. For desktop browsers, if the user has zoom enabled, it affects the pixel ratio
// and causes artifacts on the canvas. As of 02/26/2014, there doesn't seem to be a way
// to reliably calculate the browser zoom for modern browsers, which is why we just set
// the pixel ratio to 1 for desktops
_pixelRatio = Kinetic.UA.mobile ? (function() {
var devicePixelRatio = window.devicePixelRatio || 1,
backingStoreRatio = context.webkitBackingStorePixelRatio
|| context.mozBackingStorePixelRatio
|| context.msBackingStorePixelRatio
|| context.oBackingStorePixelRatio
|| context.backingStorePixelRatio
|| 1;
return devicePixelRatio / backingStoreRatio;
})() : 1;
/**
* Canvas Renderer constructor

View File

@@ -50,6 +50,7 @@ var Kinetic = {};
traceArrMax: 100,
dblClickWindow: 400,
pixelRatio: undefined,
enableThrottling: true,
// user agent
UA: (function() {
@@ -60,11 +61,17 @@ var Kinetic = {};
/(opera)(?:.*version|)[ \/]([\w.]+)/.exec( ua ) ||
/(msie) ([\w.]+)/.exec( ua ) ||
ua.indexOf('compatible') < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec( ua ) ||
[];
[],
// adding mobile flag as well
mobile = !!(navigator.userAgent.match(/Android|BlackBerry|iPhone|iPad|iPod|Opera Mini|IEMobile/i));
return {
browser: match[ 1 ] || '',
version: match[ 2 ] || '0'
version: match[ 2 ] || '0',
// adding mobile flab
mobile: mobile
};
})(),

View File

@@ -342,58 +342,64 @@
}
},
_mouseover: function(evt) {
this._fire(CONTENT_MOUSEOVER, evt);
if (!Kinetic.UA.mobile) {
this._fire(CONTENT_MOUSEOVER, evt);
}
},
_mouseout: function(evt) {
this._setPointerPosition(evt);
var targetShape = this.targetShape;
if (!Kinetic.UA.mobile) {
this._setPointerPosition(evt);
var targetShape = this.targetShape;
if(targetShape && !Kinetic.isDragging()) {
targetShape._fireAndBubble(MOUSEOUT, evt);
targetShape._fireAndBubble(MOUSELEAVE, evt);
this.targetShape = null;
}
this.pointerPos = undefined;
this._fire(CONTENT_MOUSEOUT, evt);
},
_mousemove: function(evt) {
this._setPointerPosition(evt);
var dd = Kinetic.DD,
shape = this.getIntersection(this.getPointerPosition());
if(shape && shape.isListening()) {
if(!Kinetic.isDragging() && (!this.targetShape || this.targetShape._id !== shape._id)) {
if(this.targetShape) {
this.targetShape._fireAndBubble(MOUSEOUT, evt, shape);
this.targetShape._fireAndBubble(MOUSELEAVE, evt, shape);
}
shape._fireAndBubble(MOUSEOVER, evt, this.targetShape);
shape._fireAndBubble(MOUSEENTER, evt, this.targetShape);
this.targetShape = shape;
}
else {
shape._fireAndBubble(MOUSEMOVE, evt);
}
}
/*
* if no shape was detected, clear target shape and try
* to run mouseout from previous target shape
*/
else {
if(this.targetShape && !Kinetic.isDragging()) {
this.targetShape._fireAndBubble(MOUSEOUT, evt);
this.targetShape._fireAndBubble(MOUSELEAVE, evt);
if(targetShape && !Kinetic.isDragging()) {
targetShape._fireAndBubble(MOUSEOUT, evt);
targetShape._fireAndBubble(MOUSELEAVE, evt);
this.targetShape = null;
}
this.pointerPos = undefined;
this._fire(CONTENT_MOUSEOUT, evt);
}
},
_mousemove: Kinetic.Util._throttle(function(evt) {
if (!Kinetic.UA.mobile) {
this._setPointerPosition(evt);
var dd = Kinetic.DD,
shape = this.getIntersection(this.getPointerPosition());
// content event
this._fire(CONTENT_MOUSEMOVE, evt);
if(shape && shape.isListening()) {
if(!Kinetic.isDragging() && (!this.targetShape || this.targetShape._id !== shape._id)) {
if(this.targetShape) {
this.targetShape._fireAndBubble(MOUSEOUT, evt, shape);
this.targetShape._fireAndBubble(MOUSELEAVE, evt, shape);
}
shape._fireAndBubble(MOUSEOVER, evt, this.targetShape);
shape._fireAndBubble(MOUSEENTER, evt, this.targetShape);
this.targetShape = shape;
}
else {
shape._fireAndBubble(MOUSEMOVE, evt);
}
}
/*
* if no shape was detected, clear target shape and try
* to run mouseout from previous target shape
*/
else {
if(this.targetShape && !Kinetic.isDragging()) {
this.targetShape._fireAndBubble(MOUSEOUT, evt);
this.targetShape._fireAndBubble(MOUSELEAVE, evt);
this.targetShape = null;
}
if(dd) {
dd._drag(evt);
}
// content event
this._fire(CONTENT_MOUSEMOVE, evt);
if(dd) {
dd._drag(evt);
}
}
// always call preventDefault for desktop events because some browsers
@@ -401,21 +407,23 @@
if (evt.preventDefault) {
evt.preventDefault();
}
},
}, 17),
_mousedown: function(evt) {
this._setPointerPosition(evt);
var shape = this.getIntersection(this.getPointerPosition());
if (!Kinetic.UA.mobile) {
this._setPointerPosition(evt);
var shape = this.getIntersection(this.getPointerPosition());
Kinetic.listenClickTap = true;
Kinetic.listenClickTap = true;
if (shape && shape.isListening()) {
this.clickStartShape = shape;
shape._fireAndBubble(MOUSEDOWN, evt);
if (shape && shape.isListening()) {
this.clickStartShape = shape;
shape._fireAndBubble(MOUSEDOWN, evt);
}
// content event
this._fire(CONTENT_MOUSEDOWN, evt);
}
// content event
this._fire(CONTENT_MOUSEDOWN, evt);
// always call preventDefault for desktop events because some browsers
// try to drag and drop the canvas element
if (evt.preventDefault) {
@@ -423,45 +431,48 @@
}
},
_mouseup: function(evt) {
this._setPointerPosition(evt);
var shape = this.getIntersection(this.getPointerPosition()),
clickStartShape = this.clickStartShape,
fireDblClick = false;
if (!Kinetic.UA.mobile) {
this._setPointerPosition(evt);
var that = this,
shape = this.getIntersection(this.getPointerPosition()),
clickStartShape = this.clickStartShape,
fireDblClick = false;
if(Kinetic.inDblClickWindow) {
fireDblClick = true;
Kinetic.inDblClickWindow = false;
}
else {
Kinetic.inDblClickWindow = true;
}
if(Kinetic.inDblClickWindow) {
fireDblClick = true;
Kinetic.inDblClickWindow = false;
}
else {
Kinetic.inDblClickWindow = true;
}
setTimeout(function() {
Kinetic.inDblClickWindow = false;
}, Kinetic.dblClickWindow);
setTimeout(function() {
Kinetic.inDblClickWindow = false;
}, Kinetic.dblClickWindow);
if (shape && shape.isListening()) {
shape._fireAndBubble(MOUSEUP, evt);
if (shape && shape.isListening()) {
shape._fireAndBubble(MOUSEUP, evt);
// detect if click or double click occurred
if(Kinetic.listenClickTap && clickStartShape && clickStartShape._id === shape._id) {
shape._fireAndBubble(CLICK, evt);
// detect if click or double click occurred
if(Kinetic.listenClickTap && clickStartShape && clickStartShape._id === shape._id) {
shape._fireAndBubble(CLICK, evt);
if(fireDblClick) {
shape._fireAndBubble(DBL_CLICK, evt);
if(fireDblClick) {
shape._fireAndBubble(DBL_CLICK, evt);
}
}
}
}
// content events
this._fire(CONTENT_MOUSEUP, evt);
if (Kinetic.listenClickTap) {
this._fire(CONTENT_CLICK, evt);
if(fireDblClick) {
this._fire(CONTENT_DBL_CLICK, evt);
// content events
this._fire(CONTENT_MOUSEUP, evt);
if (Kinetic.listenClickTap) {
this._fire(CONTENT_CLICK, evt);
if(fireDblClick) {
this._fire(CONTENT_DBL_CLICK, evt);
}
}
}
Kinetic.listenClickTap = false;
Kinetic.listenClickTap = false;
}
// always call preventDefault for desktop events because some browsers
// try to drag and drop the canvas element
@@ -530,7 +541,7 @@
Kinetic.listenClickTap = false;
},
_touchmove: function(evt) {
_touchmove: Kinetic.Util._throttle(function(evt) {
this._setPointerPosition(evt);
var dd = Kinetic.DD,
shape = this.getIntersection(this.getPointerPosition());
@@ -549,7 +560,7 @@
if(dd) {
dd._drag(evt);
}
},
}, 17),
_setPointerPosition: function(evt) {
var contentPosition = this._getContentPosition(),
offsetX = evt.offsetX,

View File

@@ -319,6 +319,40 @@
_isString: function(obj) {
return Object.prototype.toString.call(obj) == OBJECT_STRING;
},
// Returns a function, that, when invoked, will only be triggered at most once
// during a given window of time. Normally, the throttled function will run
// as much as it can, without ever going more than once per `wait` duration;
// but if you'd like to disable the execution on the leading edge, pass
// `{leading: false}`. To disable execution on the trailing edge, ditto.
_throttle: function(func, wait, options) {
var context, args, result;
var timeout = null;
var previous = 0;
options || (options = {});
var later = function() {
previous = options.leading === false ? 0 : new Date().getTime();
timeout = null;
result = func.apply(context, args);
context = args = null;
};
return function() {
var now = new Date().getTime();
if (!previous && options.leading === false) previous = now;
var remaining = wait - (now - previous);
context = this;
args = arguments;
if (remaining <= 0) {
clearTimeout(timeout);
timeout = null;
previous = now;
result = func.apply(context, args);
context = args = null;
} else if (!timeout && options.trailing !== false) {
timeout = setTimeout(later, remaining);
}
return result;
};
},
/*
* other utils
*/

View File

@@ -13,6 +13,7 @@
* @param {String} [config.fontFamily] default is Calibri
* @param {Number} [config.fontSize] default is 12
* @param {String} [config.fontStyle] can be normal, bold, or italic. Default is normal
* @param {String} [config.fontVariant] can be normal or small-caps. Default is normal
* @param {String} config.text
* @param {String} config.data SVG data string
* @@shapeParams
@@ -380,6 +381,23 @@
* @memberof Kinetic.TextPath.prototype
*/
Kinetic.Factory.addGetterSetter(Kinetic.TextPath, 'fontVariant', NORMAL);
/**
* set font variant. Can be 'normal' or 'small-caps'. 'normal' is the default.
* @name setFontVariant
* @method
* @memberof Kinetic.TextPath.prototype
* @param {String} fontVariant
*/
/**
* @get font variant
* @name getFontVariant
* @method
* @memberof Kinetic.TextPath.prototype
*/
Kinetic.Factory.addGetter(Kinetic.TextPath, 'text', EMPTY_STRING);
/**

View File

@@ -18,7 +18,7 @@
WORD = 'word',
CHAR = 'char',
NONE = 'none',
ATTR_CHANGE_LIST = ['fontFamily', 'fontSize', 'fontStyle', 'padding', 'align', 'lineHeight', 'text', 'width', 'height', 'wrap'],
ATTR_CHANGE_LIST = ['fontFamily', 'fontSize', 'fontStyle', 'fontVariant', 'padding', 'align', 'lineHeight', 'text', 'width', 'height', 'wrap'],
// cached variables
attrChangeListLen = ATTR_CHANGE_LIST.length,
@@ -33,6 +33,7 @@
* @param {String} [config.fontFamily] default is Arial
* @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.fontVariant] can be normal or small-caps. Default is normal
* @param {String} config.text
* @param {String} [config.align] can be left, center, or right
* @param {Number} [config.padding]
@@ -193,7 +194,7 @@
};
},
_getContextFont: function() {
return this.getFontStyle() + SPACE + this.getFontSize() + PX_SPACE + this.getFontFamily();
return this.getFontStyle() + SPACE + this.getFontVariant() + SPACE + this.getFontSize() + PX_SPACE + this.getFontFamily();
},
_addTextLine: function (line, width) {
return this.textArr.push({text: line, width: width});
@@ -220,7 +221,7 @@
this.textArr = [];
dummyContext.save();
dummyContext.font = this.getFontStyle() + SPACE + fontSize + PX_SPACE + this.getFontFamily();
dummyContext.font = this._getContextFont();
for (var i = 0, max = lines.length; i < max; ++i) {
var line = lines[i],
lineWidth = this._getTextWidth(line);
@@ -364,6 +365,23 @@
* text.fontStyle('bold');
*/
Kinetic.Factory.addGetterSetter(Kinetic.Text, 'fontVariant', NORMAL);
/**
* set font variant. Can be 'normal' or 'small-caps'. 'normal' is the default.
* @name fontVariant
* @method
* @memberof Kinetic.Text.prototype
* @param {String} fontVariant
* @returns {String}
* @example
* // get font variant<br>
* var fontVariant = text.fontVariant();<br><br>
*
* // set font variant<br>
* text.fontVariant('small-caps');
*/
Kinetic.Factory.addGetterSetter(Kinetic.Text, 'padding', 0);
/**