9 生活离不开规则(课件)-2025-2026学年统编版(五四制)道德与法治三年级下册
|
18页
|
78人阅读
|
1人下载
普通
内容正文:
第1课时 生活处处有规则
生活离不开规则
部编版小学道德与法治三年级下册第9课
同学们好,我是月月!
游戏盲盒
规则
“规则”,就是规定出来供大家共同遵守的制度或章程。
1.游戏双方必须同时出手。
2.一旦出手,不能改变手势。
3.按照提前约定的局数定胜负。
游戏规则:
小讨论
你们经常玩哪些游戏?说说其中要遵守的规则。
游戏名称 游戏规则
离开“规则”的游戏会不会更好玩?你遇到过不遵守规则的情况或是不遵守游戏规则的小伙伴吗?当时你的感受怎样?
“抢板凳”
“跳绳”
还有哪里有规则?
规则大调查
我们小组调查的是 我们找到的规则:
衣( )
食( )
住( )
行( )
其他( ) 1.
2.
3.
4.
5.
小组活动:
1.小组内交流课前寻找到的公共生活规则;
2.小组内选取一位代表上台分享;
3.音乐停,交流停。
我不想遵守这些规则,好想去没有规则的世界!
如果驾驶汽车时没有规则,_______________。
如果乘坐交通工具时没有规则,_______________。
如果上下楼梯时没有规则,___________。
请同学们以小组为单位,选择一个场景,体会一下“自由”的生活是什么样的
“规则小能手”学习单
序号 规则 作用
1 搭乘电梯要先出后进 防止造成混乱,想出的人出不来,想进的人进不去。
2
3
4
5
......
生活需要规则,请你举例说一说规则对生活的影响。
某市一所小学的学生玲玲,下楼去操场做广播体操,玲玲按照老师的要求,靠右下楼梯,但此时她被一名靠左上楼的同学撞倒,玲玲不禁感叹:“为什么守规则反而倒霉”。
你有没有过类似的经历,有没有产生过这样的想法?
为什么会发生这样的事情?
规则看似在约束我们,实则在保护我们
规则让我们的生活有序、安全又美好。
我知道生活需要规则,有时候不是我想破坏规则,而是没有办法......
过马路时,一辆车都没有,我选择……
下面的情况,该如何遵守规则?
很多人都在插队购票,我在后面等了很久,要不我也……
花儿真漂亮,前面的人摘了一朵没人管,我也想……
过马路时,一辆车都没有,我选择……
课后请大家观察生活中出现的破坏规则的事,采访了解一下他们是出于什么原因破坏规则,想一想有没有解决办法。
课后作业
interact_pk_1.0.0.1040/assets/audio/btnClick.mp3
null
261.2245
interact_pk_1.0.0.1040/assets/audio/excellent.mp3
1044.8976
interact_pk_1.0.0.1040/assets/audio/gameBgm.mp3
null
21524.873
interact_pk_1.0.0.1040/assets/audio/gameEnd.mp3
null
2014-09-24
5198.3794
interact_pk_1.0.0.1040/assets/audio/good.mp3
679.1835
interact_pk_1.0.0.1040/assets/audio/great.mp3
679.1835
interact_pk_1.0.0.1040/assets/audio/prepare.mp3
2020
Blues
4388.58
interact_pk_1.0.0.1040/assets/audio/right.mp3
809.79565
interact_pk_1.0.0.1040/assets/audio/unbelievable.mp3
1410.6117
interact_pk_1.0.0.1040/assets/audio/wrong.mp3
»éÀñÒôЧ
»éÀñÒôÀÖ´óÈ«
www.igooood.com
Other
835.9184
chi -
www.igooood.com
��� -
www.igooood.com
interact_pk_1.0.0.1040/assets/img/1.png
interact_pk_1.0.0.1040/assets/img/2.png
interact_pk_1.0.0.1040/assets/img/3.png
interact_pk_1.0.0.1040/assets/img/answerBtn.png
interact_pk_1.0.0.1040/assets/img/answerReport_bg.png
interact_pk_1.0.0.1040/assets/img/centerBg.png
interact_pk_1.0.0.1040/assets/img/circleBtn.png
interact_pk_1.0.0.1040/assets/img/continueBtn.png
interact_pk_1.0.0.1040/assets/img/element/element1_1.png
interact_pk_1.0.0.1040/assets/img/element/element1_2.png
interact_pk_1.0.0.1040/assets/img/element/element2_1.png
interact_pk_1.0.0.1040/assets/img/element/element2_2.png
interact_pk_1.0.0.1040/assets/img/element/element3_1.png
interact_pk_1.0.0.1040/assets/img/element/element3_2.png
interact_pk_1.0.0.1040/assets/img/element/element4_1.png
interact_pk_1.0.0.1040/assets/img/element/element4_2.png
interact_pk_1.0.0.1040/assets/img/errorShadow.png
interact_pk_1.0.0.1040/assets/img/errorShadow_bottom.png
interact_pk_1.0.0.1040/assets/img/failRightIcon.png
interact_pk_1.0.0.1040/assets/img/finishPage_left.png
interact_pk_1.0.0.1040/assets/img/finishPage_right.png
interact_pk_1.0.0.1040/assets/img/gameLevelStar.png
interact_pk_1.0.0.1040/assets/img/goodFont/0.png
interact_pk_1.0.0.1040/assets/img/goodFont/1.png
interact_pk_1.0.0.1040/assets/img/goodFont/2.png
interact_pk_1.0.0.1040/assets/img/goodFont/3.png
interact_pk_1.0.0.1040/assets/img/goodFont/4.png
interact_pk_1.0.0.1040/assets/img/goodFont/5.png
interact_pk_1.0.0.1040/assets/img/goodFont/6.png
interact_pk_1.0.0.1040/assets/img/goodFont/7.png
interact_pk_1.0.0.1040/assets/img/goodFont/8.png
interact_pk_1.0.0.1040/assets/img/goodFont/9.png
interact_pk_1.0.0.1040/assets/img/goodFont/good10.png
interact_pk_1.0.0.1040/assets/img/goodFont/good11.png
interact_pk_1.0.0.1040/assets/img/goodFont/good12.png
interact_pk_1.0.0.1040/assets/img/goodFont/good13.png
interact_pk_1.0.0.1040/assets/img/goodFont/good14.png
interact_pk_1.0.0.1040/assets/img/goodFont/good15.png
interact_pk_1.0.0.1040/assets/img/goodFont/good16.png
interact_pk_1.0.0.1040/assets/img/goodFont/good17.png
interact_pk_1.0.0.1040/assets/img/goodFont/good18.png
interact_pk_1.0.0.1040/assets/img/goodFont/good19.png
interact_pk_1.0.0.1040/assets/img/goodFont/good20.png
interact_pk_1.0.0.1040/assets/img/goodFont/good3.png
interact_pk_1.0.0.1040/assets/img/goodFont/good4.png
interact_pk_1.0.0.1040/assets/img/goodFont/good5.png
interact_pk_1.0.0.1040/assets/img/goodFont/good6.png
interact_pk_1.0.0.1040/assets/img/goodFont/good7.png
interact_pk_1.0.0.1040/assets/img/goodFont/good8.png
interact_pk_1.0.0.1040/assets/img/goodFont/good9.png
interact_pk_1.0.0.1040/assets/img/goodFont/level1.png
interact_pk_1.0.0.1040/assets/img/goodFont/level2.png
interact_pk_1.0.0.1040/assets/img/goodFont/level3.png
interact_pk_1.0.0.1040/assets/img/goodFont/level4.png
interact_pk_1.0.0.1040/assets/img/header-btn-bg-bottom.png
interact_pk_1.0.0.1040/assets/img/header-btn-bg-corner.png
interact_pk_1.0.0.1040/assets/img/header-btn-bg-line.png
interact_pk_1.0.0.1040/assets/img/icon_upload_btn.png
interact_pk_1.0.0.1040/assets/img/img_grass.png
interact_pk_1.0.0.1040/assets/img/img_light.png
interact_pk_1.0.0.1040/assets/img/leftGroup.png
interact_pk_1.0.0.1040/assets/img/letsgo.png
interact_pk_1.0.0.1040/assets/img/pauseBtn.png
interact_pk_1.0.0.1040/assets/img/pausePage.png
interact_pk_1.0.0.1040/assets/img/picture/picture1_1.png
interact_pk_1.0.0.1040/assets/img/picture/picture1_2.png
interact_pk_1.0.0.1040/assets/img/picture/picture2_1.png
interact_pk_1.0.0.1040/assets/img/picture/picture2_2.png
interact_pk_1.0.0.1040/assets/img/picture/picture3_1.png
interact_pk_1.0.0.1040/assets/img/picture/picture3_2.png
interact_pk_1.0.0.1040/assets/img/picture/picture4_1.png
interact_pk_1.0.0.1040/assets/img/picture/picture4_2.png
interact_pk_1.0.0.1040/assets/img/picture/??ɫ-????.png
interact_pk_1.0.0.1040/assets/img/picture/??ɫ-????.png
interact_pk_1.0.0.1040/assets/img/resetBtn.png
interact_pk_1.0.0.1040/assets/img/resetBtn2.png
interact_pk_1.0.0.1040/assets/img/rightGroup.png
interact_pk_1.0.0.1040/assets/img/rightIcon.png
interact_pk_1.0.0.1040/assets/img/sameScorePage.png
interact_pk_1.0.0.1040/assets/img/select_set.png
interact_pk_1.0.0.1040/assets/img/showAnswerBtn.png
interact_pk_1.0.0.1040/assets/img/shrink.png
interact_pk_1.0.0.1040/assets/img/sprite/blow.gif
interact_pk_1.0.0.1040/assets/img/sprite/blow.png
interact_pk_1.0.0.1040/assets/img/sprite/blow1.png
interact_pk_1.0.0.1040/assets/img/sprite/blows.gif
interact_pk_1.0.0.1040/assets/img/star_gray.png
interact_pk_1.0.0.1040/assets/img/theme/theme1.png
interact_pk_1.0.0.1040/assets/img/theme/theme2.png
interact_pk_1.0.0.1040/assets/img/theme/theme3.png
interact_pk_1.0.0.1040/assets/img/theme/theme4.png
interact_pk_1.0.0.1040/assets/img/theme/theme_1.png
interact_pk_1.0.0.1040/assets/img/theme/theme_2.png
interact_pk_1.0.0.1040/assets/img/theme/theme_3.png
interact_pk_1.0.0.1040/assets/img/theme/theme_4.png
interact_pk_1.0.0.1040/assets/img/theme/thumb_1.png
interact_pk_1.0.0.1040/assets/img/theme/thumb_2.png
interact_pk_1.0.0.1040/assets/img/theme/thumb_3.png
interact_pk_1.0.0.1040/assets/img/theme/thumb_4.png
interact_pk_1.0.0.1040/assets/img/wrongIcon.png
interact_pk_1.0.0.1040/libs/customiseControls.js
/*
* fabric.js Controls Extension
* for fabric.js current build
* Simon Kunz 09.02.2016 for pixolith
* Licensed under the MIT license.
*/
'use strict';
(function(window) {
var fabric = window.fabric || ( window.fabric = {} ),
minExtCompat = '1.6.0',
isVML = function() {
return typeof G_vmlCanvasManager !== 'undefined';
},
degreesToRadians = fabric.util.degreesToRadians,
cursorOffset = {
mt: 0, // n
tr: 1, // ne
mr: 2, // e
br: 3, // se
mb: 4, // s
bl: 5, // sw
ml: 6, // w
tl: 7, // nw
};
if (minExtCompat.localeCompare(window.fabric.version) > -1) {
console.warn('this extension might not be fully compatible with your version ' +
'of fabric.js (' + window.fabric.version + ').' +
'Consider using the latest compatible build of fabric.js (> ' + minExtCompat + ')'
);
}
fabric.util.object.extend(fabric.Object.prototype, {
/**
* When true, image icons are loaded via the drawImage method
* @type Boolean
* @default false
*/
useCustomIcons: false,
/**
* Sets a background-color for drawImage operations with transparency
* @type string
* @default transparent
*/
cornerBackgroundColor: 'transparent',
/**
* Sets the shape of the background for drawImage operations with transparency
* @type string
* @default rect
*/
cornerShape: '',
/**
* Inner Padding between Shape Background and drawn Image
* @type int
* @default rect
*/
cornerPadding: 0,
/**
* Set a custom corner icon
* @param {Object} obj settings and icon url.
* @param callback function
*/
customiseCornerIcons: function(obj, callback) {
var setting,
cornerConfig;
for (setting in obj) {
if (obj.hasOwnProperty(setting)) {
cornerConfig = {};
if (obj[setting].cornerShape !== undefined) {
this.cornerShape = obj[setting].cornerShape;
}
if (obj[setting].cornerBackgroundColor !== undefined) {
this.cornerBackgroundColor = obj[setting].cornerBackgroundColor;
}
if (obj[setting].borderColor !== undefined) {
this.borderColor = obj[setting].borderColor;
}
if (obj[setting].cornerSize !== undefined) {
this.cornerSize = obj[setting].cornerSize;
}
if (obj[setting].cornerPadding !== undefined) {
this.cornerPadding = obj[setting].cornerPadding;
}
if (obj[setting].icon !== undefined || Object.keys(obj)[0] === 'settings') {
this.useCustomIcons = true;
if (obj[setting].settings !== undefined) {
cornerConfig.settings = obj[setting].settings;
}
if (obj[setting].icon !== undefined) {
cornerConfig.icon = obj[setting].icon;
this.loadIcon(setting, cornerConfig, function() {
if (callback && typeof( callback ) === 'function') {
callback();
}
});
}
}
}
}
},
/**
* loads the icon image as an image src.
* @param {Object} corner to load an icon.
* @param cornerConfig as object containing icon url and corner specific settings
* @param callback function.
*/
loadIcon: function(corner, cornerConfig, callback) {
var self = this,
icon = new Image();
icon.onload = function() {
self[corner + 'Icon'] = this;
if (cornerConfig.settings) {
self[corner + 'Settings'] = cornerConfig.settings;
}
if (callback && typeof( callback ) === 'function') {
callback();
}
};
icon.onerror = function() {
fabric.warn(this.src + ' icon is not an image');
};
if (cornerConfig.icon.match(/^http[s]?:\/\//) || cornerConfig.icon.substring(0, 2) === '//') {
icon.crossOrigin = 'Anonymous';
}
icon.src = cornerConfig.icon;
},
/**
* copy of the setter method for our american friends.
* @param {Object} obj containing corner icon urls and settings.
*/
customizeCornerIcons: function(obj) {
this.customiseCornerIcons(obj);
},
/**
* Draws corners of an object's bounding box.
* Requires public properties: width, height
* Requires public options: cornerSize, padding
* @param {CanvasRenderingContext2D} ctx Context to draw on
* @return {fabric.Object} thisArg
* @chainable
*/
drawControls: function(ctx) {
if (!this.hasControls) {
return this;
}
var wh = this._calculateCurrentDimensions(),
width = wh.x,
height = wh.y,
scaleOffset = this.cornerSize,
left = -(width + scaleOffset) / 2,
top = -(height + scaleOffset) / 2,
methodName;
if (!this.useCustomIcons) {
ctx.lineWidth = 1;
ctx.globalAlpha = this.isMoving ? this.borderOpacityWhenMoving : 1;
ctx.strokeStyle = ctx.fillStyle = this.cornerColor;
if (!this.transparentCorners) {
ctx.strokeStyle = this.cornerStrokeColor;
}
methodName = this.transparentCorners ? 'stroke' : 'fill';
} else {
methodName = 'drawImage';
}
ctx.save();
this._setLineDash(ctx, this.cornerDashArray, null);
// top-left
this._drawControl('tl', ctx, methodName,
left,
top,
this.tlIcon,
this.tlSettings
);
// top-right
this._drawControl('tr', ctx, methodName,
left + width,
top,
this.trIcon,
this.trSettings
);
// bottom-left
this._drawControl('bl', ctx, methodName,
left,
top + height,
this.blIcon,
this.blSettings
);
// bottom-right
this._drawControl('br', ctx, methodName,
left + width,
top + height,
this.brIcon,
this.brSettings
);
if (!this.get('lockUniScaling')) {
// middle-top
this._drawControl('mt', ctx, methodName,
left + width / 2,
top,
this.mtIcon,
this.mtSettings
);
// middle-bottom
this._drawControl('mb', ctx, methodName,
left + width / 2,
top + height,
this.mbIcon,
this.mbSettings
);
// middle-right
this._drawControl('mr', ctx, methodName,
left + width,
top + height / 2,
this.mrIcon,
this.mrSettings
);
// middle-left
this._drawControl('ml', ctx, methodName,
left,
top + height / 2,
this.mlIcon,
this.mlSettings
);
}
// middle-top-rotate
if (this.hasRotatingPoint) {
this._drawControl('mtr', ctx, methodName,
left + width / 2,
top - this.rotatingPointOffset,
this.mtrIcon,
this.mtrSettings
);
}
ctx.restore();
return this;
},
/** Draw controls either with background-shape and color (transparency) or plain image (modified core method)
* @private
* {string} icon url of the control
*/
_drawControl: function(control, ctx, methodName, left, top, icon, settings) {
if (!this.isControlVisible(control)) {
return;
}
var size = this.cornerSize,
cornerStroke = this.cornerStrokeColor || 'transparent',
cornerBG = this.cornerBackgroundColor || 'black',
cornerShape = this.cornerShape || 'rect',
cornerPadding = typeof this.cornerPadding === 'number' ? this.cornerPadding : 10;
if (settings) {
if (settings.cornerSize) {
// Set the size, and also recalc left and top
left = left + size / 2 - settings.cornerSize / 2;
top = top + size / 2 - settings.cornerSize / 2;
size = settings.cornerSize;
}
cornerShape = settings.cornerShape || cornerShape;
cornerBG = settings.cornerBackgroundColor || cornerBG;
cornerPadding = typeof settings.cornerPadding === 'number' ? settings.cornerPadding : cornerPadding;
cornerStroke = settings.cornerStrokeColor || cornerStroke;
}
if (this.useCustomIcons) {
if (cornerShape) {
ctx.globalAlpha = 1;
ctx.fillStyle = cornerBG;
ctx.lineWidth = 1;
ctx.strokeStyle = cornerStroke;
switch (cornerShape) {
case 'rect':
ctx.fillRect(left, top, size, size);
if (cornerStroke) {
ctx.strokeRect(left, top, size, size);
}
break;
case 'circle':
ctx.beginPath();
ctx.arc(left + size / 2, top + size / 2, size / 2, 0, 2 * Math.PI);
ctx.fill();
if (cornerStroke) {
ctx.stroke();
}
ctx.closePath();
break;
}
if (icon !== undefined) {
ctx[methodName](
icon,
left + cornerPadding / 2,
top + cornerPadding / 2,
size - cornerPadding,
size - cornerPadding
);
}
} else {
if (icon !== undefined) {
ctx[methodName](
icon,
left,
top,
size,
size
);
}
}
} else {
isVML() || this.transparentCorners || ctx.clearRect(left, top, size, size);
ctx[methodName + 'Rect'](left, top, size, size);
if (!this.transparentCorners && cornerStroke) {
ctx.strokeRect(left, top, size, size);
}
}
},
});
fabric.util.object.extend(fabric.Canvas.prototype, {
/**
* When true, actions can be overwritten
* @type Boolean
* @default false
*/
overwriteActions: false,
/**
* When true, cursors are fixed
* @type Boolean
* @default false
*/
fixedCursors: false,
/**
* setter Method for actions and cursors.
* @param {Object} obj containing corner action and cursor url/type.
*/
customiseControls: function(obj) {
var setting;
for (setting in obj) {
if (obj.hasOwnProperty(setting)) {
if (obj[setting].action !== undefined) {
this.overwriteActions = true;
this.setCustomAction(setting, obj[setting].action);
}
if (obj[setting].cursor !== undefined) {
this.fixedCursors = true;
this.setCustomCursor(setting, obj[setting].cursor);
}
}
}
},
/**
* loads the icon image as an image src.
* @param {Object} corner to load an icon.
* @param action as a string.
*/
setCustomAction: function(corner, action) {
this[corner + 'Action'] = action;
},
/**
* loads the icon image as an image src.
* @param {Object} corner to load an icon.
* @param cursorUrl as a string.
*/
setCustomCursor: function(corner, cursorUrl) {
this[corner + 'cursorIcon'] = cursorUrl;
},
/**
* copy of the setter method for our american friends.
* @param {Object} obj containing corner action and cursor url/type.
*/
customizeControls: function(obj) {
this.customiseControls(obj);
},
/**
* @private
*/
_getActionFromCorner: function(target, corner, e) {
if (!corner) {
return 'drag';
}
if (corner) {
if (this[corner + 'Action'] && this.overwriteActions) {
switch (corner) {
case 'mtr':
return this[corner + 'Action'] || 'rotate';
case 'ml':
case 'mr':
if (e[this.altActionKey]) {
return e[this.altActionKey] ? 'skewY' : 'scaleX';
}
return this[corner + 'Action'];
case 'mt':
case 'mb':
if (e[this.altActionKey]) {
return e[this.altActionKey] ? 'skewY' : 'scaleY';
}
return this[corner + 'Action'];
default:
return this[corner + 'Action'] || 'scale';
}
} else {
switch (corner) {
case 'mtr':
return 'rotate';
case 'ml':
case 'mr':
return e[this.altActionKey] ? 'skewY' : 'scaleX';
case 'mt':
case 'mb':
return e[this.altActionKey] ? 'skewX' : 'scaleY';
default:
return 'scale';
}
}
}
return false;
},
/**
* @private
* @param {Event} e Event object
* @param {fabric.Object} target
*/
_setupCurrentTransform: function(e, target) {
if (!target) {
return;
}
var pointer = this.getPointer(e),
corner = target._findTargetCorner(this.getPointer(e, true)),
action = this._getActionFromCorner(target, corner, e),
origin = this._getOriginFromCorner(target, corner);
if (typeof action === 'function') {
action.call(this, e, target);
// as of fabric 1.7.11 object cache will try to slice the action to check for scale so we need to convert this to a string
action = 'void';
}
this._currentTransform = {
target: target,
action: action,
corner: corner,
scaleX: target.scaleX,
scaleY: target.scaleY,
skewX: target.skewX,
skewY: target.skewY,
offsetX: pointer.x - target.left,
offsetY: pointer.y - target.top,
originX: origin.x,
originY: origin.y,
ex: pointer.x,
ey: pointer.y,
lastX: pointer.x,
lastY: pointer.y,
left: target.left,
top: target.top,
theta: degreesToRadians(target.angle),
width: target.width * target.scaleX,
mouseXSign: 1,
mouseYSign: 1,
shiftKey: e.shiftKey,
altKey: e[this.centeredKey],
};
this._currentTransform.original = {
left: target.left,
top: target.top,
scaleX: target.scaleX,
scaleY: target.scaleY,
skewX: target.skewX,
skewY: target.skewY,
originX: origin.x,
originY: origin.y,
};
if (action === 'remove') {
this._removeAction(e, target);
}
if (action === 'moveUp') {
this._moveLayerUpAction(e, target);
}
if (action === 'moveDown') {
this._moveLayerDownAction(e, target);
}
if (typeof action === 'object') {
if (Object.keys(action)[0] === 'rotateByDegrees') {
this._rotateByDegrees(e, target, action.rotateByDegrees);
}
}
this._resetCurrentTransform();
},
/**
* Custom remove object action
* @private
* @param {Event} e Event object
* @param {fabric.Object} target
*/
_removeAction: function(e, target) {
var _this = this;
if (this.getActiveGroup() && this.getActiveGroup() !== 'undefined') {
this.getActiveGroup().forEachObject(function(o) {
o.off();
o.remove();
});
this.discardActiveGroup();
// as of fabric 1.6.3 necessary for reasons..
setTimeout(function() {
_this.deactivateAll();
}, 0);
} else {
target.off();
target.remove();
setTimeout(function() {
_this.deactivateAll();
}, 0);
}
},
/**
* Custom move up object action
* @private
* @param {Event} e Event object
* @param {fabric.Object} target
*/
_moveLayerUpAction: function(e, target) {
if (this.getActiveGroup() && this.getActiveGroup() !== 'undefined') {
this.getActiveGroup().forEachObject(function(o) {
o.bringForward();
});
} else {
target.bringForward();
}
},
/**
* Custom move down object action
* @private
* @param {Event} e Event object
* @param {fabric.Object} target
*/
_moveLayerDownAction: function(e, target) {
if (this.getActiveGroup() && this.getActiveGroup() !== 'undefined') {
this.getActiveGroup().forEachObject(function(o) {
o.sendBackwards();
});
} else {
target.sendBackwards();
}
},
/**
* Custom move down object action
* @private
* @param {Event} e Event object
* @param {fabric.Object} target
* @param {Integer} value of rotation
*/
_rotateByDegrees: function(e, target, value) {
var angle = parseInt(target.getAngle()) + value,
needsOriginRestore = false;
if (( target.originX !== 'center' || target.originY !== 'center' ) && target.centeredRotation) {
this._setOriginToCenter(target);
needsOriginRestore = true;
}
angle = angle > 360 ? angle - 360 : angle;
if (this.getActiveGroup() && this.getActiveGroup() !== 'undefined') {
this.getActiveGroup().forEachObject(function(obj) {
obj
.setAngle(angle)
.setCoords();
});
} else {
target
.setAngle(angle)
.setCoords();
}
if (needsOriginRestore) {
this._setCenterToOrigin(target);
}
this.renderAll();
},
/**
* Sets either the standard behaviour cursors or if fixedCursors is true, tries to set a custom cursor
* either by using an icon or a build-in cursor. Cursor icon extensions are matched with a regular expression.
* @private
* {string} corner name
* {target} event handler of the hovered corner
*/
_setCornerCursor: function(corner, target, e) {
var iconUrlPattern = /\.(?:jpe?g|png|gif|jpg|jpeg|svg)$/;
if (this.fixedCursors && this[corner + 'cursorIcon']) {
if (this[corner + 'cursorIcon'].match(iconUrlPattern)) {
this.setCursor('url(' + this[corner + 'cursorIcon'] + '), auto');
} else {
if (this[corner + 'cursorIcon'] === 'resize') {
this.setCursor(this._getRotatedCornerCursor(corner, target, e));
} else {
this.setCursor(this[corner + 'cursorIcon']);
}
}
} else {
if (corner in cursorOffset) {
this.setCursor(this._getRotatedCornerCursor(corner, target, e));
} else if (corner === 'mtr' && target.hasRotatingPoint) {
this.setCursor(this.rotationCursor);
} else {
this.setCursor(this.defaultCursor);
return false;
}
}
return false;
},
});
if (typeof exports !== 'undefined') {
module.exports = this;
}
})(window);
interact_pk_1.0.0.1040/libs/customiseControls.js.gz
interact_pk_1.0.0.1040/libs/customiseControls.js
/*
* fabric.js Controls Extension
* for fabric.js current build
* Simon Kunz 09.02.2016 for pixolith
* Licensed under the MIT license.
*/
'use strict';
(function(window) {
var fabric = window.fabric || ( window.fabric = {} ),
minExtCompat = '1.6.0',
isVML = function() {
return typeof G_vmlCanvasManager !== 'undefined';
},
degreesToRadians = fabric.util.degreesToRadians,
cursorOffset = {
mt: 0, // n
tr: 1, // ne
mr: 2, // e
br: 3, // se
mb: 4, // s
bl: 5, // sw
ml: 6, // w
tl: 7, // nw
};
if (minExtCompat.localeCompare(window.fabric.version) > -1) {
console.warn('this extension might not be fully compatible with your version ' +
'of fabric.js (' + window.fabric.version + ').' +
'Consider using the latest compatible build of fabric.js (> ' + minExtCompat + ')'
);
}
fabric.util.object.extend(fabric.Object.prototype, {
/**
* When true, image icons are loaded via the drawImage method
* @type Boolean
* @default false
*/
useCustomIcons: false,
/**
* Sets a background-color for drawImage operations with transparency
* @type string
* @default transparent
*/
cornerBackgroundColor: 'transparent',
/**
* Sets the shape of the background for drawImage operations with transparency
* @type string
* @default rect
*/
cornerShape: '',
/**
* Inner Padding between Shape Background and drawn Image
* @type int
* @default rect
*/
cornerPadding: 0,
/**
* Set a custom corner icon
* @param {Object} obj settings and icon url.
* @param callback function
*/
customiseCornerIcons: function(obj, callback) {
var setting,
cornerConfig;
for (setting in obj) {
if (obj.hasOwnProperty(setting)) {
cornerConfig = {};
if (obj[setting].cornerShape !== undefined) {
this.cornerShape = obj[setting].cornerShape;
}
if (obj[setting].cornerBackgroundColor !== undefined) {
this.cornerBackgroundColor = obj[setting].cornerBackgroundColor;
}
if (obj[setting].borderColor !== undefined) {
this.borderColor = obj[setting].borderColor;
}
if (obj[setting].cornerSize !== undefined) {
this.cornerSize = obj[setting].cornerSize;
}
if (obj[setting].cornerPadding !== undefined) {
this.cornerPadding = obj[setting].cornerPadding;
}
if (obj[setting].icon !== undefined || Object.keys(obj)[0] === 'settings') {
this.useCustomIcons = true;
if (obj[setting].settings !== undefined) {
cornerConfig.settings = obj[setting].settings;
}
if (obj[setting].icon !== undefined) {
cornerConfig.icon = obj[setting].icon;
this.loadIcon(setting, cornerConfig, function() {
if (callback && typeof( callback ) === 'function') {
callback();
}
});
}
}
}
}
},
/**
* loads the icon image as an image src.
* @param {Object} corner to load an icon.
* @param cornerConfig as object containing icon url and corner specific settings
* @param callback function.
*/
loadIcon: function(corner, cornerConfig, callback) {
var self = this,
icon = new Image();
icon.onload = function() {
self[corner + 'Icon'] = this;
if (cornerConfig.settings) {
self[corner + 'Settings'] = cornerConfig.settings;
}
if (callback && typeof( callback ) === 'function') {
callback();
}
};
icon.onerror = function() {
fabric.warn(this.src + ' icon is not an image');
};
if (cornerConfig.icon.match(/^http[s]?:\/\//) || cornerConfig.icon.substring(0, 2) === '//') {
icon.crossOrigin = 'Anonymous';
}
icon.src = cornerConfig.icon;
},
/**
* copy of the setter method for our american friends.
* @param {Object} obj containing corner icon urls and settings.
*/
customizeCornerIcons: function(obj) {
this.customiseCornerIcons(obj);
},
/**
* Draws corners of an object's bounding box.
* Requires public properties: width, height
* Requires public options: cornerSize, padding
* @param {CanvasRenderingContext2D} ctx Context to draw on
* @return {fabric.Object} thisArg
* @chainable
*/
drawControls: function(ctx) {
if (!this.hasControls) {
return this;
}
var wh = this._calculateCurrentDimensions(),
width = wh.x,
height = wh.y,
scaleOffset = this.cornerSize,
left = -(width + scaleOffset) / 2,
top = -(height + scaleOffset) / 2,
methodName;
if (!this.useCustomIcons) {
ctx.lineWidth = 1;
ctx.globalAlpha = this.isMoving ? this.borderOpacityWhenMoving : 1;
ctx.strokeStyle = ctx.fillStyle = this.cornerColor;
if (!this.transparentCorners) {
ctx.strokeStyle = this.cornerStrokeColor;
}
methodName = this.transparentCorners ? 'stroke' : 'fill';
} else {
methodName = 'drawImage';
}
ctx.save();
this._setLineDash(ctx, this.cornerDashArray, null);
// top-left
this._drawControl('tl', ctx, methodName,
left,
top,
this.tlIcon,
this.tlSettings
);
// top-right
this._drawControl('tr', ctx, methodName,
left + width,
top,
this.trIcon,
this.trSettings
);
// bottom-left
this._drawControl('bl', ctx, methodName,
left,
top + height,
this.blIcon,
this.blSettings
);
// bottom-right
this._drawControl('br', ctx, methodName,
left + width,
top + height,
this.brIcon,
this.brSettings
);
if (!this.get('lockUniScaling')) {
// middle-top
this._drawControl('mt', ctx, methodName,
left + width / 2,
top,
this.mtIcon,
this.mtSettings
);
// middle-bottom
this._drawControl('mb', ctx, methodName,
left + width / 2,
top + height,
this.mbIcon,
this.mbSettings
);
// middle-right
this._drawControl('mr', ctx, methodName,
left + width,
top + height / 2,
this.mrIcon,
this.mrSettings
);
// middle-left
this._drawControl('ml', ctx, methodName,
left,
top + height / 2,
this.mlIcon,
this.mlSettings
);
}
// middle-top-rotate
if (this.hasRotatingPoint) {
this._drawControl('mtr', ctx, methodName,
left + width / 2,
top - this.rotatingPointOffset,
this.mtrIcon,
this.mtrSettings
);
}
ctx.restore();
return this;
},
/** Draw controls either with background-shape and color (transparency) or plain image (modified core method)
* @private
* {string} icon url of the control
*/
_drawControl: function(control, ctx, methodName, left, top, icon, settings) {
if (!this.isControlVisible(control)) {
return;
}
var size = this.cornerSize,
cornerStroke = this.cornerStrokeColor || 'transparent',
cornerBG = this.cornerBackgroundColor || 'black',
cornerShape = this.cornerShape || 'rect',
cornerPadding = typeof this.cornerPadding === 'number' ? this.cornerPadding : 10;
if (settings) {
if (settings.cornerSize) {
// Set the size, and also recalc left and top
left = left + size / 2 - settings.cornerSize / 2;
top = top + size / 2 - settings.cornerSize / 2;
size = settings.cornerSize;
}
cornerShape = settings.cornerShape || cornerShape;
cornerBG = settings.cornerBackgroundColor || cornerBG;
cornerPadding = typeof settings.cornerPadding === 'number' ? settings.cornerPadding : cornerPadding;
cornerStroke = settings.cornerStrokeColor || cornerStroke;
}
if (this.useCustomIcons) {
if (cornerShape) {
ctx.globalAlpha = 1;
ctx.fillStyle = cornerBG;
ctx.lineWidth = 1;
ctx.strokeStyle = cornerStroke;
switch (cornerShape) {
case 'rect':
ctx.fillRect(left, top, size, size);
if (cornerStroke) {
ctx.strokeRect(left, top, size, size);
}
break;
case 'circle':
ctx.beginPath();
ctx.arc(left + size / 2, top + size / 2, size / 2, 0, 2 * Math.PI);
ctx.fill();
if (cornerStroke) {
ctx.stroke();
}
ctx.closePath();
break;
}
if (icon !== undefined) {
ctx[methodName](
icon,
left + cornerPadding / 2,
top + cornerPadding / 2,
size - cornerPadding,
size - cornerPadding
);
}
} else {
if (icon !== undefined) {
ctx[methodName](
icon,
left,
top,
size,
size
);
}
}
} else {
isVML() || this.transparentCorners || ctx.clearRect(left, top, size, size);
ctx[methodName + 'Rect'](left, top, size, size);
if (!this.transparentCorners && cornerStroke) {
ctx.strokeRect(left, top, size, size);
}
}
},
});
fabric.util.object.extend(fabric.Canvas.prototype, {
/**
* When true, actions can be overwritten
* @type Boolean
* @default false
*/
overwriteActions: false,
/**
* When true, cursors are fixed
* @type Boolean
* @default false
*/
fixedCursors: false,
/**
* setter Method for actions and cursors.
* @param {Object} obj containing corner action and cursor url/type.
*/
customiseControls: function(obj) {
var setting;
for (setting in obj) {
if (obj.hasOwnProperty(setting)) {
if (obj[setting].action !== undefined) {
this.overwriteActions = true;
this.setCustomAction(setting, obj[setting].action);
}
if (obj[setting].cursor !== undefined) {
this.fixedCursors = true;
this.setCustomCursor(setting, obj[setting].cursor);
}
}
}
},
/**
* loads the icon image as an image src.
* @param {Object} corner to load an icon.
* @param action as a string.
*/
setCustomAction: function(corner, action) {
this[corner + 'Action'] = action;
},
/**
* loads the icon image as an image src.
* @param {Object} corner to load an icon.
* @param cursorUrl as a string.
*/
setCustomCursor: function(corner, cursorUrl) {
this[corner + 'cursorIcon'] = cursorUrl;
},
/**
* copy of the setter method for our american friends.
* @param {Object} obj containing corner action and cursor url/type.
*/
customizeControls: function(obj) {
this.customiseControls(obj);
},
/**
* @private
*/
_getActionFromCorner: function(target, corner, e) {
if (!corner) {
return 'drag';
}
if (corner) {
if (this[corner + 'Action'] && this.overwriteActions) {
switch (corner) {
case 'mtr':
return this[corner + 'Action'] || 'rotate';
case 'ml':
case 'mr':
if (e[this.altActionKey]) {
return e[this.altActionKey] ? 'skewY' : 'scaleX';
}
return this[corner + 'Action'];
case 'mt':
case 'mb':
if (e[this.altActionKey]) {
return e[this.altActionKey] ? 'skewY' : 'scaleY';
}
return this[corner + 'Action'];
default:
return this[corner + 'Action'] || 'scale';
}
} else {
switch (corner) {
case 'mtr':
return 'rotate';
case 'ml':
case 'mr':
return e[this.altActionKey] ? 'skewY' : 'scaleX';
case 'mt':
case 'mb':
return e[this.altActionKey] ? 'skewX' : 'scaleY';
default:
return 'scale';
}
}
}
return false;
},
/**
* @private
* @param {Event} e Event object
* @param {fabric.Object} target
*/
_setupCurrentTransform: function(e, target) {
if (!target) {
return;
}
var pointer = this.getPointer(e),
corner = target._findTargetCorner(this.getPointer(e, true)),
action = this._getActionFromCorner(target, corner, e),
origin = this._getOriginFromCorner(target, corner);
if (typeof action === 'function') {
action.call(this, e, target);
// as of fabric 1.7.11 object cache will try to slice the action to check for scale so we need to convert this to a string
action = 'void';
}
this._currentTransform = {
target: target,
action: action,
corner: corner,
scaleX: target.scaleX,
scaleY: target.scaleY,
skewX: target.skewX,
skewY: target.skewY,
offsetX: pointer.x - target.left,
offsetY: pointer.y - target.top,
originX: origin.x,
originY: origin.y,
ex: pointer.x,
ey: pointer.y,
lastX: pointer.x,
lastY: pointer.y,
left: target.left,
top: target.top,
theta: degreesToRadians(target.angle),
width: target.width * target.scaleX,
mouseXSign: 1,
mouseYSign: 1,
shiftKey: e.shiftKey,
altKey: e[this.centeredKey],
};
this._currentTransform.original = {
left: target.left,
top: target.top,
scaleX: target.scaleX,
scaleY: target.scaleY,
skewX: target.skewX,
skewY: target.skewY,
originX: origin.x,
originY: origin.y,
};
if (action === 'remove') {
this._removeAction(e, target);
}
if (action === 'moveUp') {
this._moveLayerUpAction(e, target);
}
if (action === 'moveDown') {
this._moveLayerDownAction(e, target);
}
if (typeof action === 'object') {
if (Object.keys(action)[0] === 'rotateByDegrees') {
this._rotateByDegrees(e, target, action.rotateByDegrees);
}
}
this._resetCurrentTransform();
},
/**
* Custom remove object action
* @private
* @param {Event} e Event object
* @param {fabric.Object} target
*/
_removeAction: function(e, target) {
var _this = this;
if (this.getActiveGroup() && this.getActiveGroup() !== 'undefined') {
this.getActiveGroup().forEachObject(function(o) {
o.off();
o.remove();
});
this.discardActiveGroup();
// as of fabric 1.6.3 necessary for reasons..
setTimeout(function() {
_this.deactivateAll();
}, 0);
} else {
target.off();
target.remove();
setTimeout(function() {
_this.deactivateAll();
}, 0);
}
},
/**
* Custom move up object action
* @private
* @param {Event} e Event object
* @param {fabric.Object} target
*/
_moveLayerUpAction: function(e, target) {
if (this.getActiveGroup() && this.getActiveGroup() !== 'undefined') {
this.getActiveGroup().forEachObject(function(o) {
o.bringForward();
});
} else {
target.bringForward();
}
},
/**
* Custom move down object action
* @private
* @param {Event} e Event object
* @param {fabric.Object} target
*/
_moveLayerDownAction: function(e, target) {
if (this.getActiveGroup() && this.getActiveGroup() !== 'undefined') {
this.getActiveGroup().forEachObject(function(o) {
o.sendBackwards();
});
} else {
target.sendBackwards();
}
},
/**
* Custom move down object action
* @private
* @param {Event} e Event object
* @param {fabric.Object} target
* @param {Integer} value of rotation
*/
_rotateByDegrees: function(e, target, value) {
var angle = parseInt(target.getAngle()) + value,
needsOriginRestore = false;
if (( target.originX !== 'center' || target.originY !== 'center' ) && target.centeredRotation) {
this._setOriginToCenter(target);
needsOriginRestore = true;
}
angle = angle > 360 ? angle - 360 : angle;
if (this.getActiveGroup() && this.getActiveGroup() !== 'undefined') {
this.getActiveGroup().forEachObject(function(obj) {
obj
.setAngle(angle)
.setCoords();
});
} else {
target
.setAngle(angle)
.setCoords();
}
if (needsOriginRestore) {
this._setCenterToOrigin(target);
}
this.renderAll();
},
/**
* Sets either the standard behaviour cursors or if fixedCursors is true, tries to set a custom cursor
* either by using an icon or a build-in cursor. Cursor icon extensions are matched with a regular expression.
* @private
* {string} corner name
* {target} event handler of the hovered corner
*/
_setCornerCursor: function(corner, target, e) {
var iconUrlPattern = /\.(?:jpe?g|png|gif|jpg|jpeg|svg)$/;
if (this.fixedCursors && this[corner + 'cursorIcon']) {
if (this[corner + 'cursorIcon'].match(iconUrlPattern)) {
this.setCursor('url(' + this[corner + 'cursorIcon'] + '), auto');
} else {
if (this[corner + 'cursorIcon'] === 'resize') {
this.setCursor(this._getRotatedCornerCursor(corner, target, e));
} else {
this.setCursor(this[corner + 'cursorIcon']);
}
}
} else {
if (corner in cursorOffset) {
this.setCursor(this._getRotatedCornerCursor(corner, target, e));
} else if (corner === 'mtr' && target.hasRotatingPoint) {
this.setCursor(this.rotationCursor);
} else {
this.setCursor(this.defaultCursor);
return false;
}
}
return false;
},
});
if (typeof exports !== 'undefined') {
module.exports = this;
}
})(window);
interact_pk_1.0.0.1040/libs/fabric.js
/* build: `node build.js modules=ALL exclude=gestures,accessors requirejs minifier=uglifyjs` */
/*! Fabric.js Copyright 2008-2015, Printio (Juriy Zaytsev, Maxim Chernyak) */
var fabric = fabric || {version: '3.6.1'};
if (typeof exports !== 'undefined') {
exports.fabric = fabric;
}
/* _AMD_START_ */
else if (typeof define === 'function' && define.amd) {
define([], function () { return fabric; });
}
/* _AMD_END_ */
if (typeof document !== 'undefined' && typeof window !== 'undefined') {
if (document instanceof (typeof HTMLDocument !== 'undefined' ? HTMLDocument : Document)) {
fabric.document = document;
}
else {
fabric.document = document.implementation.createHTMLDocument('');
}
fabric.window = window;
}
else {
// assume we're running under node.js when document/window are not present
var jsdom = require('jsdom');
var virtualWindow = new jsdom.JSDOM(
decodeURIComponent('%3C!DOCTYPE%20html%3E%3Chtml%3E%3Chead%3E%3C%2Fhead%3E%3Cbody%3E%3C%2Fbody%3E%3C%2Fhtml%3E'),
{
features : {
FetchExternalResources: ['img']
},
resources: 'usable'
}).window;
fabric.document = virtualWindow.document;
fabric.jsdomImplForWrapper = require('jsdom/lib/jsdom/living/generated/utils').implForWrapper;
fabric.nodeCanvas = require('jsdom/lib/jsdom/utils').Canvas;
fabric.window = virtualWindow;
DOMParser = fabric.window.DOMParser;
}
/**
* True when in environment that supports touch events
* @type boolean
*/
fabric.isTouchSupported = 'ontouchstart' in fabric.window || 'ontouchstart' in fabric.document ||
(fabric.window && fabric.window.navigator && fabric.window.navigator.maxTouchPoints > 0);
/**
* True when in environment that's probably Node.js
* @type boolean
*/
fabric.isLikelyNode = typeof Buffer !== 'undefined' &&
typeof window === 'undefined';
/* _FROM_SVG_START_ */
/**
* Attributes parsed from all SVG elements
* @type array
*/
fabric.SHARED_ATTRIBUTES = [
'display',
'transform',
'fill', 'fill-opacity', 'fill-rule',
'opacity',
'stroke', 'stroke-dasharray', 'stroke-linecap', 'stroke-dashoffset',
'stroke-linejoin', 'stroke-miterlimit',
'stroke-opacity', 'stroke-width',
'id', 'paint-order', 'vector-effect',
'instantiated_by_use', 'clip-path'
];
/* _FROM_SVG_END_ */
/**
* Pixel per Inch as a default value set to 96. Can be changed for more realistic conversion.
*/
fabric.DPI = 96;
fabric.reNum = '(?:[-+]?(?:\\d+|\\d*\\.\\d+)(?:[eE][-+]?\\d+)?)';
fabric.rePathCommand = /([-+]?((\d+\.\d+)|((\d+)|(\.\d+)))(?:[eE][-+]?\d+)?)/ig;
fabric.reNonWord = /[
\.,;!\?\-]/;
fabric.fontPaths = {};
fabric.iMatrix = [1, 0, 0, 1, 0, 0];
fabric.svgNS = 'http://www.w3.org/2000/svg';
/**
* Pixel limit for cache canvases. 1Mpx , 4Mpx should be fine.
* @since 1.7.14
* @type Number
* @default
*/
fabric.perfLimitSizeTotal = 2097152;
/**
* Pixel limit for cache canvases width or height. IE fixes the maximum at 5000
* @since 1.7.14
* @type Number
* @default
*/
fabric.maxCacheSideLimit = 4096;
/**
* Lowest pixel limit for cache canvases, set at 256PX
* @since 1.7.14
* @type Number
* @default
*/
fabric.minCacheSideLimit = 256;
/**
* Cache Object for widths of chars in text rendering.
*/
fabric.charWidthsCache = {};
/**
* if webgl is enabled and available, textureSize will determine the size
* of the canvas backend
* @since 2.0.0
* @type Number
* @default
*/
fabric.textureSize = 2048;
/**
* When 'true', style information is not retained when copy/pasting text, making
* pasted text use destination style.
* Defaults to 'false'.
* @type Boolean
* @default
*/
fabric.disableStyleCopyPaste = false;
/**
* Enable webgl for filtering picture is available
* A filtering backend will be initialized, this will both take memory and
* time since a default 2048x2048 canvas will be created for the gl context
* @since 2.0.0
* @type Boolean
* @default
*/
fabric.enableGLFiltering = true;
/**
* Device Pixel Ratio
* @see https://developer.apple.com/library/safari/documentation/AudioVideo/Conceptual/HTML-canvas-guide/SettingUptheCanvas/SettingUptheCanvas.html
*/
fabric.devicePixelRatio = fabric.window.devicePixelRatio ||
fabric.window.webkitDevicePixelRatio ||
fabric.window.mozDevicePixelRatio ||
1;
/**
* Browser-specific constant to adjust CanvasRenderingContext2D.shadowBlur value,
* which is unitless and not rendered equally across browsers.
*
* Values that work quite well (as of October 2017) are:
* - Chrome: 1.5
* - Edge: 1.75
* - Firefox: 0.9
* - Safari: 0.95
*
* @since 2.0.0
* @type Number
* @default 1
*/
fabric.browserShadowBlurConstant = 1;
/**
* This object contains the result of arc to beizer conversion for faster retrieving if the same arc needs to be converted again.
* It was an internal variable, is accessible since version 2.3.4
*/
fabric.arcToSegmentsCache = {};
/**
* This object keeps the results of the boundsOfCurve calculation mapped by the joined arguments necessary to calculate it.
* It does speed up calculation, if you parse and add always the same paths, but in case of heavy usage of freedrawing
* you do not get any speed benefit and you get a big object in memory.
* The object was a private variable before, while now is appended to the lib so that you have access to it and you
* can eventually clear it.
* It was an internal variable, is accessible since version 2.3.4
*/
fabric.boundsOfCurveCache = {};
/**
* If disabled boundsOfCurveCache is not used. For apps that make heavy usage of pencil drawing probably disabling it is better
* @default true
*/
fabric.cachesBoundsOfCurve = true;
/**
* Skip performance testing of setupGLContext and force the use of putImageData that seems to be the one that works best on
* Chrome + old hardware. if your users are experiencing empty images after filtering you may try to force this to true
* this has to be set before instantiating the filtering backend ( before filtering the first image )
* @type Boolean
* @default false
*/
fabric.forceGLPutImageData = false;
fabric.initFilterBackend = function () {
if (fabric.enableGLFiltering && fabric.isWebglSupported && fabric.isWebglSupported(fabric.textureSize)) {
console.log('max texture size: ' + fabric.maxTextureSize);
return (new fabric.WebglFilterBackend({tileSize: fabric.textureSize}));
}
else if (fabric.Canvas2dFilterBackend) {
return (new fabric.Canvas2dFilterBackend());
}
};
if (typeof document !== 'undefined' && typeof window !== 'undefined') {
// ensure globality even if entire library were function wrapped (as in Meteor.js packaging system)
window.fabric = fabric;
}
(function () {
/**
* @private
* @param {String} eventName
* @param {Function} handler
*/
function _removeEventListener (eventName, handler) {
if (!this.__eventListeners[eventName]) {
return;
}
var eventListener = this.__eventListeners[eventName];
if (handler) {
eventListener[eventListener.indexOf(handler)] = false;
}
else {
fabric.util.array.fill(eventListener, false);
}
}
/**
* Observes specified event
* @deprecated `observe` deprecated since 0.8.34 (use `on` instead)
* @memberOf fabric.Observable
* @alias on
* @param {String|Object} eventName Event name (eg. 'after:render') or object with key/value pairs (eg. {'after:render': handler, 'selection:cleared': handler})
* @param {Function} handler Function that receives a notification when an event of the specified type occurs
* @return {Self} thisArg
* @chainable
*/
function observe (eventName, handler) {
if (!this.__eventListeners) {
this.__eventListeners = {};
}
// one object with key/value pairs was passed
if (arguments.length === 1) {
for (var prop in eventName) {
this.on(prop, eventName[prop]);
}
}
else {
if (!this.__eventListeners[eventName]) {
this.__eventListeners[eventName] = [];
}
this.__eventListeners[eventName].push(handler);
}
return this;
}
/**
* Stops event observing for a particular event handler. Calling this method
* without arguments removes all handlers for all events
* @deprecated `stopObserving` deprecated since 0.8.34 (use `off` instead)
* @memberOf fabric.Observable
* @alias off
* @param {String|Object} eventName Event name (eg. 'after:render') or object with key/value pairs (eg. {'after:render': handler, 'selection:cleared': handler})
* @param {Function} handler Function to be deleted from EventListeners
* @return {Self} thisArg
* @chainable
*/
function stopObserving (eventName, handler) {
if (!this.__eventListeners) {
return this;
}
// remove all key/value pairs (event name -> event handler)
if (arguments.length === 0) {
for (eventName in this.__eventListeners) {
_removeEventListener.call(this, eventName);
}
}
// one object with key/value pairs was passed
else if (arguments.length === 1 && typeof arguments[0] === 'object') {
for (var prop in eventName) {
_removeEventListener.call(this, prop, eventName[prop]);
}
}
else {
_removeEventListener.call(this, eventName, handler);
}
return this;
}
/**
* Fires event with an optional options object
* @deprecated `fire` deprecated since 1.0.7 (use `trigger` instead)
* @memberOf fabric.Observable
* @alias trigger
* @param {String} eventName Event name to fire
* @param {Object} [options] Options object
* @return {Self} thisArg
* @chainable
*/
function fire (eventName, options) {
if (!this.__eventListeners) {
return this;
}
var listenersForEvent = this.__eventListeners[eventName];
if (!listenersForEvent) {
return this;
}
for (var i = 0, len = listenersForEvent.length; i < len; i++) {
listenersForEvent[i] && listenersForEvent[i].call(this, options || {});
}
this.__eventListeners[eventName] = listenersForEvent.filter(function (value) {
return value !== false;
});
return this;
}
/**
* @namespace fabric.Observable
* @tutorial {@link http://fabricjs.com/fabric-intro-part-2#events}
* @see {@link http://fabricjs.com/events|Events demo}
*/
fabric.Observable = {
observe : observe,
stopObserving: stopObserving,
fire : fire,
on : observe,
off : stopObserving,
trigger: fire
};
})();
/**
* @namespace fabric.Collection
*/
fabric.Collection = {
_objects: [],
/**
* Adds objects to collection, Canvas or Group, then renders canvas
* (if `renderOnAddRemove` is not `false`).
* in case of Group no changes to bounding box are made.
* Objects should be instances of (or inherit from) fabric.Object
* Use of this function is highly discouraged for groups.
* you can add a bunch of objects with the add method but then you NEED
* to run a addWithUpdate call for the Group class or position/bbox will be wrong.
* @param {...fabric.Object} object Zero or more fabric instances
* @return {Self} thisArg
* @chainable
*/
add: function () {
this._objects.push.apply(this._objects, arguments);
if (this._onObjectAdded) {
for (var i = 0, length = arguments.length; i < length; i++) {
this._onObjectAdded(arguments[i]);
}
}
this.renderOnAddRemove && this.requestRenderAll();
return this;
},
/**
* Inserts an object into collection at specified index, then renders canvas (if `renderOnAddRemove` is not `false`)
* An object should be an instance of (or inherit from) fabric.Object
* Use of this function is highly discouraged for groups.
* you can add a bunch of objects with the insertAt method but then you NEED
* to run a addWithUpdate call for the Group class or position/bbox will be wrong.
* @param {Object} object Object to insert
* @param {Number} index Index to insert object at
* @param {Boolean} nonSplicing When `true`, no splicing (shifting) of objects occurs
* @return {Self} thisArg
* @chainable
*/
insertAt: function (object, index, nonSplicing) {
var objects = this._objects;
if (nonSplicing) {
objects[index] = object;
}
else {
objects.splice(index, 0, object);
}
this._onObjectAdded && this._onObjectAdded(object);
this.renderOnAddRemove && this.requestRenderAll();
return this;
},
/**
* Removes objects from a collection, then renders canvas (if `renderOnAddRemove` is not `false`)
* @param {...fabric.Object} object Zero or more fabric instances
* @return {Self} thisArg
* @chainable
*/
remove: function () {
var objects = this._objects,
index, somethingRemoved = false;
for (var i = 0, length = arguments.length; i < length; i++) {
index = objects.indexOf(arguments[i]);
// only call onObjectRemoved if an object was actually removed
if (index !== -1) {
somethingRemoved = true;
objects.splice(index, 1);
this._onObjectRemoved && this._onObjectRemoved(arguments[i]);
}
}
this.renderOnAddRemove && somethingRemoved && this.requestRenderAll();
return this;
},
/**
* Executes given function for each object in this group
* @param {Function} callback
* Callback invoked with current object as first argument,
* index - as second and an array of all objects - as third.
* Callback is invoked in a context of Global Object (e.g. `window`)
* when no `context` argument is given
*
* @param {Object} context Context (aka thisObject)
* @return {Self} thisArg
* @chainable
*/
forEachObject: function (callback, context) {
var objects = this.getObjects();
for (var i = 0, len = objects.length; i < len; i++) {
callback.call(context, objects[i], i, objects);
}
return this;
},
/**
* Returns an array of children objects of this instance
* Type parameter introduced in 1.3.10
* since 2.3.5 this method return always a COPY of the array;
* @param {String} [type] When specified, only objects of this type are returned
* @return {Array}
*/
getObjects: function (type) {
if (typeof type === 'undefined') {
return this._objects.concat();
}
return this._objects.filter(function (o) {
return o.type === type;
});
},
/**
* Returns object at specified index
* @param {Number} index
* @return {Self} thisArg
*/
item: function (index) {
return this._objects[index];
},
/**
* Returns true if collection contains no objects
* @return {Boolean} true if collection is empty
*/
isEmpty: function () {
return this._objects.length === 0;
},
/**
* Returns a size of a collection (i.e: length of an array containing its objects)
* @return {Number} Collection size
*/
size: function () {
return this._objects.length;
},
/**
* Returns true if collection contains an object
* @param {Object} object Object to check against
* @return {Boolean} `true` if collection contains an object
*/
contains: function (object) {
return this._objects.indexOf(object) > -1;
},
/**
* Returns number representation of a collection complexity
* @return {Number} complexity
*/
complexity: function () {
return this._objects.reduce(function (memo, current) {
memo += current.complexity ? current.complexity() : 0;
return memo;
}, 0);
}
};
/**
* @namespace fabric.CommonMethods
*/
fabric.CommonMethods = {
/**
* Sets object's properties from options
* @param {Object} [options] Options object
*/
_setOptions: function (options) {
for (var prop in options) {
this.set(prop, options[prop]);
}
},
/**
* @private
* @param {Object} [filler] Options object
* @param {String} [property] property to set the Gradient to
*/
_initGradient: function (filler, property) {
if (filler && filler.colorStops && !(filler instanceof fabric.Gradient)) {
this.set(property, new fabric.Gradient(filler));
}
},
/**
* @private
* @param {Object} [filler] Options object
* @param {String} [property] property to set the Pattern to
* @param {Function} [callback] callback to invoke after pattern load
*/
_initPattern: function (filler, property, callback) {
if (filler && filler.source && !(filler instanceof fabric.Pattern)) {
this.set(property, new fabric.Pattern(filler, callback));
}
else {
callback && callback();
}
},
/**
* @private
* @param {Object} [options] Options object
*/
_initClipping: function (options) {
if (!options.clipTo || typeof options.clipTo !== 'string') {
return;
}
var functionBody = fabric.util.getFunctionBody(options.clipTo);
if (typeof functionBody !== 'undefined') {
this.clipTo = new Function('ctx', functionBody);
}
},
/**
* @private
*/
_setObject: function (obj) {
for (var prop in obj) {
this._set(prop, obj[prop]);
}
},
/**
* Sets property to a given value. When changing position/dimension -related properties (left, top, scale, angle, etc.) `set` does not update position of object's borders/controls. If you need to update those, call `setCoords()`.
* @param {String|Object} key Property name or object (if object, iterate over the object properties)
* @param {Object|Function} value Property value (if function, the value is passed into it and its return value is used as a new one)
* @return {fabric.Object} thisArg
* @chainable
*/
set: function (key, value) {
if (typeof key === 'object') {
this._setObject(key);
}
else {
if (typeof value === 'function' && key !== 'clipTo') {
this._set(key, value(this.get(key)));
}
else {
this._set(key, value);
}
}
return this;
},
_set: function (key, value) {
this[key] = value;
},
/**
* Toggles specified property from `true` to `false` or from `false` to `true`
* @param {String} property Property to toggle
* @return {fabric.Object} thisArg
* @chainable
*/
toggle: function (property) {
var value = this.get(property);
if (typeof value === 'boolean') {
this.set(property, !value);
}
return this;
},
/**
* Basic getter
* @param {String} property Property name
* @return {*} value of a property
*/
get: function (property) {
return this[property];
}
};
(function (global) {
var sqrt = Math.sqrt,
atan2 = Math.atan2,
pow = Math.pow,
PiBy180 = Math.PI / 180,
PiBy2 = Math.PI / 2;
/**
* @namespace fabric.util
*/
fabric.util = {
/**
* Calculate the cos of an angle, avoiding returning floats for known results
* @static
* @memberOf fabric.util
* @param {Number} angle the angle in radians or in degree
* @return {Number}
*/
cos: function (angle) {
if (angle === 0) {
return 1;
}
if (angle < 0) {
// cos(a) = cos(-a)
angle = -angle;
}
var angleSlice = angle / PiBy2;
switch (angleSlice) {
case 1:
case 3:
return 0;
case 2:
return -1;
}
return Math.cos(angle);
},
/**
* Calculate the sin of an angle, avoiding returning floats for known results
* @static
* @memberOf fabric.util
* @param {Number} angle the angle in radians or in degree
* @return {Number}
*/
sin: function (angle) {
if (angle === 0) {
return 0;
}
var angleSlice = angle / PiBy2, sign = 1;
if (angle < 0) {
// sin(-a) = -sin(a)
sign = -1;
}
switch (angleSlice) {
case 1:
return sign;
case 2:
return 0;
case 3:
return -sign;
}
return Math.sin(angle);
},
/**
* Removes value from an array.
* Presence of value (and its position in an array) is determined via `Array.prototype.indexOf`
* @static
* @memberOf fabric.util
* @param {Array} array
* @param {*} value
* @return {Array} original array
*/
removeFromArray: function (array, value) {
var idx = array.indexOf(value);
if (idx !== -1) {
array.splice(idx, 1);
}
return array;
},
/**
* Returns random number between 2 specified ones.
* @static
* @memberOf fabric.util
* @param {Number} min lower limit
* @param {Number} max upper limit
* @return {Number} random value (between min and max)
*/
getRandomInt: function (min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
},
/**
* Transforms degrees to radians.
* @static
* @memberOf fabric.util
* @param {Number} degrees value in degrees
* @return {Number} value in radians
*/
degreesToRadians: function (degrees) {
return degrees * PiBy180;
},
/**
* Transforms radians to degrees.
* @static
* @memberOf fabric.util
* @param {Number} radians value in radians
* @return {Number} value in degrees
*/
radiansToDegrees: function (radians) {
return radians / PiBy180;
},
/**
* Rotates `point` around `origin` with `radians`
* @static
* @memberOf fabric.util
* @param {fabric.Point} point The point to rotate
* @param {fabric.Point} origin The origin of the rotation
* @param {Number} radians The radians of the angle for the rotation
* @return {fabric.Point} The new rotated point
*/
rotatePoint: function (point, origin, radians) {
point.subtractEquals(origin);
var v = fabric.util.rotateVector(point, radians);
return new fabric.Point(v.x, v.y).addEquals(origin);
},
/**
* Rotates `vector` with `radians`
* @static
* @memberOf fabric.util
* @param {Object} vector The vector to rotate (x and y)
* @param {Number} radians The radians of the angle for the rotation
* @return {Object} The new rotated point
*/
rotateVector: function (vector, radians) {
var sin = fabric.util.sin(radians),
cos = fabric.util.cos(radians),
rx = vector.x * cos - vector.y * sin,
ry = vector.x * sin + vector.y * cos;
return {
x: rx,
y: ry
};
},
/**
* Apply transform t to point p
* @static
* @memberOf fabric.util
* @param {fabric.Point} p The point to transform
* @param {Array} t The transform
* @param {Boolean} [ignoreOffset] Indicates that the offset should not be applied
* @return {fabric.Point} The transformed point
*/
transformPoint: function (p, t, ignoreOffset) {
if (ignoreOffset) {
return new fabric.Point(
t[0] * p.x + t[2] * p.y,
t[1] * p.x + t[3] * p.y
);
}
return new fabric.Point(
t[0] * p.x + t[2] * p.y + t[4],
t[1] * p.x + t[3] * p.y + t[5]
);
},
/**
* Returns coordinates of points's bounding rectangle (left, top, width, height)
* @param {Array} points 4 points array
* @param {Array} [transform] an array of 6 numbers representing a 2x3 transform matrix
* @return {Object} Object with left, top, width, height properties
*/
makeBoundingBoxFromPoints: function (points, transform) {
if (transform) {
for (var i = 0; i < points.length; i++) {
points[i] = fabric.util.transformPoint(points[i], transform);
}
}
var xPoints = [points[0].x, points[1].x, points[2].x, points[3].x],
minX = fabric.util.array.min(xPoints),
maxX = fabric.util.array.max(xPoints),
width = maxX - minX,
yPoints = [points[0].y, points[1].y, points[2].y, points[3].y],
minY = fabric.util.array.min(yPoints),
maxY = fabric.util.array.max(yPoints),
height = maxY - minY;
return {
left : minX,
top : minY,
width : width,
height: height
};
},
/**
* Invert transformation t
* @static
* @memberOf fabric.util
* @param {Array} t The transform
* @return {Array} The inverted transform
*/
invertTransform: function (t) {
var a = 1 / (t[0] * t[3] - t[1] * t[2]),
r = [a * t[3], -a * t[1], -a * t[2], a * t[0]],
o = fabric.util.transformPoint({x: t[4], y: t[5]}, r, true);
r[4] = -o.x;
r[5] = -o.y;
return r;
},
/**
* A wrapper around Number#toFixed, which contrary to native method returns number, not string.
* @static
* @memberOf fabric.util
* @param {Number|String} number number to operate on
* @param {Number} fractionDigits number of fraction digits to "leave"
* @return {Number}
*/
toFixed: function (number, fractionDigits) {
return parseFloat(Number(number).toFixed(fractionDigits));
},
/**
* Converts from attribute value to pixel value if applicable.
* Returns converted pixels or original value not converted.
* @param {Number|String} value number to operate on
* @param {Number} fontSize
* @return {Number|String}
*/
parseUnit: function (value, fontSize) {
var unit = /\D{0,2}$/.exec(value),
number = parseFloat(value);
if (!fontSize) {
fontSize = fabric.Text.DEFAULT_SVG_FONT_SIZE;
}
switch (unit[0]) {
case 'mm':
return number * fabric.DPI / 25.4;
case 'cm':
return number * fabric.DPI / 2.54;
case 'in':
return number * fabric.DPI;
case 'pt':
return number * fabric.DPI / 72; // or * 4 / 3
case 'pc':
return number * fabric.DPI / 72 * 12; // or * 16
case 'em':
return number * fontSize;
default:
return number;
}
},
/**
* Function which always returns `false`.
* @static
* @memberOf fabric.util
* @return {Boolean}
*/
falseFunction: function () {
return false;
},
/**
* Returns klass "Class" object of given namespace
* @memberOf fabric.util
* @param {String} type Type of object (eg. 'circle')
* @param {String} namespace Namespace to get klass "Class" object from
* @return {Object} klass "Class"
*/
getKlass: function (type, namespace) {
// capitalize first letter only
type = fabric.util.string.camelize(type.charAt(0).toUpperCase() + type.slice(1));
return fabric.util.resolveNamespace(namespace)[type];
},
/**
* Returns array of attributes for given svg that fabric parses
* @memberOf fabric.util
* @param {String} type Type of svg element (eg. 'circle')
* @return {Array} string names of supported attributes
*/
getSvgAttributes: function (type) {
var attributes = [
'instantiated_by_use',
'style',
'id',
'class'
];
switch (type) {
case 'linearGradient':
attributes = attributes.concat(['x1', 'y1', 'x2', 'y2', 'gradientUnits', 'gradientTransform']);
break;
case 'radialGradient':
attributes = attributes.concat(['gradientUnits', 'gradientTransform', 'cx', 'cy', 'r', 'fx', 'fy', 'fr']);
break;
case 'stop':
attributes = attributes.concat(['offset', 'stop-color', 'stop-opacity']);
break;
}
return attributes;
},
/**
* Returns object of given namespace
* @memberOf fabric.util
* @param {String} namespace Namespace string e.g. 'fabric.Image.filter' or 'fabric'
* @return {Object} Object for given namespace (default fabric)
*/
resolveNamespace: function (namespace) {
if (!namespace) {
return fabric;
}
var parts = namespace.split('.'),
len = parts.length, i,
obj = global || fabric.window;
for (i = 0; i < len; ++i) {
obj = obj[parts[i]];
}
return obj;
},
/**
* Loads image element from given url and passes it to a callback
* @memberOf fabric.util
* @param {String} url URL representing an image
* @param {Function} callback Callback; invoked with loaded image
* @param {*} [context] Context to invoke callback in
* @param {Object} [crossOrigin] crossOrigin value to set image element to
*/
loadImage: function (url, callback, context, crossOrigin) {
if (!url) {
callback && callback.call(context, url);
return;
}
var img = fabric.util.createImage();
/** @ignore */
var onLoadCallback = function () {
callback && callback.call(context, img);
img = img.onload = img.onerror = null;
};
img.onload = onLoadCallback;
/** @ignore */
img.onerror = function () {
fabric.log('Error loading ' + img.src);
callback && callback.call(context, null, true);
img = img.onload = img.onerror = null;
};
// data-urls appear to be buggy with crossOrigin
// https://github.com/kangax/fabric.js/commit/d0abb90f1cd5c5ef9d2a94d3fb21a22330da3e0a#commitcomment-4513767
// see https://code.google.com/p/chromium/issues/detail?id=315152
// https://bugzilla.mozilla.org/show_bug.cgi?id=935069
if (url.indexOf('data') !== 0 && crossOrigin) {
img.crossOrigin = crossOrigin;
}
// IE10 / IE11-Fix: SVG contents from data: URI
// will only be available if the IMG is present
// in the DOM (and visible)
if (url.substring(0, 14) === 'data:image/svg') {
img.onload = null;
fabric.util.loadImageInDom(img, onLoadCallback);
}
img.src = url;
},
/**
* Attaches SVG image with data: URL to the dom
* @memberOf fabric.util
* @param {Object} img Image object with data:image/svg src
* @param {Function} callback Callback; invoked with loaded image
* @return {Object} DOM element (div containing the SVG image)
*/
loadImageInDom: function (img, onLoadCallback) {
var div = fabric.document.createElement('div');
div.style.width = div.style.height = '1px';
div.style.left = div.style.top = '-100%';
div.style.position = 'absolute';
div.appendChild(img);
fabric.document.querySelector('body').appendChild(div);
/**
* Wrap in function to:
* 1. Call existing callback
* 2. Cleanup DOM
*/
img.onload = function () {
onLoadCallback();
div.parentNode.removeChild(div);
div = null;
};
},
/**
* Creates corresponding fabric instances from their object representations
* @static
* @memberOf fabric.util
* @param {Array} objects Objects to enliven
* @param {Function} callback Callback to invoke when all objects are created
* @param {String} namespace Namespace to get klass "Class" object from
* @param {Function} reviver Method for further parsing of object elements,
* called after each fabric object created.
*/
enlivenObjects: function (objects, callback, namespace, reviver) {
objects = objects || [];
var enlivenedObjects = [],
numLoadedObjects = 0,
numTotalObjects = objects.length;
function onLoaded () {
if (++numLoadedObjects === numTotalObjects) {
callback && callback(enlivenedObjects.filter(function (obj) {
// filter out undefined objects (objects that gave error)
return obj;
}));
}
}
if (!numTotalObjects) {
callback && callback(enlivenedObjects);
return;
}
objects.forEach(function (o, index) {
// if sparse array
if (!o || !o.type) {
onLoaded();
return;
}
var klass = fabric.util.getKlass(o.type, namespace);
klass.fromObject(o, function (obj, error) {
error || (enlivenedObjects[index] = obj);
reviver && reviver(o, obj, error);
onLoaded();
});
});
},
/**
* Create and wait for loading of patterns
* @static
* @memberOf fabric.util
* @param {Array} patterns Objects to enliven
* @param {Function} callback Callback to invoke when all objects are created
* called after each fabric object created.
*/
enlivenPatterns: function (patterns, callback) {
patterns = patterns || [];
function onLoaded () {
if (++numLoadedPatterns === numPatterns) {
callback && callback(enlivenedPatterns);
}
}
var enlivenedPatterns = [],
numLoadedPatterns = 0,
numPatterns = patterns.length;
if (!numPatterns) {
callback && callback(enlivenedPatterns);
return;
}
patterns.forEach(function (p, index) {
if (p && p.source) {
new fabric.Pattern(p, function (pattern) {
enlivenedPatterns[index] = pattern;
onLoaded();
});
}
else {
enlivenedPatterns[index] = p;
onLoaded();
}
});
},
/**
* Groups SVG elements (usually those retrieved from SVG document)
* @static
* @memberOf fabric.util
* @param {Array} elements SVG elements to group
* @param {Object} [options] Options object
* @param {String} path Value to set sourcePath to
* @return {fabric.Object|fabric.Group}
*/
groupSVGElements: function (elements, options, path) {
var object;
if (elements && elements.length === 1) {
return elements[0];
}
if (options) {
if (options.width && options.height) {
options.centerPoint = {
x: options.width / 2,
y: options.height / 2
};
}
else {
delete options.width;
delete options.height;
}
}
object = new fabric.Group(elements, options);
if (typeof path !== 'undefined') {
object.sourcePath = path;
}
return object;
},
/**
* Populates an object with properties of another object
* @static
* @memberOf fabric.util
* @param {Object} source Source object
* @param {Object} destination Destination object
* @return {Array} properties Properties names to include
*/
populateWithProperties: function (source, destination, properties) {
if (properties && Object.prototype.toString.call(properties) === '[object Array]') {
for (var i = 0, len = properties.length; i < len; i++) {
if (properties[i] in source) {
destination[properties[i]] = source[properties[i]];
}
}
}
},
/**
* Draws a dashed line between two points
*
* This method is used to draw dashed line around selection area.
* See <a href="http://stackoverflow.com/questions/4576724/dotted-stroke-in-canvas">dotted stroke in canvas</a>
*
* @param {CanvasRenderingContext2D} ctx context
* @param {Number} x start x coordinate
* @param {Number} y start y coordinate
* @param {Number} x2 end x coordinate
* @param {Number} y2 end y coordinate
* @param {Array} da dash array pattern
*/
drawDashedLine: function (ctx, x, y, x2, y2, da) {
var dx = x2 - x,
dy = y2 - y,
len = sqrt(dx * dx + dy * dy),
rot = atan2(dy, dx),
dc = da.length,
di = 0,
draw = true;
ctx.save();
ctx.translate(x, y);
ctx.moveTo(0, 0);
ctx.rotate(rot);
x = 0;
while (len > x) {
x += da[di++ % dc];
if (x > len) {
x = len;
}
ctx[draw ? 'lineTo' : 'moveTo'](x, 0);
draw = !draw;
}
ctx.restore();
},
/**
* Creates canvas element
* @static
* @memberOf fabric.util
* @return {CanvasElement} initialized canvas element
*/
createCanvasElement: function () {
return fabric.document.createElement('canvas');
},
/**
* Creates a canvas element that is a copy of another and is also painted
* @param {CanvasElement} canvas to copy size and content of
* @static
* @memberOf fabric.util
* @return {CanvasElement} initialized canvas element
*/
copyCanvasElement: function (canvas) {
var newCanvas = fabric.util.createCanvasElement();
newCanvas.width = canvas.width;
newCanvas.height = canvas.height;
newCanvas.getContext('2d').drawImage(canvas, 0, 0);
return newCanvas;
},
/**
* since 2.6.0 moved from canvas instance to utility.
* @param {CanvasElement} canvasEl to copy size and content of
* @param {String} format 'jpeg' or 'png', in some browsers 'webp' is ok too
* @param {Number} quality <= 1 and > 0
* @static
* @memberOf fabric.util
* @return {String} data url
*/
toDataURL: function (canvasEl, format, quality) {
return canvasEl.toDataURL('image/' + format, quality);
},
/**
* Creates image element (works on client and node)
* @static
* @memberOf fabric.util
* @return {HTMLImageElement} HTML image element
*/
createImage: function () {
return fabric.document.createElement('img');
},
/**
* @static
* @memberOf fabric.util
* @deprecated since 2.0.0
* @param {fabric.Object} receiver Object implementing `clipTo` method
* @param {CanvasRenderingContext2D} ctx Context to clip
*/
clipContext: function (receiver, ctx) {
ctx.save();
ctx.beginPath();
receiver.clipTo(ctx);
ctx.clip();
},
/**
* Multiply matrix A by matrix B to nest transformations
* @static
* @memberOf fabric.util
* @param {Array} a First transformMatrix
* @param {Array} b Second transformMatrix
* @param {Boolean} is2x2 flag to multiply matrices as 2x2 matrices
$