UNPKG

sg-vhuman

Version:

简单的数字人虚拟形象组件

1,238 lines (1,237 loc) 801 kB
import { ref as vt, onMounted as Pt, withDirectives as Et, createElementBlock as Ct, openBlock as St, normalizeStyle as kt, createElementVNode as bt, createCommentVNode as Dt, vShow as At } from "vue"; class Mt { constructor() { this.peerConnection = null, this.remoteStream = null; } /** * 初始化 WebRTC 连接 * @param {Object} configuration - WebRTC 配置项,包含 ICE 服务器等信息 * @returns {Promise<boolean>} 初始化是否成功 */ async initializeConnection(f = { iceServers: [ { urls: "stun:stun.l.google.com:19302" } ] }) { try { return this.peerConnection = new RTCPeerConnection(f), this.peerConnection.addTransceiver("video", { direction: "recvonly" }), this.peerConnection.addTransceiver("audio", { direction: "recvonly" }), this.setupPeerConnectionListeners(), !0; } catch (st) { return console.error("Failed to initialize WebRTC connection:", st), !1; } } /** * 设置 WebRTC 连接的事件监听器 * 包括 ICE 候选者和远程流接收的处理 */ setupPeerConnectionListeners() { this.peerConnection.onicecandidate = (f) => { f.candidate && this.onIceCandidate(f.candidate); }, this.peerConnection.ontrack = (f) => { this.remoteStream = f.streams[0], this.onRemoteStreamReceived(this.remoteStream); }; } /** * 创建并设置本地 SDP offer * @returns {Promise<RTCSessionDescription>} 本地 SDP 描述 */ async createOffer() { try { const f = await this.peerConnection.createOffer(); return await this.peerConnection.setLocalDescription(f), this.peerConnection.localDescription; } catch (f) { throw console.error("Error creating offer:", f), f; } } /** * 处理远程 SDP answer * @param {RTCSessionDescriptionInit} answer - 远程 SDP 答复 */ async handleAnswer(f) { try { await this.peerConnection.setRemoteDescription(new RTCSessionDescription(f)); } catch (st) { throw console.error("Error handling answer:", st), st; } } // 回调函数接口 onIceCandidate(f) { } onRemoteStreamReceived(f) { } /** * 关闭并清理 WebRTC 连接 */ closeConnection() { this.peerConnection && this.peerConnection.close(), this.remoteStream = null, this.peerConnection = null; } } function Ft(q) { if (Object.prototype.hasOwnProperty.call(q, "__esModule")) return q; var f = q.default; if (typeof f == "function") { var st = function rt() { return this instanceof rt ? Reflect.construct(f, arguments, this.constructor) : f.apply(this, arguments); }; st.prototype = f.prototype; } else st = {}; return Object.defineProperty(st, "__esModule", { value: !0 }), Object.keys(q).forEach(function(rt) { var at = Object.getOwnPropertyDescriptor(q, rt); Object.defineProperty(st, rt, at.get ? at : { enumerable: !0, get: function() { return q[rt]; } }); }), st; } var pt = {}; const Lt = {}, It = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ __proto__: null, default: Lt }, Symbol.toStringTag, { value: "Module" })), _t = /* @__PURE__ */ Ft(It); var xt; function jt() { return xt || (xt = 1, function(q) { /*! Fabric.js Copyright 2008-2015, Printio (Juriy Zaytsev, Maxim Chernyak) */ var f = f || { version: "5.3.0" }; if (q.fabric = f, typeof document < "u" && typeof window < "u") document instanceof (typeof HTMLDocument < "u" ? HTMLDocument : Document) ? f.document = document : f.document = document.implementation.createHTMLDocument(""), f.window = window; else { var st = _t, rt = new st.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; f.document = rt.document, f.jsdomImplForWrapper = _t.implForWrapper, f.nodeCanvas = _t.Canvas, f.window = rt, DOMParser = f.window.DOMParser; } f.isTouchSupported = "ontouchstart" in f.window || "ontouchstart" in f.document || f.window && f.window.navigator && f.window.navigator.maxTouchPoints > 0, f.isLikelyNode = typeof Buffer < "u" && typeof window > "u", f.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" ], f.DPI = 96, f.reNum = "(?:[-+]?(?:\\d+|\\d*\\.\\d+)(?:[eE][-+]?\\d+)?)", f.commaWsp = "(?:\\s+,?\\s*|,\\s*)", f.rePathCommand = /([-+]?((\d+\.\d+)|((\d+)|(\.\d+)))(?:[eE][-+]?\d+)?)/ig, f.reNonWord = /[ \n\.,;!\?\-]/, f.fontPaths = {}, f.iMatrix = [1, 0, 0, 1, 0, 0], f.svgNS = "http://www.w3.org/2000/svg", f.perfLimitSizeTotal = 2097152, f.maxCacheSideLimit = 4096, f.minCacheSideLimit = 256, f.charWidthsCache = {}, f.textureSize = 2048, f.disableStyleCopyPaste = !1, f.enableGLFiltering = !0, f.devicePixelRatio = f.window.devicePixelRatio || f.window.webkitDevicePixelRatio || f.window.mozDevicePixelRatio || 1, f.browserShadowBlurConstant = 1, f.arcToSegmentsCache = {}, f.boundsOfCurveCache = {}, f.cachesBoundsOfCurve = !0, f.forceGLPutImageData = !1, f.initFilterBackend = function() { if (f.enableGLFiltering && f.isWebglSupported && f.isWebglSupported(f.textureSize)) return console.log("max texture size: " + f.maxTextureSize), new f.WebglFilterBackend({ tileSize: f.textureSize }); if (f.Canvas2dFilterBackend) return new f.Canvas2dFilterBackend(); }, typeof document < "u" && typeof window < "u" && (window.fabric = f), function() { function c(t, n) { if (this.__eventListeners[t]) { var a = this.__eventListeners[t]; n ? a[a.indexOf(n)] = !1 : f.util.array.fill(a, !1); } } function s(t, n) { if (this.__eventListeners || (this.__eventListeners = {}), arguments.length === 1) for (var a in t) this.on(a, t[a]); else this.__eventListeners[t] || (this.__eventListeners[t] = []), this.__eventListeners[t].push(n); return this; } function h(t, n) { var a = (function() { n.apply(this, arguments), this.off(t, a); }).bind(this); this.on(t, a); } function o(t, n) { if (arguments.length === 1) for (var a in t) h.call(this, a, t[a]); else h.call(this, t, n); return this; } function e(t, n) { if (!this.__eventListeners) return this; if (arguments.length === 0) for (t in this.__eventListeners) c.call(this, t); else if (arguments.length === 1 && typeof arguments[0] == "object") for (var a in t) c.call(this, a, t[a]); else c.call(this, t, n); return this; } function r(t, n) { if (!this.__eventListeners) return this; var a = this.__eventListeners[t]; if (!a) return this; for (var i = 0, l = a.length; i < l; i++) a[i] && a[i].call(this, n || {}); return this.__eventListeners[t] = a.filter(function(u) { return u !== !1; }), this; } f.Observable = { fire: r, on: s, once: o, off: e }; }(), f.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() { if (this._objects.push.apply(this._objects, arguments), this._onObjectAdded) for (var c = 0, s = arguments.length; c < s; c++) this._onObjectAdded(arguments[c]); return this.renderOnAddRemove && this.requestRenderAll(), 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(c, s, h) { var o = this._objects; return h ? o[s] = c : o.splice(s, 0, c), this._onObjectAdded && this._onObjectAdded(c), this.renderOnAddRemove && this.requestRenderAll(), 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() { for (var c = this._objects, s, h = !1, o = 0, e = arguments.length; o < e; o++) s = c.indexOf(arguments[o]), s !== -1 && (h = !0, c.splice(s, 1), this._onObjectRemoved && this._onObjectRemoved(arguments[o])); return this.renderOnAddRemove && h && this.requestRenderAll(), 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(c, s) { for (var h = this.getObjects(), o = 0, e = h.length; o < e; o++) c.call(s, h[o], o, h); 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(c) { return typeof c > "u" ? this._objects.concat() : this._objects.filter(function(s) { return s.type === c; }); }, /** * Returns object at specified index * @param {Number} index * @return {Self} thisArg */ item: function(c) { return this._objects[c]; }, /** * 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 * @param {Boolean} [deep=false] `true` to check all descendants, `false` to check only `_objects` * @return {Boolean} `true` if collection contains an object */ contains: function(c, s) { return this._objects.indexOf(c) > -1 ? !0 : s ? this._objects.some(function(h) { return typeof h.contains == "function" && h.contains(c, !0); }) : !1; }, /** * Returns number representation of a collection complexity * @return {Number} complexity */ complexity: function() { return this._objects.reduce(function(c, s) { return c += s.complexity ? s.complexity() : 0, c; }, 0); } }, f.CommonMethods = { /** * Sets object's properties from options * @param {Object} [options] Options object */ _setOptions: function(c) { for (var s in c) this.set(s, c[s]); }, /** * @private * @param {Object} [filler] Options object * @param {String} [property] property to set the Gradient to */ _initGradient: function(c, s) { c && c.colorStops && !(c instanceof f.Gradient) && this.set(s, new f.Gradient(c)); }, /** * @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(c, s, h) { c && c.source && !(c instanceof f.Pattern) ? this.set(s, new f.Pattern(c, h)) : h && h(); }, /** * @private */ _setObject: function(c) { for (var s in c) this._set(s, c[s]); }, /** * 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(c, s) { return typeof c == "object" ? this._setObject(c) : this._set(c, s), this; }, _set: function(c, s) { this[c] = s; }, /** * 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(c) { var s = this.get(c); return typeof s == "boolean" && this.set(c, !s), this; }, /** * Basic getter * @param {String} property Property name * @return {*} value of a property */ get: function(c) { return this[c]; } }, function(c) { var s = Math.sqrt, h = Math.atan2, o = Math.pow, e = Math.PI / 180, r = Math.PI / 2; f.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(t) { if (t === 0) return 1; t < 0 && (t = -t); var n = t / r; switch (n) { case 1: case 3: return 0; case 2: return -1; } return Math.cos(t); }, /** * 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(t) { if (t === 0) return 0; var n = t / r, a = 1; switch (t < 0 && (a = -1), n) { case 1: return a; case 2: return 0; case 3: return -a; } return Math.sin(t); }, /** * 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(t, n) { var a = t.indexOf(n); return a !== -1 && t.splice(a, 1), t; }, /** * 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(t, n) { return Math.floor(Math.random() * (n - t + 1)) + t; }, /** * Transforms degrees to radians. * @static * @memberOf fabric.util * @param {Number} degrees value in degrees * @return {Number} value in radians */ degreesToRadians: function(t) { return t * e; }, /** * Transforms radians to degrees. * @static * @memberOf fabric.util * @param {Number} radians value in radians * @return {Number} value in degrees */ radiansToDegrees: function(t) { return t / e; }, /** * 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(t, n, a) { var i = new f.Point(t.x - n.x, t.y - n.y), l = f.util.rotateVector(i, a); return new f.Point(l.x, l.y).addEquals(n); }, /** * 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(t, n) { var a = f.util.sin(n), i = f.util.cos(n), l = t.x * i - t.y * a, u = t.x * a + t.y * i; return { x: l, y: u }; }, /** * Creates a vetor from points represented as a point * @static * @memberOf fabric.util * * @typedef {Object} Point * @property {number} x * @property {number} y * * @param {Point} from * @param {Point} to * @returns {Point} vector */ createVector: function(t, n) { return new f.Point(n.x - t.x, n.y - t.y); }, /** * Calculates angle between 2 vectors using dot product * @static * @memberOf fabric.util * @param {Point} a * @param {Point} b * @returns the angle in radian between the vectors */ calcAngleBetweenVectors: function(t, n) { return Math.acos((t.x * n.x + t.y * n.y) / (Math.hypot(t.x, t.y) * Math.hypot(n.x, n.y))); }, /** * @static * @memberOf fabric.util * @param {Point} v * @returns {Point} vector representing the unit vector of pointing to the direction of `v` */ getHatVector: function(t) { return new f.Point(t.x, t.y).multiply(1 / Math.hypot(t.x, t.y)); }, /** * @static * @memberOf fabric.util * @param {Point} A * @param {Point} B * @param {Point} C * @returns {{ vector: Point, angle: number }} vector representing the bisector of A and A's angle */ getBisector: function(t, n, a) { var i = f.util.createVector(t, n), l = f.util.createVector(t, a), u = f.util.calcAngleBetweenVectors(i, l), d = f.util.calcAngleBetweenVectors(f.util.rotateVector(i, u), l), g = u * (d === 0 ? 1 : -1) / 2; return { vector: f.util.getHatVector(f.util.rotateVector(i, g)), angle: u }; }, /** * Project stroke width on points returning 2 projections for each point as follows: * - `miter`: 2 points corresponding to the outer boundary and the inner boundary of stroke. * - `bevel`: 2 points corresponding to the bevel boundaries, tangent to the bisector. * - `round`: same as `bevel` * Used to calculate object's bounding box * @static * @memberOf fabric.util * @param {Point[]} points * @param {Object} options * @param {number} options.strokeWidth * @param {'miter'|'bevel'|'round'} options.strokeLineJoin * @param {number} options.strokeMiterLimit https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/stroke-miterlimit * @param {boolean} options.strokeUniform * @param {number} options.scaleX * @param {number} options.scaleY * @param {boolean} [openPath] whether the shape is open or not, affects the calculations of the first and last points * @returns {fabric.Point[]} array of size 2n/4n of all suspected points */ projectStrokeOnPoints: function(t, n, a) { var i = [], l = n.strokeWidth / 2, u = n.strokeUniform ? new f.Point(1 / n.scaleX, 1 / n.scaleY) : new f.Point(1, 1), d = function(g) { var m = l / Math.hypot(g.x, g.y); return new f.Point(g.x * m * u.x, g.y * m * u.y); }; return t.length <= 1 || t.forEach(function(g, m) { var v = new f.Point(g.x, g.y), y, w; m === 0 ? (w = t[m + 1], y = a ? d(f.util.createVector(w, v)).addEquals(v) : t[t.length - 1]) : m === t.length - 1 ? (y = t[m - 1], w = a ? d(f.util.createVector(y, v)).addEquals(v) : t[0]) : (y = t[m - 1], w = t[m + 1]); var M = f.util.getBisector(v, y, w), X = M.vector, W = M.angle, G, N; if (n.strokeLineJoin === "miter" && (G = -l / Math.sin(W / 2), N = new f.Point( X.x * G * u.x, X.y * G * u.y ), Math.hypot(N.x, N.y) / l <= n.strokeMiterLimit)) { i.push(v.add(N)), i.push(v.subtract(N)); return; } G = -l * Math.SQRT2, N = new f.Point( X.x * G * u.x, X.y * G * u.y ), i.push(v.add(N)), i.push(v.subtract(N)); }), i; }, /** * 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(t, n, a) { return a ? new f.Point( n[0] * t.x + n[2] * t.y, n[1] * t.x + n[3] * t.y ) : new f.Point( n[0] * t.x + n[2] * t.y + n[4], n[1] * t.x + n[3] * t.y + n[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(t, n) { if (n) for (var a = 0; a < t.length; a++) t[a] = f.util.transformPoint(t[a], n); var i = [t[0].x, t[1].x, t[2].x, t[3].x], l = f.util.array.min(i), u = f.util.array.max(i), d = u - l, g = [t[0].y, t[1].y, t[2].y, t[3].y], m = f.util.array.min(g), v = f.util.array.max(g), y = v - m; return { left: l, top: m, width: d, height: y }; }, /** * Invert transformation t * @static * @memberOf fabric.util * @param {Array} t The transform * @return {Array} The inverted transform */ invertTransform: function(t) { var n = 1 / (t[0] * t[3] - t[1] * t[2]), a = [n * t[3], -n * t[1], -n * t[2], n * t[0]], i = f.util.transformPoint({ x: t[4], y: t[5] }, a, !0); return a[4] = -i.x, a[5] = -i.y, a; }, /** * 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(t, n) { return parseFloat(Number(t).toFixed(n)); }, /** * 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(t, n) { var a = /\D{0,2}$/.exec(t), i = parseFloat(t); switch (n || (n = f.Text.DEFAULT_SVG_FONT_SIZE), a[0]) { case "mm": return i * f.DPI / 25.4; case "cm": return i * f.DPI / 2.54; case "in": return i * f.DPI; case "pt": return i * f.DPI / 72; // or * 4 / 3 case "pc": return i * f.DPI / 72 * 12; // or * 16 case "em": return i * n; default: return i; } }, /** * Function which always returns `false`. * @static * @memberOf fabric.util * @return {Boolean} */ falseFunction: function() { return !1; }, /** * 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(t, n) { return t = f.util.string.camelize(t.charAt(0).toUpperCase() + t.slice(1)), f.util.resolveNamespace(n)[t]; }, /** * 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(t) { var n = [ "instantiated_by_use", "style", "id", "class" ]; switch (t) { case "linearGradient": n = n.concat(["x1", "y1", "x2", "y2", "gradientUnits", "gradientTransform"]); break; case "radialGradient": n = n.concat(["gradientUnits", "gradientTransform", "cx", "cy", "r", "fx", "fy", "fr"]); break; case "stop": n = n.concat(["offset", "stop-color", "stop-opacity"]); break; } return n; }, /** * 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(t) { if (!t) return f; var n = t.split("."), a = n.length, i, l = c || f.window; for (i = 0; i < a; ++i) l = l[n[i]]; return l; }, /** * 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(t, n, a, i) { if (!t) { n && n.call(a, t); return; } var l = f.util.createImage(), u = function() { n && n.call(a, l, !1), l = l.onload = l.onerror = null; }; l.onload = u, l.onerror = function() { f.log("Error loading " + l.src), n && n.call(a, null, !0), l = l.onload = l.onerror = null; }, t.indexOf("data") !== 0 && i !== void 0 && i !== null && (l.crossOrigin = i), t.substring(0, 14) === "data:image/svg" && (l.onload = null, f.util.loadImageInDom(l, u)), l.src = t; }, /** * 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(t, n) { var a = f.document.createElement("div"); a.style.width = a.style.height = "1px", a.style.left = a.style.top = "-100%", a.style.position = "absolute", a.appendChild(t), f.document.querySelector("body").appendChild(a), t.onload = function() { n(), a.parentNode.removeChild(a), a = 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(t, n, a, i) { t = t || []; var l = [], u = 0, d = t.length; function g() { ++u === d && n && n(l.filter(function(m) { return m; })); } if (!d) { n && n(l); return; } t.forEach(function(m, v) { if (!m || !m.type) { g(); return; } var y = f.util.getKlass(m.type, a); y.fromObject(m, function(w, M) { M || (l[v] = w), i && i(m, w, M), g(); }); }); }, /** * Creates corresponding fabric instances residing in an object, e.g. `clipPath` * @see {@link fabric.Object.ENLIVEN_PROPS} * @param {Object} object * @param {Object} [context] assign enlived props to this object (pass null to skip this) * @param {(objects:fabric.Object[]) => void} callback */ enlivenObjectEnlivables: function(t, n, a) { var i = f.Object.ENLIVEN_PROPS.filter(function(l) { return !!t[l]; }); f.util.enlivenObjects(i.map(function(l) { return t[l]; }), function(l) { var u = {}; i.forEach(function(d, g) { u[d] = l[g], n && (n[d] = l[g]); }), a && a(u); }); }, /** * 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(t, n) { t = t || []; function a() { ++l === u && n && n(i); } var i = [], l = 0, u = t.length; if (!u) { n && n(i); return; } t.forEach(function(d, g) { d && d.source ? new f.Pattern(d, function(m) { i[g] = m, a(); }) : (i[g] = d, a()); }); }, /** * 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(t, n, a) { var i; return t && t.length === 1 ? (typeof a < "u" && (t[0].sourcePath = a), t[0]) : (n && (n.width && n.height ? n.centerPoint = { x: n.width / 2, y: n.height / 2 } : (delete n.width, delete n.height)), i = new f.Group(t, n), typeof a < "u" && (i.sourcePath = a), i); }, /** * 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(t, n, a) { if (a && Array.isArray(a)) for (var i = 0, l = a.length; i < l; i++) a[i] in t && (n[a[i]] = t[a[i]]); }, /** * Creates canvas element * @static * @memberOf fabric.util * @return {CanvasElement} initialized canvas element */ createCanvasElement: function() { return f.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(t) { var n = f.util.createCanvasElement(); return n.width = t.width, n.height = t.height, n.getContext("2d").drawImage(t, 0, 0), n; }, /** * 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(t, n, a) { return t.toDataURL("image/" + n, a); }, /** * Creates image element (works on client and node) * @static * @memberOf fabric.util * @return {HTMLImageElement} HTML image element */ createImage: function() { return f.document.createElement("img"); }, /** * 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 * @return {Array} The product of the two transform matrices */ multiplyTransformMatrices: function(t, n, a) { return [ t[0] * n[0] + t[2] * n[1], t[1] * n[0] + t[3] * n[1], t[0] * n[2] + t[2] * n[3], t[1] * n[2] + t[3] * n[3], a ? 0 : t[0] * n[4] + t[2] * n[5] + t[4], a ? 0 : t[1] * n[4] + t[3] * n[5] + t[5] ]; }, /** * Decomposes standard 2x3 matrix into transform components * @static * @memberOf fabric.util * @param {Array} a transformMatrix * @return {Object} Components of transform */ qrDecompose: function(t) { var n = h(t[1], t[0]), a = o(t[0], 2) + o(t[1], 2), i = s(a), l = (t[0] * t[3] - t[2] * t[1]) / i, u = h(t[0] * t[2] + t[1] * t[3], a); return { angle: n / e, scaleX: i, scaleY: l, skewX: u / e, skewY: 0, translateX: t[4], translateY: t[5] }; }, /** * Returns a transform matrix starting from an object of the same kind of * the one returned from qrDecompose, useful also if you want to calculate some * transformations from an object that is not enlived yet * @static * @memberOf fabric.util * @param {Object} options * @param {Number} [options.angle] angle in degrees * @return {Number[]} transform matrix */ calcRotateMatrix: function(t) { if (!t.angle) return f.iMatrix.concat(); var n = f.util.degreesToRadians(t.angle), a = f.util.cos(n), i = f.util.sin(n); return [a, i, -i, a, 0, 0]; }, /** * Returns a transform matrix starting from an object of the same kind of * the one returned from qrDecompose, useful also if you want to calculate some * transformations from an object that is not enlived yet. * is called DimensionsTransformMatrix because those properties are the one that influence * the size of the resulting box of the object. * @static * @memberOf fabric.util * @param {Object} options * @param {Number} [options.scaleX] * @param {Number} [options.scaleY] * @param {Boolean} [options.flipX] * @param {Boolean} [options.flipY] * @param {Number} [options.skewX] * @param {Number} [options.skewY] * @return {Number[]} transform matrix */ calcDimensionsMatrix: function(t) { var n = typeof t.scaleX > "u" ? 1 : t.scaleX, a = typeof t.scaleY > "u" ? 1 : t.scaleY, i = [ t.flipX ? -n : n, 0, 0, t.flipY ? -a : a, 0, 0 ], l = f.util.multiplyTransformMatrices, u = f.util.degreesToRadians; return t.skewX && (i = l( i, [1, 0, Math.tan(u(t.skewX)), 1], !0 )), t.skewY && (i = l( i, [1, Math.tan(u(t.skewY)), 0, 1], !0 )), i; }, /** * Returns a transform matrix starting from an object of the same kind of * the one returned from qrDecompose, useful also if you want to calculate some * transformations from an object that is not enlived yet * @static * @memberOf fabric.util * @param {Object} options * @param {Number} [options.angle] * @param {Number} [options.scaleX] * @param {Number} [options.scaleY] * @param {Boolean} [options.flipX] * @param {Boolean} [options.flipY] * @param {Number} [options.skewX] * @param {Number} [options.skewX] * @param {Number} [options.translateX] * @param {Number} [options.translateY] * @return {Number[]} transform matrix */ composeMatrix: function(t) { var n = [1, 0, 0, 1, t.translateX || 0, t.translateY || 0], a = f.util.multiplyTransformMatrices; return t.angle && (n = a(n, f.util.calcRotateMatrix(t))), (t.scaleX !== 1 || t.scaleY !== 1 || t.skewX || t.skewY || t.flipX || t.flipY) && (n = a(n, f.util.calcDimensionsMatrix(t))), n; }, /** * reset an object transform state to neutral. Top and left are not accounted for * @static * @memberOf fabric.util * @param {fabric.Object} target object to transform */ resetObjectTransform: function(t) { t.scaleX = 1, t.scaleY = 1, t.skewX = 0, t.skewY = 0, t.flipX = !1, t.flipY = !1, t.rotate(0); }, /** * Extract Object transform values * @static * @memberOf fabric.util * @param {fabric.Object} target object to read from * @return {Object} Components of transform */ saveObjectTransform: function(t) { return { scaleX: t.scaleX, scaleY: t.scaleY, skewX: t.skewX, skewY: t.skewY, angle: t.angle, left: t.left, flipX: t.flipX, flipY: t.flipY, top: t.top }; }, /** * Returns true if context has transparent pixel * at specified location (taking tolerance into account) * @param {CanvasRenderingContext2D} ctx context * @param {Number} x x coordinate * @param {Number} y y coordinate * @param {Number} tolerance Tolerance */ isTransparent: function(t, n, a, i) { i > 0 && (n > i ? n -= i : n = 0, a > i ? a -= i : a = 0); var l = !0, u, d, g = t.getImageData(n, a, i * 2 || 1, i * 2 || 1), m = g.data.length; for (u = 3; u < m && (d = g.data[u], l = d <= 0, l !== !1); u += 4) ; return g = null, l; }, /** * Parse preserveAspectRatio attribute from element * @param {string} attribute to be parsed * @return {Object} an object containing align and meetOrSlice attribute */ parsePreserveAspectRatioAttribute: function(t) { var n = "meet", a = "Mid", i = "Mid", l = t.split(" "), u; return l && l.length && (n = l.pop(), n !== "meet" && n !== "slice" ? (u = n, n = "meet") : l.length && (u = l.pop())), a = u !== "none" ? u.slice(1, 4) : "none", i = u !== "none" ? u.slice(5, 8) : "none", { meetOrSlice: n, alignX: a, alignY: i }; }, /** * Clear char widths cache for the given font family or all the cache if no * fontFamily is specified. * Use it if you know you are loading fonts in a lazy way and you are not waiting * for custom fonts to load properly when adding text objects to the canvas. * If a text object is added when its own font is not loaded yet, you will get wrong * measurement and so wrong bounding boxes. * After the font cache is cleared, either change the textObject text content or call * initDimensions() to trigger a recalculation * @memberOf fabric.util * @param {String} [fontFamily] font family to clear */ clearFabricFontCache: function(t) { t = (t || "").toLowerCase(), t ? f.charWidthsCache[t] && delete f.charWidthsCache[t] : f.charWidthsCache = {}; }, /** * Given current aspect ratio, determines the max width and height that can * respect the total allowed area for the cache. * @memberOf fabric.util * @param {Number} ar aspect ratio * @param {Number} maximumArea Maximum area you want to achieve * @return {Object.x} Limited dimensions by X * @return {Object.y} Limited dimensions by Y */ limitDimsByArea: function(t, n) { var a = Math.sqrt(n * t), i = Math.floor(n / a); return { x: Math.floor(a), y: i }; }, capValue: function(t, n, a) { return Math.max(t, Math.min(n, a)); }, /** * Finds the scale for the object source to fit inside the object destination, * keeping aspect ratio intact. * respect the total allowed area for the cache. * @memberOf fabric.util * @param {Object | fabric.Object} source * @param {Number} source.height natural unscaled height of the object * @param {Number} source.width natural unscaled width of the object * @param {Object | fabric.Object} destination * @param {Number} destination.height natural unscaled height of the object * @param {Number} destination.width natural unscaled width of the object * @return {Number} scale factor to apply to source to fit into destination */ findScaleToFit: function(t, n) { return Math.min(n.width / t.width, n.height / t.height); }, /** * Finds the scale for the object source to cover entirely the object destination, * keeping aspect ratio intact. * respect the total allowed area for the cache. * @memberOf fabric.util * @param {Object | fabric.Object} source * @param {Number} source.height natural unscaled height of the object * @param {Number} source.width natural unscaled width of the object * @param {Object | fabric.Object} destination * @param {Number} destination.height natural unscaled height of the object * @param {Number} destination.width natural unscaled width of the object * @return {Number} scale factor to apply to source to cover destination */ findScaleToCover: function(t, n) { return Math.max(n.width / t.width, n.height / t.height); }, /** * given an array of 6 number returns something like `"matrix(...numbers)"` * @memberOf fabric.util * @param {Array} transform an array with 6 numbers * @return {String} transform matrix for svg * @return {Object.y} Limited dimensions by Y */ matrixToSVG: function(t) { return "matrix(" + t.map(function(n) { return f.util.toFixed(n, f.Object.NUM_FRACTION_DIGITS); }).join(" ") + ")"; }, /** * given an object and a transform, apply the inverse transform to the object, * this is equivalent to remove from that object that transformation, so that * added in a space with the removed transform, the object will be the same as before. * Removing from an object a transform that scale by 2 is like scaling it by 1/2. * Removing from an object a transfrom that rotate by 30deg is like rotating by 30deg * in the opposite direction. * This util is used to add objects inside transformed groups or nested groups. * @memberOf fabric.util * @param {fabric.Object} object the object you want to transform * @param {Array} transform the destination transform */ removeTransformFromObject: function(t, n) { var a = f.util.invertTransform(n), i = f.util.multiplyTransformMatrices(a, t.calcOwnMatrix()); f.util.applyTransformToObject(t, i); }, /** * given an object and a transform, apply the transform to the object. * this is equivalent to change the space where the object is drawn. * Adding to an object a transform that scale by 2 is like scaling it by 2. * This is used when removing an object from an active selection for example. * @memberOf fabric.util * @param {fabric.Object} object the object you want to transform * @param {Array} transform the destination transform */ addTransformToObject: function(t, n) { f.util.applyTransformToObject( t, f.util.multiplyTransformMatrices(n, t.calcOwnMatrix()) ); }, /** * discard an object transform state and apply the one from the matrix. * @memberOf fabric.util * @param {fabric.Object} object the object you want to transform * @param {Array} transform the destination transform */ applyTransformToObject: function(t, n) { var a = f.util.qrDecompose(n), i = new f.Point(a.translateX, a.translateY); t.flipX = !1, t.flipY = !1, t.set("scaleX", a.scaleX), t.set("scaleY", a.scaleY), t.skewX = a.skewX, t.skewY = a.skewY, t.angle = a.angle, t.setPositionByOrigin(i, "center", "center"); }, /** * given a width and height, return the size of the bounding box * that can contains the box with width/height with applied transform * described in options. * Use to calculate the boxes around objects for controls. * @memberOf fabric.util * @param {Number} width * @param {Number} height * @param {Object} options * @param {Number} options.scaleX * @param {Number} options.scaleY * @param {Number} options.skewX * @param {Number} options.skewY * @return {Object.x} width of containing * @return {Object.y} height of containing */ sizeAfterTransform: function(t, n, a) { var i = t / 2, l = n / 2, u = [ { x: -i, y: -l }, { x: i, y: -l }, { x: -i, y: l }, { x: i, y: l } ], d = f.util.calcDimensionsMatrix(a), g = f.util.makeBoundingBoxFromPoints(u, d); return { x: g.width, y: g.height }; }, /**