diff --git a/CHANGELOG.md b/CHANGELOG.md index 8c225e87..8443da2d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). ## Unreleased * Add `onUpdate` callbacks to `Konva.Tween` configuration and `node.to()` method. +* Up to 6x faster initializations of objects, like `const shape = new Konva.Shape()`. ## 7.0.3 - 2020-07-09 diff --git a/konva.js b/konva.js index 4a5ab1a2..1a9cc377 100644 --- a/konva.js +++ b/konva.js @@ -8,7 +8,7 @@ * Konva JavaScript Framework v7.0.3 * http://konvajs.org/ * Licensed under the MIT - * Date: Thu Jul 09 2020 + * Date: Thu Jul 30 2020 * * Original work Copyright (C) 2011 - 2013 by Eric Rowell (KineticJS) * Modified work Copyright (C) 2014 - present by Anton Lavrenov (Konva) @@ -2641,7 +2641,6 @@ */ var Node = /** @class */ (function () { function Node(config) { - var _this = this; this._id = idCounter++; this.eventListeners = {}; this.attrs = {}; @@ -2656,25 +2655,12 @@ this._isUnderCache = false; this.children = emptyChildren; this._dragEventId = null; + this._shouldFireChangeEvents = false; + // on initial set attrs wi don't need to fire change events + // because nobody is listening to them yet this.setAttrs(config); - // event bindings for cache handling - this.on(TRANSFORM_CHANGE_STR, function () { - if (_this._batchingTransformChange) { - _this._needClearTransformCache = true; - return; - } - _this._clearCache(TRANSFORM); - _this._clearSelfAndDescendantCache(ABSOLUTE_TRANSFORM); - }); - this.on('visibleChange.konva', function () { - _this._clearSelfAndDescendantCache(VISIBLE); - }); - this.on('listeningChange.konva', function () { - _this._clearSelfAndDescendantCache(LISTENING); - }); - this.on('opacityChange.konva', function () { - _this._clearSelfAndDescendantCache(ABSOLUTE_OPACITY); - }); + this._shouldFireChangeEvents = true; + // all change event listeners are attached to the prototype } Node.prototype.hasChildren = function () { return false; @@ -4425,7 +4411,7 @@ } return this; }; - Node.prototype._setAttr = function (key, val) { + Node.prototype._setAttr = function (key, val, skipFire) { var oldVal = this.attrs[key]; if (oldVal === val && !Util.isObject(val)) { return; @@ -4436,7 +4422,9 @@ else { this.attrs[key] = val; } - this._fireChangeEvent(key, oldVal, val); + if (this._shouldFireChangeEvents) { + this._fireChangeEvent(key, oldVal, val); + } }; Node.prototype._setComponentAttr = function (key, component, val) { var oldVal; @@ -4480,9 +4468,29 @@ } } }; + Node.prototype._getListeners = function (eventType) { + var totalEvents = []; + var obj; + while (true) { + obj = obj ? Object.getPrototypeOf(obj) : this; + if (!obj) { + break; + } + if (!obj.eventListeners) { + continue; + } + var events = obj.eventListeners[eventType]; + if (!events) { + continue; + } + totalEvents = events.concat(totalEvents); + obj = Object.getPrototypeOf(obj); + } + return totalEvents; + }; Node.prototype._fire = function (eventType, evt) { - var events = this.eventListeners[eventType], i; - if (events) { + var events = this._getListeners(eventType), i; + if (events.length) { evt = evt || {}; evt.currentTarget = this; evt.type = eventType; @@ -4699,6 +4707,26 @@ }()); Node.prototype.nodeType = 'Node'; Node.prototype._attrsAffectingSize = []; + // attache events listeners once into prototype + // that way we don't spend too much time on making an new instance + Node.prototype.eventListeners = {}; + Node.prototype.on.call(Node.prototype, TRANSFORM_CHANGE_STR, function () { + if (this._batchingTransformChange) { + this._needClearTransformCache = true; + return; + } + this._clearCache(TRANSFORM); + this._clearSelfAndDescendantCache(ABSOLUTE_TRANSFORM); + }); + Node.prototype.on.call(Node.prototype, 'visibleChange.konva', function () { + this._clearSelfAndDescendantCache(VISIBLE); + }); + Node.prototype.on.call(Node.prototype, 'listeningChange.konva', function () { + this._clearSelfAndDescendantCache(LISTENING); + }); + Node.prototype.on.call(Node.prototype, 'opacityChange.konva', function () { + this._clearSelfAndDescendantCache(ABSOLUTE_OPACITY); + }); var addGetterSetter = Factory.addGetterSetter; /** * get/set zIndex relative to the node's siblings who share the same parent. @@ -6881,11 +6909,6 @@ } _this.colorKey = key; shapes[key] = _this; - _this.on('shadowColorChange.konva shadowBlurChange.konva shadowOffsetChange.konva shadowOpacityChange.konva shadowEnabledChange.konva', _clearHasShadowCache); - _this.on('shadowColorChange.konva shadowOpacityChange.konva shadowEnabledChange.konva', _clearGetShadowRGBACache); - _this.on('fillPriorityChange.konva fillPatternImageChange.konva fillPatternRepeatChange.konva fillPatternScaleXChange.konva fillPatternScaleYChange.konva', _clearFillPatternCache); - _this.on('fillPriorityChange.konva fillLinearGradientColorStopsChange.konva fillLinearGradientStartPointXChange.konva fillLinearGradientStartPointYChange.konva fillLinearGradientEndPointXChange.konva fillLinearGradientEndPointYChange.konva', _clearLinearGradientCache); - _this.on('fillPriorityChange.konva fillRadialGradientColorStopsChange.konva fillRadialGradientStartPointXChange.konva fillRadialGradientStartPointYChange.konva fillRadialGradientEndPointXChange.konva fillRadialGradientEndPointYChange.konva fillRadialGradientStartRadiusChange.konva fillRadialGradientEndRadiusChange.konva', _clearRadialGradientCache); return _this; } /** @@ -7343,6 +7366,12 @@ Shape.prototype._centroid = false; Shape.prototype.nodeType = 'Shape'; _registerNode(Shape); + Shape.prototype.eventListeners = {}; + Shape.prototype.on.call(Shape.prototype, 'shadowColorChange.konva shadowBlurChange.konva shadowOffsetChange.konva shadowOpacityChange.konva shadowEnabledChange.konva', _clearHasShadowCache); + Shape.prototype.on.call(Shape.prototype, 'shadowColorChange.konva shadowOpacityChange.konva shadowEnabledChange.konva', _clearGetShadowRGBACache); + Shape.prototype.on.call(Shape.prototype, 'fillPriorityChange.konva fillPatternImageChange.konva fillPatternRepeatChange.konva fillPatternScaleXChange.konva fillPatternScaleYChange.konva', _clearFillPatternCache); + Shape.prototype.on.call(Shape.prototype, 'fillPriorityChange.konva fillLinearGradientColorStopsChange.konva fillLinearGradientStartPointXChange.konva fillLinearGradientStartPointYChange.konva fillLinearGradientEndPointXChange.konva fillLinearGradientEndPointYChange.konva', _clearLinearGradientCache); + Shape.prototype.on.call(Shape.prototype, 'fillPriorityChange.konva fillRadialGradientColorStopsChange.konva fillRadialGradientStartPointXChange.konva fillRadialGradientStartPointYChange.konva fillRadialGradientEndPointXChange.konva fillRadialGradientEndPointYChange.konva fillRadialGradientStartRadiusChange.konva fillRadialGradientEndRadiusChange.konva', _clearRadialGradientCache); // add getters and setters Factory.addGetterSetter(Shape, 'stroke', undefined, getStringValidator()); /** @@ -9137,7 +9166,7 @@ duration: 1, easing: 1, onFinish: 1, - yoyo: 1 + yoyo: 1, }, PAUSED = 1, PLAYING = 2, REVERSING = 3, idCounter$1 = 0, colorAttrs = ['fill', 'stroke', 'shadowColor']; var TweenEngine = /** @class */ (function () { function TweenEngine(prop, propFunc, func, begin, finish, duration, yoyo) { @@ -9234,6 +9263,7 @@ }; TweenEngine.prototype.update = function () { this.setPosition(this.getPosition(this._time)); + this.fire('onUpdate'); }; TweenEngine.prototype.onEnterFrame = function () { var t = this.getTimer() - this._startTime; @@ -9262,10 +9292,15 @@ * @example * // instantiate new tween which fully rotates a node in 1 second * var tween = new Konva.Tween({ + * // list of tween specific properties * node: node, - * rotationDeg: 360, * duration: 1, - * easing: Konva.Easings.EaseInOut + * easing: Konva.Easings.EaseInOut, + * onUpdate: () => console.log('node attrs updated') + * onFinish: () => console.log('finished'), + * // set new values for any attributes of a passed node + * rotation: 360, + * fill: 'red' * }); * * // play tween @@ -9321,6 +9356,7 @@ // callbacks this.onFinish = config.onFinish; this.onReset = config.onReset; + this.onUpdate = config.onUpdate; } Tween.prototype._addAttr = function (key, end) { var node = this.node, nodeId = node._id, start, diff, tweenId, n, len, trueEnd, trueStart, endRGBA; @@ -9361,7 +9397,7 @@ r: endRGBA.r - startRGBA.r, g: endRGBA.g - startRGBA.g, b: endRGBA.b - startRGBA.b, - a: endRGBA.a - startRGBA.a + a: endRGBA.a - startRGBA.a, }); } } @@ -9379,7 +9415,7 @@ r: endRGBA.r - start.r, g: endRGBA.g - start.g, b: endRGBA.b - start.b, - a: endRGBA.a - start.a + a: endRGBA.a - start.a, }; } else { @@ -9390,7 +9426,7 @@ diff: diff, end: end, trueEnd: trueEnd, - trueStart: trueStart + trueStart: trueStart, }; Tween.tweens[nodeId][key] = this._id; }; @@ -9481,6 +9517,11 @@ _this.onReset(); } }; + this.tween.onUpdate = function () { + if (_this.onUpdate) { + _this.onUpdate.call(_this); + } + }; }; /** * play @@ -9570,9 +9611,8 @@ * circle.to({ * x : 50, * duration : 0.5, - * onFinish: () => { - * console.log('finished'); - * } + * onUpdate: () => console.log('props updated'), + * onFinish: () => console.log('finished'), * }); */ Node.prototype.to = function (params) { @@ -9821,7 +9861,7 @@ */ Linear: function (t, b, c, d) { return (c * t) / d + b; - } + }, }; // what is core parts of Konva? diff --git a/konva.min.js b/konva.min.js index 484b7936..4a7ddbd6 100644 --- a/konva.min.js +++ b/konva.min.js @@ -3,10 +3,10 @@ * Konva JavaScript Framework v7.0.3 * http://konvajs.org/ * Licensed under the MIT - * Date: Thu Jul 09 2020 + * Date: Thu Jul 30 2020 * * Original work Copyright (C) 2011 - 2013 by Eric Rowell (KineticJS) * Modified work Copyright (C) 2014 - present by Anton Lavrenov (Konva) * * @license - */var e=Math.PI/180;function t(t){var e=t.toLowerCase(),i=/(chrome)[ /]([\w.]+)/.exec(e)||/(webkit)[ /]([\w.]+)/.exec(e)||/(opera)(?:.*version|)[ /]([\w.]+)/.exec(e)||/(msie) ([\w.]+)/.exec(e)||e.indexOf("compatible")<0&&/(mozilla)(?:.*? rv:([\w.]+)|)/.exec(e)||[],n=!!t.match(/Android|BlackBerry|iPhone|iPad|iPod|Opera Mini|IEMobile/i),r=!!t.match(/IEMobile/i);return{browser:i[1]||"",version:i[2]||"0",isIE:function(t){var e=t.indexOf("msie ");if(0>16&255,g:e>>8&255,b:255&e}},getRandomColor:function(){for(var t=(16777215*Math.random()<<0).toString(16);t.length<6;)t="0"+t;return"#"+t},get:function(t,e){return void 0===t?e:t},getRGB:function(t){var e;return t in d?{r:(e=d[t])[0],g:e[1],b:e[2]}:"#"===t[0]?this._hexToRgb(t.substring(1)):"rgb("===t.substr(0,4)?(e=u.exec(t.replace(/ /g,"")),{r:parseInt(e[1],10),g:parseInt(e[2],10),b:parseInt(e[3],10)}):{r:0,g:0,b:0}},colorToRGBA:function(t){return t=t||"black",A._namedColorToRBA(t)||A._hex3ColorToRGBA(t)||A._hex6ColorToRGBA(t)||A._rgbColorToRGBA(t)||A._rgbaColorToRGBA(t)||A._hslColorToRGBA(t)},_namedColorToRBA:function(t){var e=d[t.toLowerCase()];return e?{r:e[0],g:e[1],b:e[2],a:1}:null},_rgbColorToRGBA:function(t){if(0===t.indexOf("rgb(")){var e=(t=t.match(/rgb\(([^)]+)\)/)[1]).split(/ *, */).map(Number);return{r:e[0],g:e[1],b:e[2],a:1}}},_rgbaColorToRGBA:function(t){if(0===t.indexOf("rgba(")){var e=(t=t.match(/rgba\(([^)]+)\)/)[1]).split(/ *, */).map(Number);return{r:e[0],g:e[1],b:e[2],a:e[3]}}},_hex6ColorToRGBA:function(t){if("#"===t[0]&&7===t.length)return{r:parseInt(t.slice(1,3),16),g:parseInt(t.slice(3,5),16),b:parseInt(t.slice(5,7),16),a:1}},_hex3ColorToRGBA:function(t){if("#"===t[0]&&4===t.length)return{r:parseInt(t[1]+t[1],16),g:parseInt(t[2]+t[2],16),b:parseInt(t[3]+t[3],16),a:1}},_hslColorToRGBA:function(t){if(/hsl\((\d+),\s*([\d.]+)%,\s*([\d.]+)%\)/g.test(t)){var e=/hsl\((\d+),\s*([\d.]+)%,\s*([\d.]+)%\)/g.exec(t),i=(e[0],e.slice(1)),n=Number(i[0])/360,r=Number(i[1])/100,o=Number(i[2])/100,a=void 0,s=void 0,h=void 0;if(0==r)return h=255*o,{r:Math.round(h),g:Math.round(h),b:Math.round(h),a:1};for(var l=2*o-(a=o<.5?o*(1+r):o+r-o*r),c=[0,0,0],d=0;d<3;d++)(s=n+1/3*-(d-1))<0&&s++,1t.x+t.width||e.x+e.widtht.y+t.height||e.y+e.heighte.length&&(r=e,e=t,t=r),n=0;n=this.parent.children.length)&&A.warn("Unexpected value "+t+" for zIndex property. zIndex is just index of a node in children of its parent. Expected value is from 0 to "+(this.parent.children.length-1)+".");var e=this.index;return this.parent.children.splice(e,1),this.parent.children.splice(t,0,this),this.parent._setChildrenIndices(),this},dt.prototype.getAbsoluteOpacity=function(){return this._getCache(Z,this._getAbsoluteOpacity)},dt.prototype._getAbsoluteOpacity=function(){var t=this.opacity(),e=this.getParent();return e&&!e._isUnderCache&&(t*=e.getAbsoluteOpacity()),t},dt.prototype.moveTo=function(t){return this.getParent()!==t&&(this._remove(),t.add(this)),this},dt.prototype.toObject=function(){var t,e,i,n={},r=this.getAttrs();for(t in n.attrs={},r)e=r[t],A.isObject(e)&&!A._isPlainObject(e)&&!A._isArray(e)||(i="function"==typeof this[t]&&this[t],delete r[t],(i?i.call(this):null)!==(r[t]=e)&&(n.attrs[t]=e));return n.className=this.getClassName(),A._prepareToStringify(n)},dt.prototype.toJSON=function(){return JSON.stringify(this.toObject())},dt.prototype.getParent=function(){return this.parent},dt.prototype.findAncestors=function(t,e,i){var n=[];e&&this._isMatch(t)&&n.push(this);for(var r=this.parent;r;){if(r===i)return n;r._isMatch(t)&&n.push(r),r=r.parent}return n},dt.prototype.isAncestorOf=function(t){return!1},dt.prototype.findAncestor=function(t,e,i){return this.findAncestors(t,e,i)[0]},dt.prototype._isMatch=function(t){if(!t)return!1;if("function"==typeof t)return t(this);for(var e,i=t.replace(/ /g,"").split(","),n=i.length,r=0;rthis.duration?this.yoyo?(this._time=this.duration,this.reverse()):this.finish():t<0?this.yoyo?(this._time=0,this.play()):this.reset():(this._time=t,this.update())},Ae.prototype.getTime=function(){return this._time},Ae.prototype.setPosition=function(t){this.prevPos=this._pos,this.propFunc(t),this._pos=t},Ae.prototype.getPosition=function(t){return void 0===t&&(t=this._time),this.func(t,this.begin,this._change,this.duration)},Ae.prototype.play=function(){this.state=2,this._startTime=this.getTimer()-this._time,this.onEnterFrame(),this.fire("onPlay")},Ae.prototype.reverse=function(){this.state=3,this._time=this.duration-this._time,this._startTime=this.getTimer()-this._time,this.onEnterFrame(),this.fire("onReverse")},Ae.prototype.seek=function(t){this.pause(),this._time=t,this.update(),this.fire("onSeek")},Ae.prototype.reset=function(){this.pause(),this._time=0,this.update(),this.fire("onReset")},Ae.prototype.finish=function(){this.pause(),this._time=this.duration,this.update(),this.fire("onFinish")},Ae.prototype.update=function(){this.setPosition(this.getPosition(this._time))},Ae.prototype.onEnterFrame=function(){var t=this.getTimer()-this._startTime;2===this.state?this.setTime(t):3===this.state&&this.setTime(this.duration-t)},Ae.prototype.pause=function(){this.state=1,this.fire("onPause")},Ae.prototype.getTimer=function(){return(new Date).getTime()},Ae);function Ae(t,e,i,n,r,o,a){this.prop=t,this.propFunc=e,this.begin=n,this._pos=n,this.duration=o,this._change=0,this.prevPos=0,this.yoyo=a,this._time=0,this._position=0,this._startTime=0,this._finish=0,this.func=i,this._change=r-this.begin,this.pause()}var Me=(Ge.prototype._addAttr=function(t,e){var i,n,r,o,a,s,h,l,c=this.node,d=c._id,p=Ge.tweens[d][t];if(p&&delete Ge.attrs[d][p][t],i=c.getAttr(t),A._isArray(e))if(n=[],o=Math.max(e.length,i.length),"points"===t&&e.length!==i.length&&(e.length>i.length?(s=i,i=A._prepareArrayForTween(i,e,c.closed())):(a=e,e=A._prepareArrayForTween(e,i,c.closed()))),0===t.indexOf("fill"))for(r=0;rthis.dataArray[i].pathLength;)t-=this.dataArray[i].pathLength,++i;if(i===n)return{x:(e=this.dataArray[i-1].points.slice(-2))[0],y:e[1]};if(t<.01)return{x:(e=this.dataArray[i].points.slice(0,2))[0],y:e[1]};var r=this.dataArray[i],o=r.points;switch(r.command){case"L":return ci.getPointOnLine(t,r.start.x,r.start.y,o[0],o[1]);case"C":return ci.getPointOnCubicBezier(t/r.pathLength,r.start.x,r.start.y,o[0],o[1],o[2],o[3],o[4],o[5]);case"Q":return ci.getPointOnQuadraticBezier(t/r.pathLength,r.start.x,r.start.y,o[0],o[1],o[2],o[3]);case"A":var a=o[0],s=o[1],h=o[2],l=o[3],c=o[4],d=o[5],p=o[6];return c+=d*t/r.pathLength,ci.getPointOnEllipticalArc(a,s,h,l,c,p)}return null},ci.getLineLength=function(t,e,i,n){return Math.sqrt((i-t)*(i-t)+(n-e)*(n-e))},ci.getPointOnLine=function(t,e,i,n,r,o,a){void 0===o&&(o=e),void 0===a&&(a=i);var s=(r-i)/(n-e+1e-8),h=Math.sqrt(t*t/(1+s*s));n>>1,A=_.slice(0,1+T),M=this._getTextWidth(A)+v;M<=l?(w=1+T,P=A+(g?"…":""),k=M):C=T}if(!P)break;if(!f||0<(S=(" "===(x=_[P.length])||"-"===x)&&k<=l?P.length:Math.max(P.lastIndexOf(" "),P.lastIndexOf("-"))+1)&&(w=S,P=P.slice(0,w),k=this._getTextWidth(P)),P=P.trimRight(),this._addTextLine(P),i=Math.max(i,k),d+=n,!u||s&&ce?g=li.getPointOnLine(e,f.x,f.y,v.points[0],v.points[1],f.x,f.y):v=void 0;break;case"A":var a=v.points[4],s=v.points[5],h=v.points[4]+s;0===m?m=a+1e-8:iv.pathLength?1e-8:e/v.pathLength:il.x?-1:1,d=this.findOne(".top-left").y()>l.y?-1:1,w=s*this.cos*c,C=s*this.sin*d,this.findOne(".top-left").x(l.x-w),this.findOne(".top-left").y(l.y-C)):"top-center"===this._movingAnchorName?this.findOne(".top-left").y(e.y()):"top-right"===this._movingAnchorName?(a&&(l=p?{x:this.width()/2,y:this.height()/2}:{x:this.findOne(".bottom-left").x(),y:this.findOne(".bottom-left").y()},s=Math.sqrt(Math.pow(e.x()-l.x,2)+Math.pow(l.y-e.y(),2)),c=this.findOne(".top-right").x()l.y?-1:1,w=s*this.cos*c,C=s*this.sin*d,this.findOne(".top-right").x(l.x+w),this.findOne(".top-right").y(l.y-C)),h=e.position(),this.findOne(".top-left").y(h.y),this.findOne(".bottom-right").x(h.x)):"middle-left"===this._movingAnchorName?this.findOne(".top-left").x(e.x()):"middle-right"===this._movingAnchorName?this.findOne(".bottom-right").x(e.x()):"bottom-left"===this._movingAnchorName?(a&&(l=p?{x:this.width()/2,y:this.height()/2}:{x:this.findOne(".top-right").x(),y:this.findOne(".top-right").y()},s=Math.sqrt(Math.pow(l.x-e.x(),2)+Math.pow(e.y()-l.y,2)),c=l.x>N,0!==w?(w=255/w,P[a]=(h*B>>N)*w,P[a+1]=(l*B>>N)*w,P[a+2]=(c*B>>N)*w):P[a]=P[a+1]=P[a+2]=0,h-=p,l-=u,c-=f,d-=g,p-=O.r,u-=O.g,f-=O.b,g-=O.a,r=s+((r=i+e+1)>N,0>N)*w,P[r+1]=(l*B>>N)*w,P[r+2]=(c*B>>N)*w):P[r]=P[r+1]=P[r+2]=0,h-=p,l-=u,c-=f,d-=g,p-=O.r,u-=O.g,f-=O.b,g-=O.a,r=i+((r=n+R)>16&255,g:e>>8&255,b:255&e}},getRandomColor:function(){for(var t=(16777215*Math.random()<<0).toString(16);t.length<6;)t="0"+t;return"#"+t},get:function(t,e){return void 0===t?e:t},getRGB:function(t){var e;return t in d?{r:(e=d[t])[0],g:e[1],b:e[2]}:"#"===t[0]?this._hexToRgb(t.substring(1)):"rgb("===t.substr(0,4)?(e=u.exec(t.replace(/ /g,"")),{r:parseInt(e[1],10),g:parseInt(e[2],10),b:parseInt(e[3],10)}):{r:0,g:0,b:0}},colorToRGBA:function(t){return t=t||"black",A._namedColorToRBA(t)||A._hex3ColorToRGBA(t)||A._hex6ColorToRGBA(t)||A._rgbColorToRGBA(t)||A._rgbaColorToRGBA(t)||A._hslColorToRGBA(t)},_namedColorToRBA:function(t){var e=d[t.toLowerCase()];return e?{r:e[0],g:e[1],b:e[2],a:1}:null},_rgbColorToRGBA:function(t){if(0===t.indexOf("rgb(")){var e=(t=t.match(/rgb\(([^)]+)\)/)[1]).split(/ *, */).map(Number);return{r:e[0],g:e[1],b:e[2],a:1}}},_rgbaColorToRGBA:function(t){if(0===t.indexOf("rgba(")){var e=(t=t.match(/rgba\(([^)]+)\)/)[1]).split(/ *, */).map(Number);return{r:e[0],g:e[1],b:e[2],a:e[3]}}},_hex6ColorToRGBA:function(t){if("#"===t[0]&&7===t.length)return{r:parseInt(t.slice(1,3),16),g:parseInt(t.slice(3,5),16),b:parseInt(t.slice(5,7),16),a:1}},_hex3ColorToRGBA:function(t){if("#"===t[0]&&4===t.length)return{r:parseInt(t[1]+t[1],16),g:parseInt(t[2]+t[2],16),b:parseInt(t[3]+t[3],16),a:1}},_hslColorToRGBA:function(t){if(/hsl\((\d+),\s*([\d.]+)%,\s*([\d.]+)%\)/g.test(t)){var e=/hsl\((\d+),\s*([\d.]+)%,\s*([\d.]+)%\)/g.exec(t),i=(e[0],e.slice(1)),n=Number(i[0])/360,r=Number(i[1])/100,o=Number(i[2])/100,a=void 0,s=void 0,h=void 0;if(0==r)return h=255*o,{r:Math.round(h),g:Math.round(h),b:Math.round(h),a:1};for(var l=2*o-(a=o<.5?o*(1+r):o+r-o*r),c=[0,0,0],d=0;d<3;d++)(s=n+1/3*-(d-1))<0&&s++,1t.x+t.width||e.x+e.widtht.y+t.height||e.y+e.heighte.length&&(r=e,e=t,t=r),n=0;n=this.parent.children.length)&&A.warn("Unexpected value "+t+" for zIndex property. zIndex is just index of a node in children of its parent. Expected value is from 0 to "+(this.parent.children.length-1)+".");var e=this.index;return this.parent.children.splice(e,1),this.parent.children.splice(t,0,this),this.parent._setChildrenIndices(),this},dt.prototype.getAbsoluteOpacity=function(){return this._getCache(Z,this._getAbsoluteOpacity)},dt.prototype._getAbsoluteOpacity=function(){var t=this.opacity(),e=this.getParent();return e&&!e._isUnderCache&&(t*=e.getAbsoluteOpacity()),t},dt.prototype.moveTo=function(t){return this.getParent()!==t&&(this._remove(),t.add(this)),this},dt.prototype.toObject=function(){var t,e,i,n={},r=this.getAttrs();for(t in n.attrs={},r)e=r[t],A.isObject(e)&&!A._isPlainObject(e)&&!A._isArray(e)||(i="function"==typeof this[t]&&this[t],delete r[t],(i?i.call(this):null)!==(r[t]=e)&&(n.attrs[t]=e));return n.className=this.getClassName(),A._prepareToStringify(n)},dt.prototype.toJSON=function(){return JSON.stringify(this.toObject())},dt.prototype.getParent=function(){return this.parent},dt.prototype.findAncestors=function(t,e,i){var n=[];e&&this._isMatch(t)&&n.push(this);for(var r=this.parent;r;){if(r===i)return n;r._isMatch(t)&&n.push(r),r=r.parent}return n},dt.prototype.isAncestorOf=function(t){return!1},dt.prototype.findAncestor=function(t,e,i){return this.findAncestors(t,e,i)[0]},dt.prototype._isMatch=function(t){if(!t)return!1;if("function"==typeof t)return t(this);for(var e,i=t.replace(/ /g,"").split(","),n=i.length,r=0;rthis.duration?this.yoyo?(this._time=this.duration,this.reverse()):this.finish():t<0?this.yoyo?(this._time=0,this.play()):this.reset():(this._time=t,this.update())},we.prototype.getTime=function(){return this._time},we.prototype.setPosition=function(t){this.prevPos=this._pos,this.propFunc(t),this._pos=t},we.prototype.getPosition=function(t){return void 0===t&&(t=this._time),this.func(t,this.begin,this._change,this.duration)},we.prototype.play=function(){this.state=2,this._startTime=this.getTimer()-this._time,this.onEnterFrame(),this.fire("onPlay")},we.prototype.reverse=function(){this.state=3,this._time=this.duration-this._time,this._startTime=this.getTimer()-this._time,this.onEnterFrame(),this.fire("onReverse")},we.prototype.seek=function(t){this.pause(),this._time=t,this.update(),this.fire("onSeek")},we.prototype.reset=function(){this.pause(),this._time=0,this.update(),this.fire("onReset")},we.prototype.finish=function(){this.pause(),this._time=this.duration,this.update(),this.fire("onFinish")},we.prototype.update=function(){this.setPosition(this.getPosition(this._time)),this.fire("onUpdate")},we.prototype.onEnterFrame=function(){var t=this.getTimer()-this._startTime;2===this.state?this.setTime(t):3===this.state&&this.setTime(this.duration-t)},we.prototype.pause=function(){this.state=1,this.fire("onPause")},we.prototype.getTimer=function(){return(new Date).getTime()},we);function we(t,e,i,n,r,o,a){this.prop=t,this.propFunc=e,this.begin=n,this._pos=n,this.duration=o,this._change=0,this.prevPos=0,this.yoyo=a,this._time=0,this._position=0,this._startTime=0,this._finish=0,this.func=i,this._change=r-this.begin,this.pause()}var Ce=(Pe.prototype._addAttr=function(t,e){var i,n,r,o,a,s,h,l,c=this.node,d=c._id,p=Pe.tweens[d][t];if(p&&delete Pe.attrs[d][p][t],i=c.getAttr(t),A._isArray(e))if(n=[],o=Math.max(e.length,i.length),"points"===t&&e.length!==i.length&&(e.length>i.length?(s=i,i=A._prepareArrayForTween(i,e,c.closed())):(a=e,e=A._prepareArrayForTween(e,i,c.closed()))),0===t.indexOf("fill"))for(r=0;rthis.dataArray[i].pathLength;)t-=this.dataArray[i].pathLength,++i;if(i===n)return{x:(e=this.dataArray[i-1].points.slice(-2))[0],y:e[1]};if(t<.01)return{x:(e=this.dataArray[i].points.slice(0,2))[0],y:e[1]};var r=this.dataArray[i],o=r.points;switch(r.command){case"L":return oi.getPointOnLine(t,r.start.x,r.start.y,o[0],o[1]);case"C":return oi.getPointOnCubicBezier(t/r.pathLength,r.start.x,r.start.y,o[0],o[1],o[2],o[3],o[4],o[5]);case"Q":return oi.getPointOnQuadraticBezier(t/r.pathLength,r.start.x,r.start.y,o[0],o[1],o[2],o[3]);case"A":var a=o[0],s=o[1],h=o[2],l=o[3],c=o[4],d=o[5],p=o[6];return c+=d*t/r.pathLength,oi.getPointOnEllipticalArc(a,s,h,l,c,p)}return null},oi.getLineLength=function(t,e,i,n){return Math.sqrt((i-t)*(i-t)+(n-e)*(n-e))},oi.getPointOnLine=function(t,e,i,n,r,o,a){void 0===o&&(o=e),void 0===a&&(a=i);var s=(r-i)/(n-e+1e-8),h=Math.sqrt(t*t/(1+s*s));n>>1,A=_.slice(0,1+T),M=this._getTextWidth(A)+y;M<=l?(w=1+T,P=A+(g?"…":""),k=M):C=T}if(!P)break;if(!f||0<(S=(" "===(x=_[P.length])||"-"===x)&&k<=l?P.length:Math.max(P.lastIndexOf(" "),P.lastIndexOf("-"))+1)&&(w=S,P=P.slice(0,w),k=this._getTextWidth(P)),P=P.trimRight(),this._addTextLine(P),i=Math.max(i,k),d+=n,!u||s&&ce?g=ri.getPointOnLine(e,f.x,f.y,y.points[0],y.points[1],f.x,f.y):y=void 0;break;case"A":var a=y.points[4],s=y.points[5],h=y.points[4]+s;0===m?m=a+1e-8:iy.pathLength?1e-8:e/y.pathLength:il.x?-1:1,d=this.findOne(".top-left").y()>l.y?-1:1,w=s*this.cos*c,C=s*this.sin*d,this.findOne(".top-left").x(l.x-w),this.findOne(".top-left").y(l.y-C)):"top-center"===this._movingAnchorName?this.findOne(".top-left").y(e.y()):"top-right"===this._movingAnchorName?(a&&(l=p?{x:this.width()/2,y:this.height()/2}:{x:this.findOne(".bottom-left").x(),y:this.findOne(".bottom-left").y()},s=Math.sqrt(Math.pow(e.x()-l.x,2)+Math.pow(l.y-e.y(),2)),c=this.findOne(".top-right").x()l.y?-1:1,w=s*this.cos*c,C=s*this.sin*d,this.findOne(".top-right").x(l.x+w),this.findOne(".top-right").y(l.y-C)),h=e.position(),this.findOne(".top-left").y(h.y),this.findOne(".bottom-right").x(h.x)):"middle-left"===this._movingAnchorName?this.findOne(".top-left").x(e.x()):"middle-right"===this._movingAnchorName?this.findOne(".bottom-right").x(e.x()):"bottom-left"===this._movingAnchorName?(a&&(l=p?{x:this.width()/2,y:this.height()/2}:{x:this.findOne(".top-right").x(),y:this.findOne(".top-right").y()},s=Math.sqrt(Math.pow(l.x-e.x(),2)+Math.pow(e.y()-l.y,2)),c=l.x>N,0!==w?(w=255/w,P[a]=(h*B>>N)*w,P[a+1]=(l*B>>N)*w,P[a+2]=(c*B>>N)*w):P[a]=P[a+1]=P[a+2]=0,h-=p,l-=u,c-=f,d-=g,p-=O.r,u-=O.g,f-=O.b,g-=O.a,r=s+((r=i+e+1)>N,0>N)*w,P[r+1]=(l*B>>N)*w,P[r+2]=(c*B>>N)*w):P[r]=P[r+1]=P[r+2]=0,h-=p,l-=u,c-=f,d-=g,p-=O.r,u-=O.g,f-=O.b,g-=O.a,r=i+((r=n+R) { children = emptyChildren; nodeType!: string; className!: string; + _dragEventId: number | null = null; + _shouldFireChangeEvents = false; constructor(config?: Config) { + // on initial set attrs wi don't need to fire change events + // because nobody is listening to them yet this.setAttrs(config); + this._shouldFireChangeEvents = true; - // event bindings for cache handling - this.on(TRANSFORM_CHANGE_STR, () => { - if (this._batchingTransformChange) { - this._needClearTransformCache = true; - return; - } - this._clearCache(TRANSFORM); - this._clearSelfAndDescendantCache(ABSOLUTE_TRANSFORM); - }); - - this.on('visibleChange.konva', () => { - this._clearSelfAndDescendantCache(VISIBLE); - }); - this.on('listeningChange.konva', () => { - this._clearSelfAndDescendantCache(LISTENING); - }); - this.on('opacityChange.konva', () => { - this._clearSelfAndDescendantCache(ABSOLUTE_OPACITY); - }); + // all change event listeners are attached to the prototype } hasChildren() { @@ -2218,7 +2205,7 @@ export abstract class Node { } return this; } - _setAttr(key, val) { + _setAttr(key, val, skipFire = false) { var oldVal = this.attrs[key]; if (oldVal === val && !Util.isObject(val)) { return; @@ -2228,7 +2215,9 @@ export abstract class Node { } else { this.attrs[key] = val; } - this._fireChangeEvent(key, oldVal, val); + if (this._shouldFireChangeEvents) { + this._fireChangeEvent(key, oldVal, val); + } } _setComponentAttr(key, component, val) { var oldVal; @@ -2280,11 +2269,32 @@ export abstract class Node { } } } + + _getListeners(eventType) { + let totalEvents = []; + let obj; + while (true) { + obj = obj ? Object.getPrototypeOf(obj) : this; + if (!obj) { + break; + } + if (!obj.eventListeners) { + continue; + } + const events = obj.eventListeners[eventType]; + if (!events) { + continue; + } + totalEvents = events.concat(totalEvents); + obj = Object.getPrototypeOf(obj); + } + return totalEvents; + } _fire(eventType, evt) { - var events = this.eventListeners[eventType], + var events = this._getListeners(eventType), i; - if (events) { + if (events.length) { evt = evt || {}; evt.currentTarget = this; evt.type = eventType; @@ -2604,6 +2614,28 @@ export abstract class Node { Node.prototype.nodeType = 'Node'; Node.prototype._attrsAffectingSize = []; +// attache events listeners once into prototype +// that way we don't spend too much time on making an new instance +Node.prototype.eventListeners = {}; +Node.prototype.on.call(Node.prototype, TRANSFORM_CHANGE_STR, function () { + if (this._batchingTransformChange) { + this._needClearTransformCache = true; + return; + } + this._clearCache(TRANSFORM); + this._clearSelfAndDescendantCache(ABSOLUTE_TRANSFORM); +}); + +Node.prototype.on.call(Node.prototype, 'visibleChange.konva', function () { + this._clearSelfAndDescendantCache(VISIBLE); +}); +Node.prototype.on.call(Node.prototype, 'listeningChange.konva', function () { + this._clearSelfAndDescendantCache(LISTENING); +}); +Node.prototype.on.call(Node.prototype, 'opacityChange.konva', function () { + this._clearSelfAndDescendantCache(ABSOLUTE_OPACITY); +}); + const addGetterSetter = Factory.addGetterSetter; /** diff --git a/src/Shape.ts b/src/Shape.ts index f969ef7a..c3fbf671 100644 --- a/src/Shape.ts +++ b/src/Shape.ts @@ -182,31 +182,6 @@ export class Shape extends Node< this.colorKey = key; shapes[key] = this; - - this.on( - 'shadowColorChange.konva shadowBlurChange.konva shadowOffsetChange.konva shadowOpacityChange.konva shadowEnabledChange.konva', - _clearHasShadowCache - ); - - this.on( - 'shadowColorChange.konva shadowOpacityChange.konva shadowEnabledChange.konva', - _clearGetShadowRGBACache - ); - - this.on( - 'fillPriorityChange.konva fillPatternImageChange.konva fillPatternRepeatChange.konva fillPatternScaleXChange.konva fillPatternScaleYChange.konva', - _clearFillPatternCache - ); - - this.on( - 'fillPriorityChange.konva fillLinearGradientColorStopsChange.konva fillLinearGradientStartPointXChange.konva fillLinearGradientStartPointYChange.konva fillLinearGradientEndPointXChange.konva fillLinearGradientEndPointYChange.konva', - _clearLinearGradientCache - ); - - this.on( - 'fillPriorityChange.konva fillRadialGradientColorStopsChange.konva fillRadialGradientStartPointXChange.konva fillRadialGradientStartPointYChange.konva fillRadialGradientEndPointXChange.konva fillRadialGradientEndPointYChange.konva fillRadialGradientStartRadiusChange.konva fillRadialGradientEndRadiusChange.konva', - _clearRadialGradientCache - ); } /** @@ -842,6 +817,37 @@ Shape.prototype._centroid = false; Shape.prototype.nodeType = 'Shape'; _registerNode(Shape); +Shape.prototype.eventListeners = {}; +Shape.prototype.on.call( + Shape.prototype, + 'shadowColorChange.konva shadowBlurChange.konva shadowOffsetChange.konva shadowOpacityChange.konva shadowEnabledChange.konva', + _clearHasShadowCache +); + +Shape.prototype.on.call( + Shape.prototype, + 'shadowColorChange.konva shadowOpacityChange.konva shadowEnabledChange.konva', + _clearGetShadowRGBACache +); + +Shape.prototype.on.call( + Shape.prototype, + 'fillPriorityChange.konva fillPatternImageChange.konva fillPatternRepeatChange.konva fillPatternScaleXChange.konva fillPatternScaleYChange.konva', + _clearFillPatternCache +); + +Shape.prototype.on.call( + Shape.prototype, + 'fillPriorityChange.konva fillLinearGradientColorStopsChange.konva fillLinearGradientStartPointXChange.konva fillLinearGradientStartPointYChange.konva fillLinearGradientEndPointXChange.konva fillLinearGradientEndPointYChange.konva', + _clearLinearGradientCache +); + +Shape.prototype.on.call( + Shape.prototype, + 'fillPriorityChange.konva fillRadialGradientColorStopsChange.konva fillRadialGradientStartPointXChange.konva fillRadialGradientStartPointYChange.konva fillRadialGradientEndPointXChange.konva fillRadialGradientEndPointYChange.konva fillRadialGradientStartRadiusChange.konva fillRadialGradientEndRadiusChange.konva', + _clearRadialGradientCache +); + // add getters and setters Factory.addGetterSetter(Shape, 'stroke', undefined, getStringValidator()); diff --git a/test/performance/bunnies.html b/test/performance/bunnies.html index 885f96ee..01294e82 100644 --- a/test/performance/bunnies.html +++ b/test/performance/bunnies.html @@ -10,8 +10,8 @@
- - + + + + + + +