svg-action
Version:
控制SVG拖动,缩放,旋转,转图片等
11 lines (10 loc) • 9.78 kB
JavaScript
(function(p,r){typeof exports=="object"&&typeof module<"u"?module.exports=r():typeof define=="function"&&define.amd?define(r):(p=typeof globalThis<"u"?globalThis:p||self,p["svg-action"]=r())})(this,function(){"use strict";r.defaultOption={threshold:15,deltaThreshold:.1,range:{min:.4,max:100},scale:.25,stepSize:75,keyStepSize:10,mouseScale:!0,mouseMove:!0,mouseDrag:!0,mouseRotate:!0,mouseFlip:!0};let p;function r(e,o){let t=this;if(!e)throw new Error("初始化失败,找不到SVG!");t._svg=e,t.options={...r.defaultOption,...o},t.options.mouseControl=t.getControlElement(),t.options.keyControl=t.getKeyElement(),t._totalDelta=0,t._previouScale=1,t._deg=0,t._init()}r.prototype._init=function(){this._initSvg(),R(),this.buildEvent()},r.prototype.buildEvent=function(){this.eventState||(this.eventState=!0,T.call(this),z.call(this),D.call(this))};function _(e){let o=e||window.event;o.preventDefault&&o.preventDefault(),o.stopPropagation?o.stopPropagation():window.event.cancelBubble=!0}function F(e,o,t,n){}function L(e,o,t){var n=e.createSVGPoint();return n.x=o,n.y=t,n.matrixTransform(e.getScreenCTM().inverse())}function T(){let e=this,o=e.options,t={},n=o.mouseControl;n.addEventListener("mousedown",s,!1),n.addEventListener("touchstart",i,{passive:!1});function s(a){t.start=v(a),e._svg.className.baseVal="move-cursor-grabbing",document.addEventListener("mousemove",l,!1),document.addEventListener("mouseup",d,!1)}function i(a){if(_(a),t.start=v(a),document.addEventListener("touchmove",l,{passive:!1}),document.addEventListener("touchend",d,{passive:!1}),a.touches.length>=2){let h=v(a.touches[0]),c=v(a.touches[1]),f=k(h.x,h.y,c.x,c.y);f=L(e._svg,f.x,f.y),t.doubleStart={fast:h,last:c,cente:f,length:b(h.x,h.y,c.x,c.y)}}}function l(a){if(_(a),a.type=="touchmove"&&a.touches.length>=2){let c=v(a.touches[0]),f=v(a.touches[1]),g=t.doubleStart;g.cente;let E=b(c.x,c.y,f.x,f.y);E>g.length?e._zoom(1,g.cente,.025):e._zoom(-1,g.cente,.025),g.length=E;return}t.position=v(a),t.delta=M(t.position,t.start);let h=!1;!t.dragging&&N(t.delta)>o.threshold&&(t.dragging=!0,h=!0),t.dragging&&(o.mouseRotate&&a.ctrlKey?m(a,h):o.mouseFlip&&a.shiftKey?y(a,h):o.mouseDrag&&u(),t.last=t.position)}function u(a){let h=t.last||t.start,c=M(t.position,h);e.move({dx:c.x,dy:c.y})}function m(a,h){let c=S();h&&(document.body.appendChild(c),c.show=!0);let f=t.last||t.start,g=M(t.position,f),B=(g.x||g.y)<0?-1:1;e.rotate(B),c.style.top=a.clientY+"px",c.style.left=a.clientX+20+"px",c.innerText=e._deg+"°"}function y(a,h){h&&(t.delta.x>=o.threshold?e.flipX():t.delta.x<o.threshold&&e.flipY())}function d(a){e._svg.className.baseVal="move-cursor-grab",t={},document.removeEventListener("mousemove",l),document.removeEventListener("mouseup",d),document.removeEventListener("touchmove",l),document.removeEventListener("touchend",d),S().remove()}e._eventFn={handleMousedown:s,handleTouchstart:i,handleMouseMove:l,handleEnd:d}}function b(e,o,t,n){return Math.hypot(t-e,n-o)}function k(e,o,t,n){let s=(e-t)/2,i=(o-n)/2;return{x:e+(0-s),y:o+(0-i)}}function S(){return p||(p=document.createElement("span"),p.style.position="fixed",p.style.userSelect="none",p)}function z(){let e=this,o=e.options.mouseControl;o.addEventListener("mousewheel",t,!1);function t(i){_(i),e.options.mouseMove&&(i.ctrlKey||i.shiftKey)?n(i):e.options.mouseScale&&s(i)}e._eventFn.handleMousewheel=t;function n(i){let l=e.options.stepSize/100,u;i.shiftKey?u={dx:l*i.deltaY,dy:0}:u={dx:l*i.deltaX,dy:l*i.deltaY},e.move(u)}function s(i){var l=o.getBoundingClientRect(),u={x:i.clientX-l.left,y:i.clientY-l.top};e.zoom(i.wheelDelta<0?"out":"in",u)}}function D(){let e=this;e.options.keyControl.addEventListener("keydown",o);function o(i){let l=K(i);l&&(i.ctrlKey?n(l):i.shiftKey?s(l):l=="center"?e.reset():t(l))}e._eventFn.handleKeydown=o;function t(i){e[i]()}function n(i){switch(i){case"up":e.zoomIn();break;case"left":e.rotate();break;case"down":e.zoomOut();break;case"right":e.rotate(-45);break}}function s(i){switch(i){case"up":case"down":e.flipY();break;case"left":case"right":e.flipX();break}}}function K(e){switch(e.key){case"w":case"ArrowUp":return"up";case"s":case"ArrowDown":return"down";case"a":case"ArrowLeft":return"left";case"d":case"ArrowRight":return"right";case"0":case"`":return"center";default:return null}}r.prototype._initSvg=function(){let e=this._svg;if(e.className.baseVal="move-cursor-grab",e.tagName==="g"){let o=function(t){return t.parentElement.tagName=="svg"?t.parentElement:o(t.parentElement)};this._viewport=e,this._svg=o(this._viewport)||this._viewport}if(e.childNodes.length===1&&e.firstElementChild.tagName==="g")this._viewport=e.firstElementChild;else{let o=document.createElementNS(e.namespaceURI,"g"),t=e.childNodes;for(;t.length!==0;)o.appendChild(t[0]);e.appendChild(o),this._viewport=o}},r.prototype.zoomIn=function(e){e=e||this.options.scale,this._zoom(1,C.call(this),e)},r.prototype.zoomOut=function(e){e=e||this.options.scale,this._zoom(-1,C.call(this),e)},r.prototype.reset=function(){this.zoom("center")},r.prototype.zoom=function(e,o){let t=this;if(!e)return x(t._viewport.getCTM().a,1e3);var n=t.options.scale;if(e==="center")return t._fitViewport(e);if(e==="in")e=1;else if(e==="out")e=-1;else if(typeof e=="number")n=Math.abs(e);else return x(t._viewport.getCTM().a,1e3);if(t._totalDelta+=e,Math.abs(t._totalDelta)>t.options.deltaThreshold){let s=t._zoom(e,o,n);return t._totalDelta=0,s}},r.prototype._zoom=function(e,o,t){let n=this;var s=e>0?1:-1,i=V(n._previouScale),l=Math.round(i/t)*t;l+=t*s;var u=Math.pow(10,l);return P.call(n,A(n.options.range,u),o),this._previouScale=x(n._viewport.getCTM().a,1e3),this._previouScale};function P(e,o){var t=this._svg,n=this._viewport,s=t.createSVGMatrix(),i=t.createSVGPoint(),l,u,m,y,d;m=n.getCTM();var a=m.a;return o?(i.x=o.x,i.y=o.y,l=i,u=l.matrixTransform(m.inverse()),y=s.translate(u.x,u.y).scale(1/a*e).translate(-u.x,-u.y),d=m.multiply(y)):d=s.scale(e),w(this._viewport,{matrix:d}),d}r.prototype._fitViewport=function(e){let o=this,t={height:o._svg.clientHeight,width:o._svg.clientWidth},n=o._viewport.getBBox(),s,i;if(n.x>=0&&n.y>=0&&n.x+n.width<=t.width&&n.y+n.height<=t.height&&!e)i={x:0,y:0,width:Math.max(n.width+n.x,t.width),height:Math.max(n.height+n.y,t.height)};else{s=Math.min(1,t.width/n.width,t.height/n.height);let u=-n.x*s,m=-n.y*s,y=t.width/2-n.width*s/2,d=t.height/2-n.height*s/2;i={x:y>=0?y+u:u,y:d>=0?d+m:m}}let l=o._svg.createSVGMatrix().translate(i.x,i.y).scale(s);return w(o._viewport,{matrix:l}),o._deg=0,o._previouScale=x(o._viewport.getCTM().a,1e3)},r.prototype.move=function(e){let o=this._viewport;var t=o.getCTM();return e&&(e.dx=e.dx||e.x||0,e.dy=e.dy||e.y||0,e=Object.assign({dx:0,dy:0},e),t=this._svg.createSVGMatrix().translate(e.dx,e.dy).multiply(t),w(o,{matrix:t})),{x:t.e,y:t.f}},r.prototype.up=function(e){return e=e||this.options.keyStepSize,this.move({dy:-e})},r.prototype.down=function(e){return e=e||this.options.keyStepSize,this.move({dy:e})},r.prototype.left=function(e){return e=e||this.options.keyStepSize,this.move({dx:-e})},r.prototype.right=function(e){return e=e||this.options.keyStepSize,this.move({dx:e})},r.prototype.rotate=function(e=45){let o=this._viewport.getBBox();w(this._viewport,{rotate:{angle:e,x:o.width/2+o.x,y:o.height/2+o.y}}),this._deg+=e},r.prototype.flipX=function(){let e=this._viewport.getCTM();w(this._viewport,{matrix:e.flipX()})},r.prototype.flipY=function(){let e=this._viewport.getCTM();w(this._viewport,{matrix:e.flipY()})},r.prototype.getControlElement=function(){return this.options.mouseControl||this._svg.parentElement||document},r.prototype.getKeyElement=function(){return this.options.keyControl||document},r.prototype.toImage=function(){let e=this,o=new Image,t=e._svg.getBoundingClientRect();return o.width=t.width,o.height=t.height,o.src="data:image/svg+xml;utf8,"+unescape(e._svg.outerHTML),new Promise(n=>{o.onload=function(){let s=document.createElement("canvas");s.width=t.width,s.height=t.height,s.getContext("2d").drawImage(o,0,0,t.width,t.height),n(s.toDataURL("image/png"))}})},r.prototype.save=function(){this.toImage().then(e=>{var o=document.createElement("a");o.href=e,o.download="svg.png",o.click()})},r.prototype.disableEvent=function(){let e=this,o=e.options,t=e._eventFn,n=o.mouseControl;e.eventState=!1,n.removeEventListener("mousedown",t.handleMousedown),n.removeEventListener("touchstart",t.handleTouchstart),document.removeEventListener("mousemove",t.handleMouseMove),document.removeEventListener("mouseup",t.handleEnd),document.removeEventListener("touchmove",t.handleMouseMove),document.removeEventListener("touchend",t.handleEnd),n.removeEventListener("mousewheel",t.handleMousewheel),o.keyControl.removeEventListener("keydown",t.handleKeydown)};function C(){let e=this._viewport.getBoundingClientRect(),o=this._viewport.getCTM();return{x:o.e+e.width/2,y:o.f+e.height/2}}function w(e,{matrix:o,rotate:t}){o=o||e.getCTM();var n=`matrix(${o.a}, ${o.b}, ${o.c}, ${o.d}, ${o.e}, ${o.f})`;t&&(n+=`, rotate(${t.angle}, ${t.x}, ${t.y})`),e.setAttribute("transform",n)}function A(e,o){return Math.max(e.min,Math.min(e.max,o))}function x(e,o){return Math.round(e*o)/o}function V(e){return Math.log(e)/Math.log(10)}function M(e,o){return{x:e.x-o.x,y:e.y-o.y}}function v(e){return e.pointers&&e.pointers.length&&(e=e.pointers[0]),e.touches&&e.touches.length&&(e=e.touches[0]),e?{x:e.clientX,y:e.clientY}:null}function N(e){return Math.sqrt(Math.pow(e.x,2)+Math.pow(e.y,2))}function R(){if(document.getElementById("SvgAction"))return;let o=`
.move-cursor-grab{
cursor: grab;
cursor: -webkit-grab;
}
.move-cursor-grabbing{
cursor: grabbing;
cursor: -webkit-grabbing;
}
`,t=document.createElement("style");t.id="SvgAction",t.innerText=o,document.head.appendChild(t)}return window.SvgAction=r,r});