2010-07-27 21:58:23 +00:00
|
|
|
(function() {
|
2012-06-15 15:35:05 +00:00
|
|
|
|
2011-09-22 16:40:31 +00:00
|
|
|
/**
|
|
|
|
|
* @namespace
|
|
|
|
|
*/
|
2011-08-14 21:35:36 +00:00
|
|
|
fabric.util = { };
|
2012-06-15 15:35:05 +00:00
|
|
|
|
2010-06-09 22:34:55 +00:00
|
|
|
/**
|
2010-10-15 02:16:24 +00:00
|
|
|
* Removes value from an array.
|
2010-06-09 22:34:55 +00:00
|
|
|
* Presence of value (and its position in an array) is determined via `Array.prototype.indexOf`
|
2010-07-27 21:58:23 +00:00
|
|
|
* @static
|
2010-10-15 02:16:24 +00:00
|
|
|
* @memberOf fabric.util
|
2010-07-27 21:58:23 +00:00
|
|
|
* @method removeFromArray
|
2010-06-09 22:34:55 +00:00
|
|
|
* @param {Array} array
|
|
|
|
|
* @param {Any} value
|
|
|
|
|
* @return {Array} original array
|
|
|
|
|
*/
|
|
|
|
|
function removeFromArray(array, value) {
|
|
|
|
|
var idx = array.indexOf(value);
|
|
|
|
|
if (idx !== -1) {
|
|
|
|
|
array.splice(idx, 1);
|
|
|
|
|
}
|
|
|
|
|
return array;
|
2012-10-14 00:53:12 +00:00
|
|
|
}
|
2012-06-15 15:35:05 +00:00
|
|
|
|
2010-06-09 22:34:55 +00:00
|
|
|
/**
|
2010-10-15 02:16:24 +00:00
|
|
|
* Returns random number between 2 specified ones.
|
2010-06-09 22:34:55 +00:00
|
|
|
* @static
|
|
|
|
|
* @method getRandomInt
|
2010-10-15 02:16:24 +00:00
|
|
|
* @memberOf fabric.util
|
2010-06-09 22:34:55 +00:00
|
|
|
* @param {Number} min lower limit
|
|
|
|
|
* @param {Number} max upper limit
|
|
|
|
|
* @return {Number} random value (between min and max)
|
|
|
|
|
*/
|
|
|
|
|
function getRandomInt(min, max) {
|
|
|
|
|
return Math.floor(Math.random() * (max - min + 1)) + min;
|
|
|
|
|
}
|
2012-06-15 15:35:05 +00:00
|
|
|
|
2010-10-15 02:16:24 +00:00
|
|
|
var PiBy180 = Math.PI / 180;
|
2012-06-15 15:35:05 +00:00
|
|
|
|
2010-06-09 22:34:55 +00:00
|
|
|
/**
|
2010-10-15 02:16:24 +00:00
|
|
|
* Transforms degrees to radians.
|
2010-07-27 21:58:23 +00:00
|
|
|
* @static
|
|
|
|
|
* @method degreesToRadians
|
2010-10-15 02:16:24 +00:00
|
|
|
* @memberOf fabric.util
|
2010-06-09 22:34:55 +00:00
|
|
|
* @param {Number} degrees value in degrees
|
|
|
|
|
* @return {Number} value in radians
|
|
|
|
|
*/
|
|
|
|
|
function degreesToRadians(degrees) {
|
|
|
|
|
return degrees * PiBy180;
|
|
|
|
|
}
|
2012-06-15 15:35:05 +00:00
|
|
|
|
2012-11-15 13:20:45 +00:00
|
|
|
/**
|
|
|
|
|
* Transforms radians to degrees.
|
|
|
|
|
* @static
|
|
|
|
|
* @method radiansToDegrees
|
|
|
|
|
* @memberOf fabric.util
|
|
|
|
|
* @param {Number} radians value in radians
|
|
|
|
|
* @return {Number} value in degrees
|
|
|
|
|
*/
|
|
|
|
|
function radiansToDegrees(radians) {
|
|
|
|
|
return radians / PiBy180;
|
|
|
|
|
}
|
|
|
|
|
|
2012-12-06 18:12:54 +00:00
|
|
|
/**
|
|
|
|
|
* Rotates `point` around `origin` with `radians`
|
|
|
|
|
* @static
|
|
|
|
|
* @method rotatePoint
|
|
|
|
|
* @memberOf fabric.util
|
|
|
|
|
* @param {fabric.Point} The point to rotate
|
|
|
|
|
* @param {fabric.Point} The origin of the rotation
|
|
|
|
|
* @param {Number} The radians of the angle for the rotation
|
|
|
|
|
* @return {fabric.Point} The new rotated point
|
|
|
|
|
*/
|
|
|
|
|
function rotatePoint(point, origin, radians) {
|
|
|
|
|
var sin = Math.sin(radians),
|
|
|
|
|
cos = Math.cos(radians);
|
|
|
|
|
|
|
|
|
|
point.subtractEquals(origin);
|
|
|
|
|
|
|
|
|
|
var rx = point.x * cos - point.y * sin;
|
|
|
|
|
var ry = point.x * sin + point.y * cos;
|
|
|
|
|
|
|
|
|
|
return new fabric.Point(rx, ry).addEquals(origin);
|
|
|
|
|
}
|
|
|
|
|
|
2010-06-09 22:34:55 +00:00
|
|
|
/**
|
2010-10-15 02:16:24 +00:00
|
|
|
* A wrapper around Number#toFixed, which contrary to native method returns number, not string.
|
2010-07-27 21:58:23 +00:00
|
|
|
* @static
|
|
|
|
|
* @method toFixed
|
2010-10-15 02:16:24 +00:00
|
|
|
* @memberOf fabric.util
|
2010-06-09 22:34:55 +00:00
|
|
|
* @param {Number | String} number number to operate on
|
|
|
|
|
* @param {Number} fractionDigits number of fraction digits to "leave"
|
|
|
|
|
* @return {Number}
|
|
|
|
|
*/
|
|
|
|
|
function toFixed(number, fractionDigits) {
|
|
|
|
|
return parseFloat(Number(number).toFixed(fractionDigits));
|
|
|
|
|
}
|
2012-06-15 15:35:05 +00:00
|
|
|
|
2010-07-27 21:58:23 +00:00
|
|
|
/**
|
2010-10-15 02:16:24 +00:00
|
|
|
* Function which always returns `false`.
|
2010-07-27 21:58:23 +00:00
|
|
|
* @static
|
|
|
|
|
* @method falseFunction
|
2010-10-15 02:16:24 +00:00
|
|
|
* @memberOf fabric.util
|
2010-07-27 21:58:23 +00:00
|
|
|
* @return {Boolean}
|
|
|
|
|
*/
|
|
|
|
|
function falseFunction() {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2012-06-15 15:35:05 +00:00
|
|
|
|
2011-07-06 18:38:56 +00:00
|
|
|
/**
|
|
|
|
|
* Changes value from one to another within certain period of time, invoking callbacks as value is being changed.
|
|
|
|
|
* @method animate
|
|
|
|
|
* @memberOf fabric.util
|
|
|
|
|
* @param {Object} [options] Animation options
|
|
|
|
|
* @param {Function} [options.onChange] Callback; invoked on every value change
|
|
|
|
|
* @param {Function} [options.onComplete] Callback; invoked when value change is completed
|
|
|
|
|
* @param {Number} [options.startValue=0] Starting value
|
|
|
|
|
* @param {Number} [options.endValue=100] Ending value
|
2012-04-24 05:28:45 +00:00
|
|
|
* @param {Number} [options.byValue=100] Value to modify the property by
|
2011-07-06 18:38:56 +00:00
|
|
|
* @param {Function} [options.easing] Easing function
|
|
|
|
|
* @param {Number} [options.duration=500] Duration of change
|
|
|
|
|
*/
|
2011-11-28 04:57:28 +00:00
|
|
|
function animate(options) {
|
2011-07-06 18:38:56 +00:00
|
|
|
|
2011-11-28 04:57:28 +00:00
|
|
|
options || (options = { });
|
2011-07-06 18:38:56 +00:00
|
|
|
|
2012-06-15 15:35:05 +00:00
|
|
|
var start = +new Date(),
|
2011-11-28 04:57:28 +00:00
|
|
|
duration = options.duration || 500,
|
2012-10-14 00:53:12 +00:00
|
|
|
finish = start + duration, time,
|
2011-11-28 04:57:28 +00:00
|
|
|
onChange = options.onChange || function() { },
|
|
|
|
|
abort = options.abort || function() { return false; },
|
2012-04-26 03:01:42 +00:00
|
|
|
easing = options.easing || function(t, b, c, d) {return -c * Math.cos(t/d * (Math.PI/2)) + c + b;},
|
2011-11-28 04:57:28 +00:00
|
|
|
startValue = 'startValue' in options ? options.startValue : 0,
|
2012-10-14 00:53:12 +00:00
|
|
|
endValue = 'endValue' in options ? options.endValue : 100,
|
2012-04-24 05:28:45 +00:00
|
|
|
byValue = options.byValue || endValue - startValue;
|
2011-07-06 18:38:56 +00:00
|
|
|
|
2011-11-28 04:57:28 +00:00
|
|
|
options.onStart && options.onStart();
|
2012-06-15 15:35:05 +00:00
|
|
|
|
2011-12-08 14:15:37 +00:00
|
|
|
(function tick() {
|
2011-11-28 04:57:28 +00:00
|
|
|
time = +new Date();
|
2012-10-14 00:53:12 +00:00
|
|
|
var currentTime = time > finish ? duration : (time - start);
|
2012-04-24 05:28:45 +00:00
|
|
|
onChange(easing(currentTime, startValue, byValue, duration));
|
2011-11-28 04:57:28 +00:00
|
|
|
if (time > finish || abort()) {
|
|
|
|
|
options.onComplete && options.onComplete();
|
2011-12-08 14:15:37 +00:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
requestAnimFrame(tick);
|
|
|
|
|
})();
|
2011-11-28 04:57:28 +00:00
|
|
|
}
|
2012-06-15 15:35:05 +00:00
|
|
|
|
|
|
|
|
var _requestAnimFrame = fabric.window.requestAnimationFrame ||
|
|
|
|
|
fabric.window.webkitRequestAnimationFrame ||
|
|
|
|
|
fabric.window.mozRequestAnimationFrame ||
|
|
|
|
|
fabric.window.oRequestAnimationFrame ||
|
|
|
|
|
fabric.window.msRequestAnimationFrame ||
|
2012-10-14 00:53:12 +00:00
|
|
|
function(callback) {
|
2012-06-15 15:35:05 +00:00
|
|
|
fabric.window.setTimeout(callback, 1000 / 60);
|
|
|
|
|
};
|
2011-12-08 14:15:37 +00:00
|
|
|
/**
|
2012-06-15 15:35:05 +00:00
|
|
|
* requestAnimationFrame polyfill based on http://paulirish.com/2011/requestanimationframe-for-smart-animating/
|
2011-12-08 14:15:37 +00:00
|
|
|
* @method requestAnimFrame
|
|
|
|
|
* @memberOf fabric.util
|
|
|
|
|
* @param {Function} callback Callback to invoke
|
|
|
|
|
* @param {DOMElement} element optional Element to associate with animation
|
|
|
|
|
*/
|
2012-06-15 15:35:05 +00:00
|
|
|
var requestAnimFrame = function() {
|
|
|
|
|
return _requestAnimFrame.apply(fabric.window, arguments);
|
|
|
|
|
};
|
2011-11-28 04:57:28 +00:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Loads image element from given url and passes it to a callback
|
|
|
|
|
* @method loadImage
|
|
|
|
|
* @memberOf fabric.util
|
|
|
|
|
* @param {String} url URL representing an image
|
|
|
|
|
* @param {Function} callback Callback; invoked with loaded image
|
|
|
|
|
* @param {Any} context optional Context to invoke callback in
|
|
|
|
|
*/
|
|
|
|
|
function loadImage(url, callback, context) {
|
|
|
|
|
if (url) {
|
|
|
|
|
var img = new Image();
|
|
|
|
|
/** @ignore */
|
2012-06-15 15:35:05 +00:00
|
|
|
img.onload = function () {
|
2011-11-28 04:57:28 +00:00
|
|
|
callback && callback.call(context, img);
|
|
|
|
|
img = img.onload = null;
|
|
|
|
|
};
|
|
|
|
|
img.src = url;
|
|
|
|
|
}
|
2012-01-22 04:07:16 +00:00
|
|
|
else {
|
|
|
|
|
callback && callback.call(context, url);
|
|
|
|
|
}
|
2011-11-28 04:57:28 +00:00
|
|
|
}
|
2012-06-23 17:28:53 +00:00
|
|
|
|
2012-12-18 10:46:51 +00:00
|
|
|
/**
|
|
|
|
|
* Creates corresponding fabric instances from their object representations
|
|
|
|
|
* @static
|
|
|
|
|
* @memberOf fabric.util
|
|
|
|
|
* @method enlivenObjects
|
|
|
|
|
* @param {Array} objects Objects to enliven
|
|
|
|
|
* @param {Function} callback Callback to invoke when all objects are created
|
|
|
|
|
*/
|
2012-04-08 13:15:31 +00:00
|
|
|
function enlivenObjects(objects, callback) {
|
2012-06-23 17:28:53 +00:00
|
|
|
|
2012-04-08 13:15:31 +00:00
|
|
|
function getKlass(type) {
|
|
|
|
|
return fabric[fabric.util.string.camelize(fabric.util.string.capitalize(type))];
|
|
|
|
|
}
|
2012-06-23 17:28:53 +00:00
|
|
|
|
2012-09-02 14:39:07 +00:00
|
|
|
function onLoaded() {
|
|
|
|
|
if (++numLoadedObjects === numTotalObjects) {
|
|
|
|
|
if (callback) {
|
|
|
|
|
callback(enlivenedObjects);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2012-06-23 17:28:53 +00:00
|
|
|
|
2012-09-02 14:39:07 +00:00
|
|
|
var enlivenedObjects = [ ],
|
|
|
|
|
numLoadedObjects = 0,
|
|
|
|
|
numTotalObjects = objects.length;
|
2012-06-23 17:28:53 +00:00
|
|
|
|
2012-04-08 13:15:31 +00:00
|
|
|
objects.forEach(function (o, index) {
|
|
|
|
|
if (!o.type) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
var klass = getKlass(o.type);
|
|
|
|
|
if (klass.async) {
|
|
|
|
|
klass.fromObject(o, function (o) {
|
|
|
|
|
enlivenedObjects[index] = o;
|
2012-09-02 14:39:07 +00:00
|
|
|
onLoaded();
|
2012-04-08 13:15:31 +00:00
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
enlivenedObjects[index] = klass.fromObject(o);
|
2012-09-02 14:39:07 +00:00
|
|
|
onLoaded();
|
2012-04-08 13:15:31 +00:00
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
2011-07-06 18:38:56 +00:00
|
|
|
|
2012-10-14 00:53:12 +00:00
|
|
|
/**
|
|
|
|
|
* Groups SVG elements (usually those retrieved from SVG document)
|
|
|
|
|
* @static
|
|
|
|
|
* @memberOf fabric.util
|
|
|
|
|
* @method groupSVGElements
|
2012-12-23 14:17:45 +00:00
|
|
|
* @param {Array} elements SVG elements to group
|
2012-12-13 14:36:43 +00:00
|
|
|
* @param {Object} [options] Options object
|
|
|
|
|
* @return {fabric.Object|fabric.PathGroup}
|
2012-10-14 00:53:12 +00:00
|
|
|
*/
|
2012-06-23 17:28:53 +00:00
|
|
|
function groupSVGElements(elements, options, path) {
|
2012-12-23 14:28:08 +00:00
|
|
|
var object;
|
|
|
|
|
|
2012-12-23 14:17:45 +00:00
|
|
|
if (elements.length > 1) {
|
|
|
|
|
var hasText = elements.some(function(el) { return el.type === 'text'; });
|
2012-12-23 14:28:08 +00:00
|
|
|
|
2012-12-23 14:17:45 +00:00
|
|
|
if (hasText) {
|
|
|
|
|
object = new fabric.Group([ ], options);
|
|
|
|
|
elements.reverse().forEach(function(obj) {
|
|
|
|
|
if (obj.cx) {
|
|
|
|
|
obj.left = obj.cx;
|
|
|
|
|
}
|
|
|
|
|
if (obj.cy) {
|
|
|
|
|
obj.top = obj.cy;
|
|
|
|
|
}
|
|
|
|
|
object.addWithUpdate(obj);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
object = new fabric.PathGroup(elements, options);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
object = elements[0];
|
|
|
|
|
}
|
2012-06-23 17:28:53 +00:00
|
|
|
|
|
|
|
|
if (typeof path !== 'undefined') {
|
|
|
|
|
object.setSourcePath(path);
|
|
|
|
|
}
|
|
|
|
|
return object;
|
|
|
|
|
}
|
|
|
|
|
|
2012-11-30 22:46:09 +00:00
|
|
|
/**
|
|
|
|
|
* Populates an object with properties of another object
|
|
|
|
|
* @static
|
|
|
|
|
* @memberOf fabric.util
|
|
|
|
|
* @method populateWithProperties
|
2012-12-23 14:17:45 +00:00
|
|
|
* @param {Object} source Source object
|
|
|
|
|
* @param {Object} destination Destination object
|
|
|
|
|
* @return {Array} properties Propertie names to include
|
2012-11-30 22:46:09 +00:00
|
|
|
*/
|
|
|
|
|
function populateWithProperties(source, destination, properties) {
|
|
|
|
|
if (properties && Object.prototype.toString.call(properties) === '[object Array]') {
|
|
|
|
|
for (var i = 0, len = properties.length; i < len; i++) {
|
|
|
|
|
destination[properties[i]] = source[properties[i]];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-08-04 14:59:32 +00:00
|
|
|
fabric.util.removeFromArray = removeFromArray;
|
|
|
|
|
fabric.util.degreesToRadians = degreesToRadians;
|
2012-11-15 13:20:45 +00:00
|
|
|
fabric.util.radiansToDegrees = radiansToDegrees;
|
2012-12-06 18:12:54 +00:00
|
|
|
fabric.util.rotatePoint = rotatePoint;
|
2011-08-04 14:59:32 +00:00
|
|
|
fabric.util.toFixed = toFixed;
|
|
|
|
|
fabric.util.getRandomInt = getRandomInt;
|
|
|
|
|
fabric.util.falseFunction = falseFunction;
|
|
|
|
|
fabric.util.animate = animate;
|
2011-12-08 14:15:37 +00:00
|
|
|
fabric.util.requestAnimFrame = requestAnimFrame;
|
2011-11-28 04:57:28 +00:00
|
|
|
fabric.util.loadImage = loadImage;
|
2012-04-08 13:15:31 +00:00
|
|
|
fabric.util.enlivenObjects = enlivenObjects;
|
2012-06-23 17:28:53 +00:00
|
|
|
fabric.util.groupSVGElements = groupSVGElements;
|
2012-11-30 22:46:09 +00:00
|
|
|
fabric.util.populateWithProperties = populateWithProperties;
|
2011-11-28 04:57:28 +00:00
|
|
|
})();
|