-
Notifications
You must be signed in to change notification settings - Fork 9
/
Copy pathsimple-drawing-board.min.js
1 lines (1 loc) · 5.64 KB
/
simple-drawing-board.min.js
1
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).SimpleDrawingBoard={})}(this,(function(t){"use strict";class e{constructor(){this._events={}}on(t,e){const s=this._events;t in s||(s[t]=[]),s[t].push(e)}off(t,e){const s=this._events;if(!(t in s))return;e||(s[t]=[]);const i=s[t].indexOf(e);i>=0&&s[t].splice(i,1)}trigger(t,e){const s=this._events;if(t in s)for(let i=0;i<s[t].length;i++){const n=s[t][i];n.handleEvent?n.handleEvent.call(this,e):n.call(this,e)}}removeAllListeners(){this._events={}}}class s{constructor(t=null){this._past=[],this._present=t,this._future=[]}get value(){return this._present}undo(){if(0===this._past.length)return;const t=this._past.pop();this._future.unshift(this._present),this._present=t}redo(){if(0===this._future.length)return;const t=this._future.shift();this._past.push(this._present),this._present=t}save(t){this._present!==t&&(this._past.push(this._present),this._future.length=0,this._present=t)}clear(){this._past.length=0,this._future.length=0}}function i(){return"ontouchstart"in window.document}function n(t){return"string"==typeof t&&!!t.startsWith("data:image/")}async function o(t){return new Promise(((e,s)=>{const i=new Image;i.onerror=s,i.onload=()=>e(i),i.src=t}))}function r(t,e){return{x:t.x+e.x>>1,y:t.y+e.y>>1}}function a(t,e){let s,n;i()?(s=t.touches[0].pageX,n=t.touches[0].pageY):(s=t.pageX,n=t.pageY);const o=e.getBoundingClientRect(),r=o.left+window.pageXOffset,a=o.top+window.pageYOffset;return{x:(s-r)*(e.width/o.width),y:(n-a)*(e.height/o.height)}}class h{constructor(t){this._$el=t,this._ctx=this._$el.getContext("2d"),this._ctx.lineCap=this._ctx.lineJoin="round",this._isDrawMode=!0,this._isDrawing=!1,this._timer=null,this._coords={old:{x:0,y:0},oldMid:{x:0,y:0},current:{x:0,y:0}},this._ev=new e,this._history=new s(this.toDataURL()),this._bindEvents(),this._drawFrame()}get canvas(){return this._$el}get observer(){return this._ev}get mode(){return this._isDrawMode?"draw":"erase"}setLineSize(t){this._ctx.lineWidth=0|t||1}setLineColor(t){this._ctx.strokeStyle=t}fill(t){const e=this._ctx;e.clearRect(0,0,e.canvas.width,e.canvas.height),e.fillStyle=t,e.fillRect(0,0,e.canvas.width,e.canvas.height),this._saveHistory()}clear(){const t=this._ctx;t.clearRect(0,0,t.canvas.width,t.canvas.height),this._saveHistory()}toggleMode(){this._ctx.globalCompositeOperation=this._isDrawMode?"destination-out":"source-over",this._isDrawMode=!this._isDrawMode}toDataURL({type:t,quality:e}={}){return this._ctx.canvas.toDataURL(t,e)}fillImageByElement(t,{isOverlay:e=!1}={}){if(!function(t){return t instanceof HTMLImageElement||t instanceof SVGImageElement||t instanceof HTMLCanvasElement||t instanceof HTMLVideoElement}(t))throw new TypeError("Passed element is not a drawable!");const s=this._ctx;e||s.clearRect(0,0,s.canvas.width,s.canvas.height),s.drawImage(t,0,0,s.canvas.width,s.canvas.height),this._saveHistory()}async fillImageByDataURL(t,{isOverlay:e=!1}={}){if(!n(t))throw new TypeError("Passed src is not a base64 data URL!");const s=await o(t),i=this._ctx;e||i.clearRect(0,0,i.canvas.width,i.canvas.height),i.drawImage(s,0,0,i.canvas.width,i.canvas.height),this._saveHistory()}async undo(){this._history.undo();const t=this._history.value;if(!n(t))return;const e=await o(t),s=this._ctx;s.clearRect(0,0,s.canvas.width,s.canvas.height),s.drawImage(e,0,0,s.canvas.width,s.canvas.height)}async redo(){this._history.redo();const t=this._history.value;if(!n(t))return;const e=await o(t),s=this._ctx;s.clearRect(0,0,s.canvas.width,s.canvas.height),s.drawImage(e,0,0,s.canvas.width,s.canvas.height)}destroy(){this._unbindEvents(),this._ev.removeAllListeners(),this._history.clear(),cancelAnimationFrame(this._timer),this._timer=null}handleEvent(t){switch(t.preventDefault(),t.stopPropagation(),t.type){case"mousedown":case"touchstart":this._onInputDown(t);break;case"mousemove":case"touchmove":this._onInputMove(t);break;case"mouseup":case"touchend":this._onInputUp();break;case"mouseout":case"touchcancel":case"gesturestart":this._onInputCancel()}}_bindEvents(){const t=i()?["touchstart","touchmove","touchend","touchcancel","gesturestart"]:["mousedown","mousemove","mouseup","mouseout"];for(const e of t)this._$el.addEventListener(e,this,!1)}_unbindEvents(){const t=i()?["touchstart","touchmove","touchend","touchcancel","gesturestart"]:["mousedown","mousemove","mouseup","mouseout"];for(const e of t)this._$el.removeEventListener(e,this,!1)}_drawFrame(){if(this._timer=requestAnimationFrame((()=>this._drawFrame())),!this._isDrawing)return;const t=this._coords.old.x===this._coords.current.x&&this._coords.old.y===this._coords.current.y,e=r(this._coords.old,this._coords.current),s=this._ctx;s.beginPath(),s.moveTo(e.x,e.y),s.quadraticCurveTo(this._coords.old.x,this._coords.old.y,this._coords.oldMid.x,this._coords.oldMid.y),s.stroke(),this._coords.old=this._coords.current,this._coords.oldMid=e,t||this._ev.trigger("draw",this._coords.current)}_onInputDown(t){this._isDrawing=!0;const e=a(t,this._$el);this._coords.current=this._coords.old=e,this._coords.oldMid=r(this._coords.old,e),this._ev.trigger("drawBegin",this._coords.current)}_onInputMove(t){this._coords.current=a(t,this._$el)}_onInputUp(){this._ev.trigger("drawEnd",this._coords.current),this._saveHistory(),this._isDrawing=!1}_onInputCancel(){this._isDrawing&&(this._ev.trigger("drawEnd",this._coords.current),this._saveHistory()),this._isDrawing=!1}_saveHistory(){this._history.save(this.toDataURL()),this._ev.trigger("save",this._history.value)}}t.create=function(t){if(!(t instanceof HTMLCanvasElement))throw new TypeError("HTMLCanvasElement must be passed as first argument!");return new h(t)}}));