draw-image-editor
Version:
canvas fabric work correct
1,695 lines (1,516 loc) • 1.1 MB
JavaScript
(function webpackUniversalModuleDefinition(root, factory) {
if(typeof exports === 'object' && typeof module === 'object')
module.exports = factory(require("vue"));
else if(typeof define === 'function' && define.amd)
define([], factory);
else if(typeof exports === 'object')
exports["FabricCanvas"] = factory(require("vue"));
else
root["FabricCanvas"] = factory(root["Vue"]);
})((typeof self !== 'undefined' ? self : this), function(__WEBPACK_EXTERNAL_MODULE__8bbf__) {
return /******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId]) {
/******/ return installedModules[moduleId].exports;
/******/ }
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ i: moduleId,
/******/ l: false,
/******/ exports: {}
/******/ };
/******/
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ // Flag the module as loaded
/******/ module.l = true;
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/******/
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/
/******/ // define getter function for harmony exports
/******/ __webpack_require__.d = function(exports, name, getter) {
/******/ if(!__webpack_require__.o(exports, name)) {
/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter });
/******/ }
/******/ };
/******/
/******/ // define __esModule on exports
/******/ __webpack_require__.r = function(exports) {
/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
/******/ }
/******/ Object.defineProperty(exports, '__esModule', { value: true });
/******/ };
/******/
/******/ // create a fake namespace object
/******/ // mode & 1: value is a module id, require it
/******/ // mode & 2: merge all properties of value into the ns
/******/ // mode & 4: return value when already ns object
/******/ // mode & 8|1: behave like require
/******/ __webpack_require__.t = function(value, mode) {
/******/ if(mode & 1) value = __webpack_require__(value);
/******/ if(mode & 8) return value;
/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
/******/ var ns = Object.create(null);
/******/ __webpack_require__.r(ns);
/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value });
/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
/******/ return ns;
/******/ };
/******/
/******/ // getDefaultExport function for compatibility with non-harmony modules
/******/ __webpack_require__.n = function(module) {
/******/ var getter = module && module.__esModule ?
/******/ function getDefault() { return module['default']; } :
/******/ function getModuleExports() { return module; };
/******/ __webpack_require__.d(getter, 'a', getter);
/******/ return getter;
/******/ };
/******/
/******/ // Object.prototype.hasOwnProperty.call
/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "";
/******/
/******/
/******/ // Load entry module and return exports
/******/ return __webpack_require__(__webpack_require__.s = "fb15");
/******/ })
/************************************************************************/
/******/ ({
/***/ 0:
/***/ (function(module, exports) {
/* (ignored) */
/***/ }),
/***/ "0b9a":
/***/ (function(module, exports, __webpack_require__) {
(function(global) {
// var fabric = global.fabric || (global.fabric = {}),
// extend = fabric.util.object.extend,
var clone = fabric.util.object.clone;
// coordProps = { x1: 1, x2: 1, y1: 1, y2: 1 },
supportsLineDash = fabric.StaticCanvas.supports("setLineDash");
if (fabric.WaveLine) {
fabric.warn("fabric.WaveLine is already defined.");
return;
}
fabric.WaveLine = fabric.util.createClass(fabric.Line, {
type: "WaveLine",
initialize(element, options) {
options || (options = {});
this.callSuper("initialize", element, options);
// Set default options
this.set({
// hasBorders: false,
// hasControls: false,
});
},
_render(ctx) {
// this.callSuper('_render', ctx);
ctx.save();
const xDiff = this.x2 - this.x1;
const yDiff = this.y2 - this.y1;
const angle = Math.atan2(yDiff, xDiff);
ctx.translate(xDiff / 2, yDiff / 2);
ctx.rotate(angle);
ctx.beginPath();
ctx.closePath();
ctx.fillStyle = this.stroke;
ctx.strokeStyle = this.stroke;
ctx.lineWidth = this.strokeWidth;
ctx.fill();
ctx.restore();
const p = this.calcLinePoints();
const point = this.pointOnLine(
this.point(p.x2, p.y2),
this.point(p.x1, p.y1),
10
);
this.wavy(
this.point(p.x1, p.y1),
point,
this.point(p.x2, p.y2),
ctx
);
ctx.stroke();
},
point(x, y) {
return {
x,
y,
};
},
wavy(from, to, endPoint, ctx) {
let cx = 0,
cy = 0,
fx = from.x,
fy = from.y,
tx = to.x,
ty = to.y,
i = 0,
step = 4,
waveOffsetLength = 0,
ang = Math.atan2(ty - fy, tx - fx),
distance = Math.sqrt(
(fx - tx) * (fx - tx) + (fy - ty) * (fy - ty)
),
amplitude = -4,
f = (Math.PI * distance) / 10;
for (i; i <= distance; i += step) {
waveOffsetLength = Math.sin((i / distance) * f) * amplitude;
cx =
from.x +
Math.cos(ang) * i +
Math.cos(ang - Math.PI / 2) * waveOffsetLength;
cy =
from.y +
Math.sin(ang) * i +
Math.sin(ang - Math.PI / 2) * waveOffsetLength;
i > 0 ? ctx.lineTo(cx, cy) : ctx.moveTo(cx, cy);
}
ctx.lineTo(to.x, to.y);
// ctx.lineTo(endPoint.x, endPoint.y);
},
pointOnLine(point1, point2, dist) {
const len = Math.sqrt(
(point2.x - point1.x) * (point2.x - point1.x) +
(point2.y - point1.y) * (point2.y - point1.y)
);
const t = dist / len;
let x3 = (1 - t) * point1.x + t * point2.x,
y3 = (1 - t) * point1.y + t * point2.y;
return new fabric.Point(x3, y3);
},
toObject() {
return fabric.util.object.extend(this.callSuper("toObject"), {
customProps: this.customProps,
});
},
});
fabric.WaveLine.ATTRIBUTE_NAMES = fabric.SHARED_ATTRIBUTES.concat(
"x1 y1 x2 y2".split(" ")
);
fabric.WaveLine.fromElement = function(element, callback) {
var parsedAttributes = fabric.parseAttributes(
element,
fabric.WaveLine.ATTRIBUTE_NAMES
);
parsedAttributes.left =
(parsedAttributes.left || 0) - parsedAttributes.rx;
parsedAttributes.top =
(parsedAttributes.top || 0) - parsedAttributes.ry;
callback(new fabric.WaveLine(parsedAttributes));
};
/* _FROM_SVG_END_ */
/**
* Returns {@link fabric.Ellipse} instance from an object representation
* @static
* @memberOf fabric.Ellipse
* @param {Object} object Object to create an instance from
* @param {function} [callback] invoked with new instance as first argument
* @return {fabric.Ellipse}
*/
// fabric.WaveLine.fromObject = function(object, callback) {
// return fabric.WaveLine._fromObject("WaveLine", object, callback);
// };
fabric.WaveLine.fromObject = function(object, callback) {
function _callback(instance) {
delete instance.points;
callback && callback(instance);
}
var options = clone(object, true);
options.points = [object.x1, object.y1, object.x2, object.y2];
fabric.Object._fromObject("WaveLine", options, _callback, "points");
};
})( true ? exports : undefined);
// const { color = "#E34F51", lineWidth = 3, radius = 10 } = config;
// y2 - y1 < 4 * radius && (y2 = y1 + 4 * radius);
// var z1 = (y2 - y1 - 4 * radius) / 2;
// ctx.beginPath(),
// ctx.closePath(),
// (ctx.lineWidth = lineWidth),
// (ctx.strokeStyle = color),
// ctx.arc(x1, y1 + radius, radius, 1.5 * Math.PI, 2 * Math.PI),
// ctx.stroke();
// (ctx.strokeStyle = color),
// (ctx.lineWidth = lineWidth),
// ctx.beginPath(),
// ctx.moveTo(x1 + radius, y1 + radius),
// ctx.lineTo(x1 + radius, y1 + radius + z1),
// ctx.stroke(),
// ctx.beginPath(),
// (ctx.lineWidth = lineWidth),
// (ctx.strokeStyle = color),
// ctx.arc(
// x1 + 2 * radius,
// y1 + z1 + radius,
// radius,
// 0.5 * Math.PI,
// 1 * Math.PI
// ),
// ctx.stroke(),
// ctx.beginPath(),
// (ctx.lineWidth = lineWidth),
// (ctx.strokeStyle = color),
// ctx.arc(
// x1 + 2 * radius,
// y1 + z1 + 3 * radius,
// radius,
// 1 * Math.PI,
// 1.5 * Math.PI
// ),
// ctx.stroke(),
// (ctx.strokeStyle = color),
// (ctx.lineWidth = lineWidth),
// ctx.beginPath(),
// ctx.moveTo(x1 + radius, y1 + z1 + 3 * radius),
// ctx.lineTo(x1 + radius, y1 + 2 * z1 + 3 * radius),
// ctx.stroke(),
// ctx.beginPath(),
// (ctx.lineWidth = lineWidth),
// (ctx.strokeStyle = color),
// ctx.arc(
// x1,
// y1 + 2 * z1 + 3 * radius,
// radius,
// 0 * Math.PI,
// 0.5 * Math.PI
// ),
// ctx.stroke();
// if (y2 >= y1) {
// topArc = [1.5 * Math.PI, 2 * Math.PI];
// topArrow = [0.5 * Math.PI, 1 * Math.PI];
// botArrow = [1 * Math.PI, 1.5 * Math.PI];
// botArc = [0 * Math.PI, 0.5 * Math.PI];
// } else {
// topArc = [0 * Math.PI, 0.5 * Math.PI];
// botArc = [1.5 * Math.PI, 2 * Math.PI];
// topArrow = [1 * Math.PI, 1.5 * Math.PI];
// botArrow = [0.5 * Math.PI, 1 * Math.PI];
// }
// radius = (y2 - y1) / radius > radius ? 10 : Math.abs((y2 - y1) / radius);
/***/ }),
/***/ 1:
/***/ (function(module, exports) {
/* (ignored) */
/***/ }),
/***/ "1fb5":
/***/ (function(module, exports, __webpack_require__) {
"use strict";
exports.byteLength = byteLength
exports.toByteArray = toByteArray
exports.fromByteArray = fromByteArray
var lookup = []
var revLookup = []
var Arr = typeof Uint8Array !== 'undefined' ? Uint8Array : Array
var code = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
for (var i = 0, len = code.length; i < len; ++i) {
lookup[i] = code[i]
revLookup[code.charCodeAt(i)] = i
}
// Support decoding URL-safe base64 strings, as Node.js does.
// See: https://en.wikipedia.org/wiki/Base64#URL_applications
revLookup['-'.charCodeAt(0)] = 62
revLookup['_'.charCodeAt(0)] = 63
function getLens (b64) {
var len = b64.length
if (len % 4 > 0) {
throw new Error('Invalid string. Length must be a multiple of 4')
}
// Trim off extra bytes after placeholder bytes are found
// See: https://github.com/beatgammit/base64-js/issues/42
var validLen = b64.indexOf('=')
if (validLen === -1) validLen = len
var placeHoldersLen = validLen === len
? 0
: 4 - (validLen % 4)
return [validLen, placeHoldersLen]
}
// base64 is 4/3 + up to two characters of the original data
function byteLength (b64) {
var lens = getLens(b64)
var validLen = lens[0]
var placeHoldersLen = lens[1]
return ((validLen + placeHoldersLen) * 3 / 4) - placeHoldersLen
}
function _byteLength (b64, validLen, placeHoldersLen) {
return ((validLen + placeHoldersLen) * 3 / 4) - placeHoldersLen
}
function toByteArray (b64) {
var tmp
var lens = getLens(b64)
var validLen = lens[0]
var placeHoldersLen = lens[1]
var arr = new Arr(_byteLength(b64, validLen, placeHoldersLen))
var curByte = 0
// if there are placeholders, only get up to the last complete 4 chars
var len = placeHoldersLen > 0
? validLen - 4
: validLen
var i
for (i = 0; i < len; i += 4) {
tmp =
(revLookup[b64.charCodeAt(i)] << 18) |
(revLookup[b64.charCodeAt(i + 1)] << 12) |
(revLookup[b64.charCodeAt(i + 2)] << 6) |
revLookup[b64.charCodeAt(i + 3)]
arr[curByte++] = (tmp >> 16) & 0xFF
arr[curByte++] = (tmp >> 8) & 0xFF
arr[curByte++] = tmp & 0xFF
}
if (placeHoldersLen === 2) {
tmp =
(revLookup[b64.charCodeAt(i)] << 2) |
(revLookup[b64.charCodeAt(i + 1)] >> 4)
arr[curByte++] = tmp & 0xFF
}
if (placeHoldersLen === 1) {
tmp =
(revLookup[b64.charCodeAt(i)] << 10) |
(revLookup[b64.charCodeAt(i + 1)] << 4) |
(revLookup[b64.charCodeAt(i + 2)] >> 2)
arr[curByte++] = (tmp >> 8) & 0xFF
arr[curByte++] = tmp & 0xFF
}
return arr
}
function tripletToBase64 (num) {
return lookup[num >> 18 & 0x3F] +
lookup[num >> 12 & 0x3F] +
lookup[num >> 6 & 0x3F] +
lookup[num & 0x3F]
}
function encodeChunk (uint8, start, end) {
var tmp
var output = []
for (var i = start; i < end; i += 3) {
tmp =
((uint8[i] << 16) & 0xFF0000) +
((uint8[i + 1] << 8) & 0xFF00) +
(uint8[i + 2] & 0xFF)
output.push(tripletToBase64(tmp))
}
return output.join('')
}
function fromByteArray (uint8) {
var tmp
var len = uint8.length
var extraBytes = len % 3 // if we have 1 byte left, pad 2 bytes
var parts = []
var maxChunkLength = 16383 // must be multiple of 3
// go through the array every three bytes, we'll deal with trailing stuff later
for (var i = 0, len2 = len - extraBytes; i < len2; i += maxChunkLength) {
parts.push(encodeChunk(uint8, i, (i + maxChunkLength) > len2 ? len2 : (i + maxChunkLength)))
}
// pad the end with zeros, but make sure to not forget the extra bytes
if (extraBytes === 1) {
tmp = uint8[len - 1]
parts.push(
lookup[tmp >> 2] +
lookup[(tmp << 4) & 0x3F] +
'=='
)
} else if (extraBytes === 2) {
tmp = (uint8[len - 2] << 8) + uint8[len - 1]
parts.push(
lookup[tmp >> 10] +
lookup[(tmp >> 4) & 0x3F] +
lookup[(tmp << 2) & 0x3F] +
'='
)
}
return parts.join('')
}
/***/ }),
/***/ 2:
/***/ (function(module, exports) {
/* (ignored) */
/***/ }),
/***/ "239c":
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
/* harmony import */ var _node_modules_mini_css_extract_plugin_dist_loader_js_ref_10_oneOf_1_0_node_modules_vue_cli_service_node_modules_css_loader_dist_cjs_js_ref_10_oneOf_1_1_node_modules_vue_cli_service_node_modules_vue_loader_lib_loaders_stylePostLoader_js_node_modules_postcss_loader_src_index_js_ref_10_oneOf_1_2_node_modules_less_loader_dist_cjs_js_ref_10_oneOf_1_3_node_modules_cache_loader_dist_cjs_js_ref_0_0_node_modules_vue_cli_service_node_modules_vue_loader_lib_index_js_vue_loader_options_index_vue_vue_type_style_index_0_lang_less___WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__("c3af");
/* harmony import */ var _node_modules_mini_css_extract_plugin_dist_loader_js_ref_10_oneOf_1_0_node_modules_vue_cli_service_node_modules_css_loader_dist_cjs_js_ref_10_oneOf_1_1_node_modules_vue_cli_service_node_modules_vue_loader_lib_loaders_stylePostLoader_js_node_modules_postcss_loader_src_index_js_ref_10_oneOf_1_2_node_modules_less_loader_dist_cjs_js_ref_10_oneOf_1_3_node_modules_cache_loader_dist_cjs_js_ref_0_0_node_modules_vue_cli_service_node_modules_vue_loader_lib_index_js_vue_loader_options_index_vue_vue_type_style_index_0_lang_less___WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_node_modules_mini_css_extract_plugin_dist_loader_js_ref_10_oneOf_1_0_node_modules_vue_cli_service_node_modules_css_loader_dist_cjs_js_ref_10_oneOf_1_1_node_modules_vue_cli_service_node_modules_vue_loader_lib_loaders_stylePostLoader_js_node_modules_postcss_loader_src_index_js_ref_10_oneOf_1_2_node_modules_less_loader_dist_cjs_js_ref_10_oneOf_1_3_node_modules_cache_loader_dist_cjs_js_ref_0_0_node_modules_vue_cli_service_node_modules_vue_loader_lib_index_js_vue_loader_options_index_vue_vue_type_style_index_0_lang_less___WEBPACK_IMPORTED_MODULE_0__);
/* unused harmony reexport * */
/***/ }),
/***/ "24aa":
/***/ (function(module, exports) {
var g;
// This works in non-strict mode
g = (function() {
return this;
})();
try {
// This works if eval is allowed (see CSP)
g = g || new Function("return this")();
} catch (e) {
// This works if the window reference is available
if (typeof window === "object") g = window;
}
// g can still be undefined, but nothing to do about it...
// We return undefined, instead of nothing here, so it's
// easier to handle this case. if(!global) { ...}
module.exports = g;
/***/ }),
/***/ "2d70":
/***/ (function(module, exports, __webpack_require__) {
/*
* @Author: your name
* @Date: 2020-12-16 14:09:19
* @LastEditTime: 2020-12-18 23:30:43
* @LastEditors: Please set LastEditors
* @Description: In User Settings Edit
* @FilePath: /draw-image-editor/src/components/fabricCanvas/drawBomb.js
*/
(function(global) {
// var fabric = global.fabric || (global.fabric = {}),
// extend = fabric.util.object.extend,
var clone = fabric.util.object.clone;
// coordProps = { x1: 1, x2: 1, y1: 1, y2: 1 },
// supportsLineDash = fabric.StaticCanvas.supports("setLineDash");
if (fabric.DrawBomb) {
fabric.warn("fabric.DrawBomb is already defined.");
return;
}
fabric.DrawBomb = fabric.util.createClass(fabric.Line, {
type: "DrawBomb",
initialize(element, options) {
options || (options = {});
this.callSuper("initialize", element, options);
// Set default options
this.set({
// hasBorders: false,
// hasControls: false,
});
},
_render(ctx) {
// this.callSuper('_render', ctx);
// ctx.save();
const p = this.calcLinePoints();
const config = {
color: this.stroke,
lineWidth: this.strokeWidth,
radius: this.radius,
};
this.DrawBomb(ctx, p.x1, p.y1, p.x2, p.y2, config);
},
DrawBomb(ctx, startx, starty, endx, endy, config) {
let { color = "#E34F51", lineWidth = 3, radius = 3 } = config;
if (startx > endx) {
var pointX = startx - Math.abs(startx - endx) / 2;
} else {
var pointX = Math.abs(startx - endx) / 2 + startx;
}
if (starty > endy) {
var pointY = starty - Math.abs(starty - endy) / 2;
} else {
var pointY = Math.abs(starty - endy) / 2 + starty;
}
var lineX = Math.abs(startx - endx) / 2;
var lineY = Math.abs(starty - endy) / 2;
ctx.beginPath();
ctx.closePath();
ctx.lineWidth = lineWidth;
ctx.fillStyle = "rgba(0,0,0,0)";
ctx.strokeStyle = color;
ctx.ellipse(pointX, pointY, lineX, lineY, 0, 0, 2 * Math.PI);
ctx.stroke();
ctx.beginPath();
ctx.lineWidth = lineWidth;
ctx.fillStyle = "rgba(0,0,0,0)";
ctx.strokeStyle = color;
var ellipseX1 = startx + (endx - startx) / 2;
var ellipseY1 = starty;
// 第一条竖线
ctx.moveTo(ellipseX1 + 2, ellipseY1);
ctx.quadraticCurveTo(
Math.abs(ellipseX1),
ellipseY1 - 7,
ellipseX1 + 2,
ellipseY1 - 6
);
ctx.stroke();
//第一个圆
ctx.beginPath();
ctx.arc(ellipseX1 + 5, ellipseY1 - 6, radius, 0, 2 * Math.PI);
ctx.stroke();
// 第一个圆的圆边
ctx.beginPath();
ctx.moveTo(ellipseX1 + 2, ellipseY1 - 4);
ctx.quadraticCurveTo(
ellipseX1 - 3,
ellipseY1 - 8,
ellipseX1 + 3,
ellipseY1 - 15
);
ctx.stroke();
//承接线
ctx.beginPath();
ctx.moveTo(ellipseX1 + 4, ellipseY1 - 13.5);
ctx.quadraticCurveTo(
ellipseX1 + 3,
ellipseY1 - 18,
ellipseX1 + 10,
ellipseY1 - 17
);
ctx.stroke();
//第二个圆
ctx.beginPath();
ctx.arc(
Math.abs(ellipseX1 + 11),
ellipseY1 - 13,
radius,
0,
2 * Math.PI
);
ctx.stroke();
ctx.beginPath();
//第二个圆角
ctx.moveTo(Math.abs(ellipseX1 + 8), ellipseY1 - 15);
ctx.quadraticCurveTo(
Math.abs(ellipseX1 + 8),
ellipseY1 - 23,
Math.abs(ellipseX1 + 16),
ellipseY1 - 19
);
ctx.stroke();
ctx.beginPath();
ctx.stroke();
},
});
fabric.DrawBomb.ATTRIBUTE_NAMES = fabric.SHARED_ATTRIBUTES.concat(
"x1 y1 x2 y2".split(" ")
);
fabric.DrawBomb.fromElement = function(element, callback) {
var parsedAttributes = fabric.parseAttributes(
element,
fabric.DrawBomb.ATTRIBUTE_NAMES
);
parsedAttributes.left =
(parsedAttributes.left || 0) - parsedAttributes.rx;
parsedAttributes.top =
(parsedAttributes.top || 0) - parsedAttributes.ry;
callback(new fabric.DrawBomb(parsedAttributes));
};
/* _FROM_SVG_END_ */
/**
* Returns {@link fabric.DrawBomb} instance from an object representation
* @static
* @memberOf fabric.Ellipse
* @param {Object} object Object to create an instance from
* @param {function} [callback] invoked with new instance as first argument
* @return {fabric.Ellipse}
*/
fabric.DrawBomb.fromObject = function(object, callback) {
function _callback(instance) {
delete instance.points;
callback && callback(instance);
}
var options = clone(object, true);
options.points = [object.x1, object.y1, object.x2, object.y2];
fabric.Object._fromObject("DrawBomb", options, _callback, "points");
};
})( true ? exports : undefined);
/***/ }),
/***/ "34f0":
/***/ (function(module, exports, __webpack_require__) {
// extracted by mini-css-extract-plugin
/***/ }),
/***/ "4b5c":
/***/ (function(module, exports, __webpack_require__) {
(function(global) {
// var fabric = global.fabric || (global.fabric = {}),
// extend = fabric.util.object.extend,
var clone = fabric.util.object.clone;
// coordProps = { x1: 1, x2: 1, y1: 1, y2: 1 },
// supportsLineDash = fabric.StaticCanvas.supports("setLineDash");
if (fabric.BracketLine) {
fabric.warn("fabric.BracketLine is already defined.");
return;
}
fabric.BracketLine = fabric.util.createClass(fabric.Line, {
type: "BracketLine",
initialize(element, options) {
options || (options = {});
this.callSuper("initialize", element, options);
// Set default options
this.set({
// hasBorders: false,
// hasControls: false,
});
},
_render(ctx) {
// this.callSuper('_render', ctx);
// ctx.save();
const p = this.calcLinePoints();
const config = {
color: this.stroke,
lineWidth: this.strokeWidth,
radius: this.radius,
};
this.bracketLine(ctx, p.x1, p.y1, p.x2, p.y2, config);
// this.bracketLine(ctx, this.x1, this.y1, this.x2, this.y2, config);
},
bracketLine(ctx, x1, y1, x2, y2, config) {
let { color = "#E34F51", lineWidth = 3, radius = 10 } = config;
radius = (y2 - y1) / 5 > radius ? 10 : Math.abs((y2 - y1) / 5);
var z1 = (y2 - y1 - 4 * radius) / 2; //> 0 ? (y2 - y1 - 4 * radius) / 2 : 0;
ctx.beginPath();
ctx.closePath();
ctx.lineWidth = lineWidth;
ctx.strokeStyle = color;
ctx.arc(x1, y1 + radius, radius, 1.5 * Math.PI, 2 * Math.PI);
ctx.stroke();
if (y2 > y1 + radius) {
ctx.strokeStyle = color;
ctx.lineWidth = lineWidth;
ctx.beginPath();
ctx.moveTo(x1 + radius, y1 + radius);
var topLineTo = z1 > 0 ? z1 : 0;
ctx.lineTo(x1 + radius, y1 + radius + topLineTo);
ctx.stroke();
ctx.beginPath();
ctx.lineWidth = lineWidth;
ctx.strokeStyle = color;
ctx.arc(
x1 + 2 * radius,
y1 + topLineTo + radius,
radius,
0.5 * Math.PI,
1 * Math.PI
);
ctx.stroke();
ctx.beginPath();
ctx.lineWidth = lineWidth;
ctx.strokeStyle = color;
ctx.arc(
x1 + 2 * radius,
y1 + topLineTo + 3 * radius,
radius,
1 * Math.PI,
1.5 * Math.PI
);
ctx.stroke();
ctx.strokeStyle = color;
ctx.lineWidth = lineWidth;
ctx.beginPath();
ctx.moveTo(x1 + radius, y1 + topLineTo + 3 * radius);
ctx.lineTo(x1 + radius, y1 + 2 * topLineTo + 3 * radius);
ctx.stroke();
ctx.beginPath();
ctx.lineWidth = lineWidth;
ctx.strokeStyle = color;
ctx.arc(
x1,
y1 + 2 * topLineTo + 3 * radius,
radius,
0 * Math.PI,
0.5 * Math.PI
);
ctx.stroke();
}
},
});
fabric.BracketLine.ATTRIBUTE_NAMES = fabric.SHARED_ATTRIBUTES.concat(
"x1 y1 x2 y2".split(" ")
);
fabric.BracketLine.fromElement = function(element, callback) {
var parsedAttributes = fabric.parseAttributes(
element,
fabric.BracketLine.ATTRIBUTE_NAMES
);
parsedAttributes.left =
(parsedAttributes.left || 0) - parsedAttributes.rx;
parsedAttributes.top =
(parsedAttributes.top || 0) - parsedAttributes.ry;
callback(new fabric.BracketLine(parsedAttributes));
};
/* _FROM_SVG_END_ */
/**
* Returns {@link fabric.BracketLine} instance from an object representation
* @static
* @memberOf fabric.Ellipse
* @param {Object} object Object to create an instance from
* @param {function} [callback] invoked with new instance as first argument
* @return {fabric.Ellipse}
*/
fabric.BracketLine.fromObject = function(object, callback) {
function _callback(instance) {
delete instance.points;
callback && callback(instance);
}
var options = clone(object, true);
options.points = [object.x1, object.y1, object.x2, object.y2];
fabric.Object._fromObject("BracketLine", options, _callback, "points");
};
})( true ? exports : undefined);
/***/ }),
/***/ "63a0":
/***/ (function(module, exports, __webpack_require__) {
// extracted by mini-css-extract-plugin
/***/ }),
/***/ "7173":
/***/ (function(module, exports, __webpack_require__) {
// extracted by mini-css-extract-plugin
/***/ }),
/***/ "7a94":
/***/ (function(module, exports, __webpack_require__) {
/* WEBPACK VAR INJECTION */(function(Buffer) {/* 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: '4.2.0' };
if (true) {
exports.fabric = fabric;
}
/* _AMD_START_ */
else {}
/* _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 = __webpack_require__(0);
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 = __webpack_require__(1).implForWrapper;
fabric.nodeCanvas = __webpack_require__(2).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.commaWsp = '(?:\\s+,?\\s*|,\\s*)'
fabric.rePathCommand = /([-+]?((\d+\.\d+)|((\d+)|(\.\d+)))(?:[eE][-+]?\d+)?)/ig;
fabric.reNonWord = /[ \n\.,;!\?\-]/;
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
* @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 on(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
* @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 off(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
* @memberOf fabric.Observable
* @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 = {
fire: fire,
on: on,
off: off,
};
})();
/**
* @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
*/
_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 {
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
* @retu