diff --git a/docs/index.html b/docs/index.html index 9bca6edb..537e812a 100644 --- a/docs/index.html +++ b/docs/index.html @@ -49,6 +49,8 @@ + +
diff --git a/docs/js/app.js b/docs/js/app.js index fc4a8745..ec388283 100644 --- a/docs/js/app.js +++ b/docs/js/app.js @@ -2,6 +2,8 @@ const wrapper = document.getElementById("signature-pad"); const canvasWrapper = document.getElementById("canvas-wrapper"); const clearButton = wrapper.querySelector("[data-action=clear]"); const changeBackgroundColorButton = wrapper.querySelector("[data-action=change-background-color]"); +const changeHighlightColorButton = wrapper.querySelector("[data-action=change-highlight-color]"); +const changeHighlightSizeButton = wrapper.querySelector("[data-action=change-highlight-size]"); const changeColorButton = wrapper.querySelector("[data-action=change-color]"); const changeWidthButton = wrapper.querySelector("[data-action=change-width]"); const undoButton = wrapper.querySelector("[data-action=undo]"); @@ -134,6 +136,16 @@ changeBackgroundColorButton.addEventListener("click", () => { signaturePad.fromData(data); }); +changeHighlightColorButton.addEventListener("click", () => { + signaturePad.highlightColor = randomColor(); +}); + +changeHighlightSizeButton.addEventListener("click", () => { + const size = Math.round(Math.random() * 100) / 10; + + signaturePad.highlightSize = size; +}); + changeColorButton.addEventListener("click", () => { signaturePad.penColor = randomColor(); }); diff --git a/docs/js/signature_pad.umd.min.js b/docs/js/signature_pad.umd.min.js index 7a5eb93b..2f9a1ed5 100644 --- a/docs/js/signature_pad.umd.min.js +++ b/docs/js/signature_pad.umd.min.js @@ -2,5 +2,5 @@ * Signature Pad v5.0.0 | https://github.com/szimek/signature_pad * (c) 2024 Szymon Nowak | Released under the MIT license */ -!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t="undefined"!=typeof globalThis?globalThis:t||self).SignaturePad=e()}(this,(function(){"use strict";class t{constructor(t,e,i,n){if(isNaN(t)||isNaN(e))throw new Error(`Point is invalid: (${t}, ${e})`);this.x=+t,this.y=+e,this.pressure=i||0,this.time=n||Date.now()}distanceTo(t){return Math.sqrt(Math.pow(this.x-t.x,2)+Math.pow(this.y-t.y,2))}equals(t){return this.x===t.x&&this.y===t.y&&this.pressure===t.pressure&&this.time===t.time}velocityFrom(t){return this.time!==t.time?this.distanceTo(t)/(this.time-t.time):0}}class e{static fromPoints(t,i){const n=this.calculateControlPoints(t[0],t[1],t[2]).c2,s=this.calculateControlPoints(t[1],t[2],t[3]).c1;return new e(t[1],n,s,t[2],i.start,i.end)}static calculateControlPoints(e,i,n){const s=e.x-i.x,o=e.y-i.y,r=i.x-n.x,h=i.y-n.y,a=(e.x+i.x)/2,c=(e.y+i.y)/2,d=(i.x+n.x)/2,l=(i.y+n.y)/2,u=Math.sqrt(s*s+o*o),v=Math.sqrt(r*r+h*h),_=v/(u+v),p=d+(a-d)*_,m=l+(c-l)*_,g=i.x-p,w=i.y-m;return{c1:new t(a+g,c+w),c2:new t(d+g,l+w)}}constructor(t,e,i,n,s,o){this.startPoint=t,this.control2=e,this.control1=i,this.endPoint=n,this.startWidth=s,this.endWidth=o}length(){let t,e,i=0;for(let n=0;n<=10;n+=1){const s=n/10,o=this.point(s,this.startPoint.x,this.control1.x,this.control2.x,this.endPoint.x),r=this.point(s,this.startPoint.y,this.control1.y,this.control2.y,this.endPoint.y);if(n>0){const n=o-t,s=r-e;i+=Math.sqrt(n*n+s*s)}t=o,e=r}return i}point(t,e,i,n,s){return e*(1-t)*(1-t)*(1-t)+3*i*(1-t)*(1-t)*t+3*n*(1-t)*t*t+s*t*t*t}}class i{constructor(){try{this._et=new EventTarget}catch(t){this._et=document}}addEventListener(t,e,i){this._et.addEventListener(t,e,i)}dispatchEvent(t){return this._et.dispatchEvent(t)}removeEventListener(t,e,i){this._et.removeEventListener(t,e,i)}}class n extends i{constructor(t,e={}){super(),this.canvas=t,this._drawingStroke=!1,this._isEmpty=!0,this._lastPoints=[],this._data=[],this._lastVelocity=0,this._lastWidth=0,this._handleMouseDown=t=>{this._isLeftButtonPressed(t,!0)&&!this._drawingStroke&&this._strokeBegin(this._pointerEventToSignatureEvent(t))},this._handleMouseMove=t=>{this._isLeftButtonPressed(t,!0)&&this._drawingStroke?this._strokeMoveUpdate(this._pointerEventToSignatureEvent(t)):this._strokeEnd(this._pointerEventToSignatureEvent(t),!1)},this._handleMouseUp=t=>{this._isLeftButtonPressed(t)||this._strokeEnd(this._pointerEventToSignatureEvent(t))},this._handleTouchStart=t=>{1!==t.targetTouches.length||this._drawingStroke||(t.cancelable&&t.preventDefault(),this._strokeBegin(this._touchEventToSignatureEvent(t)))},this._handleTouchMove=t=>{1===t.targetTouches.length&&(t.cancelable&&t.preventDefault(),this._drawingStroke?this._strokeMoveUpdate(this._touchEventToSignatureEvent(t)):this._strokeEnd(this._touchEventToSignatureEvent(t),!1))},this._handleTouchEnd=t=>{0===t.targetTouches.length&&(t.cancelable&&t.preventDefault(),this.canvas.removeEventListener("touchmove",this._handleTouchMove),this._strokeEnd(this._touchEventToSignatureEvent(t)))},this._handlePointerDown=t=>{this._isLeftButtonPressed(t)&&!this._drawingStroke&&(t.preventDefault(),this._strokeBegin(this._pointerEventToSignatureEvent(t)))},this._handlePointerMove=t=>{this._isLeftButtonPressed(t,!0)&&this._drawingStroke?(t.preventDefault(),this._strokeMoveUpdate(this._pointerEventToSignatureEvent(t))):this._strokeEnd(this._pointerEventToSignatureEvent(t),!1)},this._handlePointerUp=t=>{this._isLeftButtonPressed(t)||(t.preventDefault(),this._strokeEnd(this._pointerEventToSignatureEvent(t)))},this.velocityFilterWeight=e.velocityFilterWeight||.7,this.minWidth=e.minWidth||.5,this.maxWidth=e.maxWidth||2.5,this.throttle="throttle"in e?e.throttle:16,this.minDistance="minDistance"in e?e.minDistance:5,this.dotSize=e.dotSize||0,this.penColor=e.penColor||"black",this.backgroundColor=e.backgroundColor||"rgba(0,0,0,0)",this.compositeOperation=e.compositeOperation||"source-over",this.canvasContextOptions="canvasContextOptions"in e?e.canvasContextOptions:{},this._strokeMoveUpdate=this.throttle?function(t,e=250){let i,n,s,o=0,r=null;const h=()=>{o=Date.now(),r=null,i=t.apply(n,s),r||(n=null,s=[])};return function(...a){const c=Date.now(),d=e-(c-o);return n=this,s=a,d<=0||d>e?(r&&(clearTimeout(r),r=null),o=c,i=t.apply(n,s),r||(n=null,s=[])):r||(r=window.setTimeout(h,d)),i}}(n.prototype._strokeUpdate,this.throttle):n.prototype._strokeUpdate,this._ctx=t.getContext("2d",this.canvasContextOptions),this.clear(),this.on()}clear(){const{_ctx:t,canvas:e}=this;t.fillStyle=this.backgroundColor,t.clearRect(0,0,e.width,e.height),t.fillRect(0,0,e.width,e.height),this._data=[],this._reset(this._getPointGroupOptions()),this._isEmpty=!0}fromDataURL(t,e={}){return new Promise(((i,n)=>{const s=new Image,o=e.ratio||window.devicePixelRatio||1,r=e.width||this.canvas.width/o,h=e.height||this.canvas.height/o,a=e.xOffset||0,c=e.yOffset||0;this._reset(this._getPointGroupOptions()),s.onload=()=>{this._ctx.drawImage(s,a,c,r,h),i()},s.onerror=t=>{n(t)},s.crossOrigin="anonymous",s.src=t,this._isEmpty=!1}))}toDataURL(t="image/png",e){return"image/svg+xml"===t?("object"!=typeof e&&(e=void 0),`data:image/svg+xml;base64,${btoa(this.toSVG(e))}`):("number"!=typeof e&&(e=void 0),this.canvas.toDataURL(t,e))}on(){this.canvas.style.touchAction="none",this.canvas.style.msTouchAction="none",this.canvas.style.userSelect="none";const t=/Macintosh/.test(navigator.userAgent)&&"ontouchstart"in document;window.PointerEvent&&!t?this._handlePointerEvents():(this._handleMouseEvents(),"ontouchstart"in window&&this._handleTouchEvents())}off(){this.canvas.style.touchAction="auto",this.canvas.style.msTouchAction="auto",this.canvas.style.userSelect="auto",this.canvas.removeEventListener("pointerdown",this._handlePointerDown),this.canvas.removeEventListener("mousedown",this._handleMouseDown),this.canvas.removeEventListener("touchstart",this._handleTouchStart),this._removeMoveUpEventListeners()}_getListenerFunctions(){var t;const e=window.document===this.canvas.ownerDocument?window:null!==(t=this.canvas.ownerDocument.defaultView)&&void 0!==t?t:this.canvas.ownerDocument;return{addEventListener:e.addEventListener.bind(e),removeEventListener:e.removeEventListener.bind(e)}}_removeMoveUpEventListeners(){const{removeEventListener:t}=this._getListenerFunctions();t("pointermove",this._handlePointerMove),t("pointerup",this._handlePointerUp),t("mousemove",this._handleMouseMove),t("mouseup",this._handleMouseUp),t("touchmove",this._handleTouchMove),t("touchend",this._handleTouchEnd)}isEmpty(){return this._isEmpty}fromData(t,{clear:e=!0}={}){e&&this.clear(),this._fromData(t,this._drawCurve.bind(this),this._drawDot.bind(this)),this._data=this._data.concat(t)}toData(){return this._data}_isLeftButtonPressed(t,e){return e?1===t.buttons:!(1&~t.buttons)}_pointerEventToSignatureEvent(t){return{event:t,type:t.type,x:t.clientX,y:t.clientY,pressure:"pressure"in t?t.pressure:0}}_touchEventToSignatureEvent(t){const e=t.changedTouches[0];return{event:t,type:t.type,x:e.clientX,y:e.clientY,pressure:e.force}}_getPointGroupOptions(t){return{penColor:t&&"penColor"in t?t.penColor:this.penColor,dotSize:t&&"dotSize"in t?t.dotSize:this.dotSize,minWidth:t&&"minWidth"in t?t.minWidth:this.minWidth,maxWidth:t&&"maxWidth"in t?t.maxWidth:this.maxWidth,velocityFilterWeight:t&&"velocityFilterWeight"in t?t.velocityFilterWeight:this.velocityFilterWeight,compositeOperation:t&&"compositeOperation"in t?t.compositeOperation:this.compositeOperation}}_strokeBegin(t){if(!this.dispatchEvent(new CustomEvent("beginStroke",{detail:t,cancelable:!0})))return;const{addEventListener:e}=this._getListenerFunctions();switch(t.event.type){case"mousedown":e("mousemove",this._handleMouseMove),e("mouseup",this._handleMouseUp);break;case"touchstart":e("touchmove",this._handleTouchMove),e("touchend",this._handleTouchEnd);break;case"pointerdown":e("pointermove",this._handlePointerMove),e("pointerup",this._handlePointerUp)}this._drawingStroke=!0;const i=this._getPointGroupOptions(),n=Object.assign(Object.assign({},i),{points:[]});this._data.push(n),this._reset(i),this._strokeUpdate(t)}_strokeUpdate(t){if(!this._drawingStroke)return;if(0===this._data.length)return void this._strokeBegin(t);this.dispatchEvent(new CustomEvent("beforeUpdateStroke",{detail:t}));const e=this._createPoint(t.x,t.y,t.pressure),i=this._data[this._data.length-1],n=i.points,s=n.length>0&&n[n.length-1],o=!!s&&e.distanceTo(s)<=this.minDistance,r=this._getPointGroupOptions(i);if(!s||!s||!o){const t=this._addPoint(e,r);s?t&&this._drawCurve(t,r):this._drawDot(e,r),n.push({time:e.time,x:e.x,y:e.y,pressure:e.pressure})}this.dispatchEvent(new CustomEvent("afterUpdateStroke",{detail:t}))}_strokeEnd(t,e=!0){this._removeMoveUpEventListeners(),this._drawingStroke&&(e&&this._strokeUpdate(t),this._drawingStroke=!1,this.dispatchEvent(new CustomEvent("endStroke",{detail:t})))}_handlePointerEvents(){this._drawingStroke=!1,this.canvas.addEventListener("pointerdown",this._handlePointerDown)}_handleMouseEvents(){this._drawingStroke=!1,this.canvas.addEventListener("mousedown",this._handleMouseDown)}_handleTouchEvents(){this.canvas.addEventListener("touchstart",this._handleTouchStart)}_reset(t){this._lastPoints=[],this._lastVelocity=0,this._lastWidth=(t.minWidth+t.maxWidth)/2,this._ctx.fillStyle=t.penColor,this._ctx.globalCompositeOperation=t.compositeOperation}_createPoint(e,i,n){const s=this.canvas.getBoundingClientRect();return new t(e-s.left,i-s.top,n,(new Date).getTime())}_addPoint(t,i){const{_lastPoints:n}=this;if(n.push(t),n.length>2){3===n.length&&n.unshift(n[0]);const t=this._calculateCurveWidths(n[1],n[2],i),s=e.fromPoints(n,t);return n.shift(),s}return null}_calculateCurveWidths(t,e,i){const n=i.velocityFilterWeight*e.velocityFrom(t)+(1-i.velocityFilterWeight)*this._lastVelocity,s=this._strokeWidth(n,i),o={end:s,start:this._lastWidth};return this._lastVelocity=n,this._lastWidth=s,o}_strokeWidth(t,e){return Math.max(e.maxWidth/(t+1),e.minWidth)}_drawCurveSegment(t,e,i){const n=this._ctx;n.moveTo(t,e),n.arc(t,e,i,0,2*Math.PI,!1),this._isEmpty=!1}_drawCurve(t,e){const i=this._ctx,n=t.endWidth-t.startWidth,s=2*Math.ceil(t.length());i.beginPath(),i.fillStyle=e.penColor;for(let i=0;i0?e.dotSize:(e.minWidth+e.maxWidth)/2;i.beginPath(),this._drawCurveSegment(t.x,t.y,n),i.closePath(),i.fillStyle=e.penColor,i.fill()}_fromData(e,i,n){for(const s of e){const{points:e}=s,o=this._getPointGroupOptions(s);if(e.length>1)for(let n=0;n{const i=document.createElement("path");if(!(isNaN(t.control1.x)||isNaN(t.control1.y)||isNaN(t.control2.x)||isNaN(t.control2.y))){const n=`M ${t.startPoint.x.toFixed(3)},${t.startPoint.y.toFixed(3)} C ${t.control1.x.toFixed(3)},${t.control1.y.toFixed(3)} ${t.control2.x.toFixed(3)},${t.control2.y.toFixed(3)} ${t.endPoint.x.toFixed(3)},${t.endPoint.y.toFixed(3)}`;i.setAttribute("d",n),i.setAttribute("stroke-width",(2.25*t.endWidth).toFixed(3)),i.setAttribute("stroke",e),i.setAttribute("fill","none"),i.setAttribute("stroke-linecap","round"),o.appendChild(i)}}),((t,{penColor:e,dotSize:i,minWidth:n,maxWidth:s})=>{const r=document.createElement("circle"),h=i>0?i:(n+s)/2;r.setAttribute("r",h.toString()),r.setAttribute("cx",t.x.toString()),r.setAttribute("cy",t.y.toString()),r.setAttribute("fill",e),o.appendChild(r)})),o.outerHTML}}return n})); +!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t="undefined"!=typeof globalThis?globalThis:t||self).SignaturePad=e()}(this,(function(){"use strict";class t{constructor(t,e,i,n){if(isNaN(t)||isNaN(e))throw new Error(`Point is invalid: (${t}, ${e})`);this.x=+t,this.y=+e,this.pressure=i||0,this.time=n||Date.now()}distanceTo(t){return Math.sqrt(Math.pow(this.x-t.x,2)+Math.pow(this.y-t.y,2))}equals(t){return this.x===t.x&&this.y===t.y&&this.pressure===t.pressure&&this.time===t.time}velocityFrom(t){return this.time!==t.time?this.distanceTo(t)/(this.time-t.time):0}}class e{static fromPoints(t,i){const n=this.calculateControlPoints(t[0],t[1],t[2]).c2,o=this.calculateControlPoints(t[1],t[2],t[3]).c1;return new e(t[1],n,o,t[2],i.start,i.end)}static calculateControlPoints(e,i,n){const o=e.x-i.x,s=e.y-i.y,h=i.x-n.x,r=i.y-n.y,a=(e.x+i.x)/2,l=(e.y+i.y)/2,d=(i.x+n.x)/2,c=(i.y+n.y)/2,u=Math.sqrt(o*o+s*s),v=Math.sqrt(h*h+r*r),g=v/(u+v),_=d+(a-d)*g,p=c+(l-c)*g,m=i.x-_,w=i.y-p;return{c1:new t(a+m,l+w),c2:new t(d+m,c+w)}}constructor(t,e,i,n,o,s){this.startPoint=t,this.control2=e,this.control1=i,this.endPoint=n,this.startWidth=o,this.endWidth=s}length(){let t,e,i=0;for(let n=0;n<=10;n+=1){const o=n/10,s=this.point(o,this.startPoint.x,this.control1.x,this.control2.x,this.endPoint.x),h=this.point(o,this.startPoint.y,this.control1.y,this.control2.y,this.endPoint.y);if(n>0){const n=s-t,o=h-e;i+=Math.sqrt(n*n+o*o)}t=s,e=h}return i}point(t,e,i,n,o){return e*(1-t)*(1-t)*(1-t)+3*i*(1-t)*(1-t)*t+3*n*(1-t)*t*t+o*t*t*t}}class i{constructor(){try{this._et=new EventTarget}catch(t){this._et=document}}addEventListener(t,e,i){this._et.addEventListener(t,e,i)}dispatchEvent(t){return this._et.dispatchEvent(t)}removeEventListener(t,e,i){this._et.removeEventListener(t,e,i)}}class n extends i{constructor(t,e={}){var i,o,s,h,r,a,l,d,c,u,v,g;super(),this.canvas=t,this._drawingStroke=!1,this._isEmpty=!0,this._lastPoints=[],this._data=[],this._lastVelocity=0,this._lastWidth=0,this._handleMouseDown=t=>{this._isLeftButtonPressed(t,!0)&&!this._drawingStroke&&this._strokeBegin(this._pointerEventToSignatureEvent(t))},this._handleMouseMove=t=>{this._isLeftButtonPressed(t,!0)&&this._drawingStroke?this._strokeMoveUpdate(this._pointerEventToSignatureEvent(t)):this._strokeEnd(this._pointerEventToSignatureEvent(t),!1)},this._handleMouseUp=t=>{this._isLeftButtonPressed(t)||this._strokeEnd(this._pointerEventToSignatureEvent(t))},this._handleTouchStart=t=>{1!==t.targetTouches.length||this._drawingStroke||(t.cancelable&&t.preventDefault(),this._strokeBegin(this._touchEventToSignatureEvent(t)))},this._handleTouchMove=t=>{1===t.targetTouches.length&&(t.cancelable&&t.preventDefault(),this._drawingStroke?this._strokeMoveUpdate(this._touchEventToSignatureEvent(t)):this._strokeEnd(this._touchEventToSignatureEvent(t),!1))},this._handleTouchEnd=t=>{0===t.targetTouches.length&&(t.cancelable&&t.preventDefault(),this.canvas.removeEventListener("touchmove",this._handleTouchMove),this._strokeEnd(this._touchEventToSignatureEvent(t)))},this._handlePointerDown=t=>{this._isLeftButtonPressed(t)&&!this._drawingStroke&&(t.preventDefault(),this._strokeBegin(this._pointerEventToSignatureEvent(t)))},this._handlePointerMove=t=>{this._isLeftButtonPressed(t,!0)&&this._drawingStroke?(t.preventDefault(),this._strokeMoveUpdate(this._pointerEventToSignatureEvent(t))):this._strokeEnd(this._pointerEventToSignatureEvent(t),!1)},this._handlePointerUp=t=>{this._isLeftButtonPressed(t)||(t.preventDefault(),this._strokeEnd(this._pointerEventToSignatureEvent(t)))},this.velocityFilterWeight=null!==(i=e.velocityFilterWeight)&&void 0!==i?i:.7,this.minWidth=null!==(o=e.minWidth)&&void 0!==o?o:.5,this.maxWidth=null!==(s=e.maxWidth)&&void 0!==s?s:2.5,this.throttle=null!==(h=e.throttle)&&void 0!==h?h:16,this.minDistance=null!==(r=e.minDistance)&&void 0!==r?r:5,this.dotSize=null!==(a=e.dotSize)&&void 0!==a?a:0,this.penColor=null!==(l=e.penColor)&&void 0!==l?l:"black",this.highlightColor=null!==(d=e.highlightColor)&&void 0!==d?d:"",this.highlightSize=null!==(c=e.highlightSize)&&void 0!==c?c:1,this.backgroundColor=null!==(u=e.backgroundColor)&&void 0!==u?u:"rgba(0,0,0,0)",this.compositeOperation=null!==(v=e.compositeOperation)&&void 0!==v?v:"source-over",this.canvasContextOptions=null!==(g=e.canvasContextOptions)&&void 0!==g?g:{},this._strokeMoveUpdate=this.throttle?function(t,e=250){let i,n,o,s=0,h=null;const r=()=>{s=Date.now(),h=null,i=t.apply(n,o),h||(n=null,o=[])};return function(...a){const l=Date.now(),d=e-(l-s);return n=this,o=a,d<=0||d>e?(h&&(clearTimeout(h),h=null),s=l,i=t.apply(n,o),h||(n=null,o=[])):h||(h=window.setTimeout(r,d)),i}}(n.prototype._strokeUpdate,this.throttle):n.prototype._strokeUpdate,this._ctx=t.getContext("2d",this.canvasContextOptions),this.clear(),this.on()}clear(){const{_ctx:t,canvas:e}=this;t.fillStyle=this.backgroundColor,t.clearRect(0,0,e.width,e.height),t.fillRect(0,0,e.width,e.height),this._data=[],this._reset(this._getPointGroupOptions()),this._isEmpty=!0}fromDataURL(t,e={}){return new Promise(((i,n)=>{const o=new Image,s=e.ratio||window.devicePixelRatio||1,h=e.width||this.canvas.width/s,r=e.height||this.canvas.height/s,a=e.xOffset||0,l=e.yOffset||0;this._reset(this._getPointGroupOptions()),o.onload=()=>{this._ctx.drawImage(o,a,l,h,r),i()},o.onerror=t=>{n(t)},o.crossOrigin="anonymous",o.src=t,this._isEmpty=!1}))}toDataURL(t="image/png",e){return"image/svg+xml"===t?("object"!=typeof e&&(e=void 0),`data:image/svg+xml;base64,${btoa(this.toSVG(e))}`):("number"!=typeof e&&(e=void 0),this.canvas.toDataURL(t,e))}on(){this.canvas.style.touchAction="none",this.canvas.style.msTouchAction="none",this.canvas.style.userSelect="none";const t=/Macintosh/.test(navigator.userAgent)&&"ontouchstart"in document;window.PointerEvent&&!t?this._handlePointerEvents():(this._handleMouseEvents(),"ontouchstart"in window&&this._handleTouchEvents())}off(){this.canvas.style.touchAction="auto",this.canvas.style.msTouchAction="auto",this.canvas.style.userSelect="auto",this.canvas.removeEventListener("pointerdown",this._handlePointerDown),this.canvas.removeEventListener("mousedown",this._handleMouseDown),this.canvas.removeEventListener("touchstart",this._handleTouchStart),this._removeMoveUpEventListeners()}_getListenerFunctions(){var t;const e=window.document===this.canvas.ownerDocument?window:null!==(t=this.canvas.ownerDocument.defaultView)&&void 0!==t?t:this.canvas.ownerDocument;return{addEventListener:e.addEventListener.bind(e),removeEventListener:e.removeEventListener.bind(e)}}_removeMoveUpEventListeners(){const{removeEventListener:t}=this._getListenerFunctions();t("pointermove",this._handlePointerMove),t("pointerup",this._handlePointerUp),t("mousemove",this._handleMouseMove),t("mouseup",this._handleMouseUp),t("touchmove",this._handleTouchMove),t("touchend",this._handleTouchEnd)}isEmpty(){return this._isEmpty}fromData(t,{clear:e=!0}={}){e&&this.clear(),this._fromData(t,this._drawCurve.bind(this),this._drawDot.bind(this)),this._data=this._data.concat(t)}toData(){return this._data}_isLeftButtonPressed(t,e){return e?1===t.buttons:!(1&~t.buttons)}_pointerEventToSignatureEvent(t){return{event:t,type:t.type,x:t.clientX,y:t.clientY,pressure:"pressure"in t?t.pressure:0}}_touchEventToSignatureEvent(t){const e=t.changedTouches[0];return{event:t,type:t.type,x:e.clientX,y:e.clientY,pressure:e.force}}_getPointGroupOptions(t){return{penColor:t&&"penColor"in t?t.penColor:this.penColor,highlightColor:t&&"highlightColor"in t?t.highlightColor:this.highlightColor,highlightSize:t&&"highlightSize"in t?t.highlightSize:this.highlightSize,dotSize:t&&"dotSize"in t?t.dotSize:this.dotSize,minWidth:t&&"minWidth"in t?t.minWidth:this.minWidth,maxWidth:t&&"maxWidth"in t?t.maxWidth:this.maxWidth,velocityFilterWeight:t&&"velocityFilterWeight"in t?t.velocityFilterWeight:this.velocityFilterWeight,compositeOperation:t&&"compositeOperation"in t?t.compositeOperation:this.compositeOperation}}_strokeBegin(t){if(!this.dispatchEvent(new CustomEvent("beginStroke",{detail:t,cancelable:!0})))return;const{addEventListener:e}=this._getListenerFunctions();switch(t.event.type){case"mousedown":e("mousemove",this._handleMouseMove),e("mouseup",this._handleMouseUp);break;case"touchstart":e("touchmove",this._handleTouchMove),e("touchend",this._handleTouchEnd);break;case"pointerdown":e("pointermove",this._handlePointerMove),e("pointerup",this._handlePointerUp)}this._drawingStroke=!0;const i=this._getPointGroupOptions(),n=Object.assign(Object.assign({},i),{points:[]});this._data.push(n),this._reset(i),this._strokeUpdate(t)}_strokeUpdate(t){if(!this._drawingStroke)return;if(0===this._data.length)return void this._strokeBegin(t);this.dispatchEvent(new CustomEvent("beforeUpdateStroke",{detail:t}));const e=this._createPoint(t.x,t.y,t.pressure),i=this._data[this._data.length-1],n=i.points,o=n.length>0&&n[n.length-1],s=!!o&&e.distanceTo(o)<=this.minDistance,h=this._getPointGroupOptions(i);if(!o||!o||!s){const t=this._addPoint(e,h);n.push({time:e.time,x:e.x,y:e.y,pressure:e.pressure}),o?t&&(h.highlightColor&&h.highlightSize?(this._drawAll(n,h,this._drawCurve.bind(this),!0),this._drawAll(n,h,this._drawCurve.bind(this),!1)):this._drawCurve(t,h,!1)):(h.highlightColor&&h.highlightSize&&this._drawDot(e,h,!0),this._drawDot(e,h,!1))}this.dispatchEvent(new CustomEvent("afterUpdateStroke",{detail:t}))}_strokeEnd(t,e=!0){this._removeMoveUpEventListeners(),this._drawingStroke&&(e&&this._strokeUpdate(t),this._drawingStroke=!1,this.dispatchEvent(new CustomEvent("endStroke",{detail:t})))}_handlePointerEvents(){this._drawingStroke=!1,this.canvas.addEventListener("pointerdown",this._handlePointerDown)}_handleMouseEvents(){this._drawingStroke=!1,this.canvas.addEventListener("mousedown",this._handleMouseDown)}_handleTouchEvents(){this.canvas.addEventListener("touchstart",this._handleTouchStart)}_reset(t){this._lastPoints=[],this._lastVelocity=0,this._lastWidth=(t.minWidth+t.maxWidth)/2,this._ctx.fillStyle=t.penColor,this._ctx.globalCompositeOperation=t.compositeOperation}_createPoint(e,i,n){const o=this.canvas.getBoundingClientRect();return new t(e-o.left,i-o.top,n,(new Date).getTime())}_addPoint(t,i){const{_lastPoints:n}=this;if(n.push(t),n.length>2){3===n.length&&n.unshift(n[0]);const t=this._calculateCurveWidths(n[1],n[2],i),o=e.fromPoints(n,t);return n.shift(),o}return null}_calculateCurveWidths(t,e,i){const n=i.velocityFilterWeight*e.velocityFrom(t)+(1-i.velocityFilterWeight)*this._lastVelocity,o=this._strokeWidth(n,i),s={end:o,start:this._lastWidth};return this._lastVelocity=n,this._lastWidth=o,s}_strokeWidth(t,e){return Math.max(e.maxWidth/(t+1),e.minWidth)}_drawCurveSegment(t,e,i){const n=this._ctx;n.moveTo(t,e),n.arc(t,e,i,0,2*Math.PI,!1),this._isEmpty=!1}_drawCurve(t,e,i){const n=this._ctx,o=t.endWidth-t.startWidth,s=2*Math.ceil(t.length());n.beginPath(),n.fillStyle=i?e.highlightColor:e.penColor;for(let n=0;n0?e.dotSize:(e.minWidth+e.maxWidth)/2;n.beginPath(),this._drawCurveSegment(t.x,t.y,o+(i?2*e.highlightSize:0)),n.closePath(),n.fillStyle=i?e.highlightColor:e.penColor,n.fill()}_fromData(t,e,i){for(const n of t){const{points:t}=n,o=this._getPointGroupOptions(n);t.length>1?(o.highlightColor&&o.highlightSize&&this._drawAll(t,o,e,!0),this._drawAll(t,o,e,!1)):(this._reset(o),o.highlightColor&&o.highlightSize&&i(t[0],o,!0),i(t[0],o,!1))}}_drawAll(e,i,n,o){for(let s=0;s{const h=document.createElement("path");if(!(isNaN(t.control1.x)||isNaN(t.control1.y)||isNaN(t.control2.x)||isNaN(t.control2.y))){const r=`M ${t.startPoint.x.toFixed(3)},${t.startPoint.y.toFixed(3)} C ${t.control1.x.toFixed(3)},${t.control1.y.toFixed(3)} ${t.control2.x.toFixed(3)},${t.control2.y.toFixed(3)} ${t.endPoint.x.toFixed(3)},${t.endPoint.y.toFixed(3)}`;h.setAttribute("d",r),h.setAttribute("stroke-width",(2.25*(t.endWidth+(o?2*n:0))).toFixed(3)),h.setAttribute("stroke",o?i:e),h.setAttribute("fill","none"),h.setAttribute("stroke-linecap","round"),s.appendChild(h)}}),((t,{penColor:e,highlightColor:i,highlightSize:n,dotSize:o,minWidth:h,maxWidth:r},a)=>{const l=document.createElement("circle"),d=o>0?o:(h+r)/2;l.setAttribute("r",(d+(a?2*n:0)).toString()),l.setAttribute("cx",t.x.toString()),l.setAttribute("cy",t.y.toString()),l.setAttribute("fill",a?i:e),s.appendChild(l)})),s.outerHTML}}return n})); //# sourceMappingURL=signature_pad.umd.min.js.map diff --git a/docs/js/signature_pad.umd.min.js.map b/docs/js/signature_pad.umd.min.js.map index 629de735..01cfd717 100644 --- a/docs/js/signature_pad.umd.min.js.map +++ b/docs/js/signature_pad.umd.min.js.map @@ -1 +1 @@ -{"version":3,"file":"signature_pad.umd.min.js","sources":["../src/point.ts","../src/bezier.ts","../src/signature_event_target.ts","../src/signature_pad.ts","../src/throttle.ts"],"sourcesContent":["// Interface for point data structure used e.g. in SignaturePad#fromData method\nexport interface BasicPoint {\n x: number;\n y: number;\n pressure: number;\n time: number;\n}\n\nexport class Point implements BasicPoint {\n public x: number;\n public y: number;\n public pressure: number;\n public time: number;\n\n constructor(x: number, y: number, pressure?: number, time?: number) {\n if (isNaN(x) || isNaN(y)) {\n throw new Error(`Point is invalid: (${x}, ${y})`);\n }\n this.x = +x;\n this.y = +y;\n this.pressure = pressure || 0;\n this.time = time || Date.now();\n }\n\n public distanceTo(start: BasicPoint): number {\n return Math.sqrt(\n Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2),\n );\n }\n\n public equals(other: BasicPoint): boolean {\n return (\n this.x === other.x &&\n this.y === other.y &&\n this.pressure === other.pressure &&\n this.time === other.time\n );\n }\n\n public velocityFrom(start: BasicPoint): number {\n return this.time !== start.time\n ? this.distanceTo(start) / (this.time - start.time)\n : 0;\n }\n}\n","import { BasicPoint, Point } from './point';\n\nexport class Bezier {\n public static fromPoints(\n points: Point[],\n widths: { start: number; end: number },\n ): Bezier {\n const c2 = this.calculateControlPoints(points[0], points[1], points[2]).c2;\n const c3 = this.calculateControlPoints(points[1], points[2], points[3]).c1;\n\n return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);\n }\n\n private static calculateControlPoints(\n s1: BasicPoint,\n s2: BasicPoint,\n s3: BasicPoint,\n ): {\n c1: BasicPoint;\n c2: BasicPoint;\n } {\n const dx1 = s1.x - s2.x;\n const dy1 = s1.y - s2.y;\n const dx2 = s2.x - s3.x;\n const dy2 = s2.y - s3.y;\n\n const m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };\n const m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };\n\n const l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);\n const l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);\n\n const dxm = m1.x - m2.x;\n const dym = m1.y - m2.y;\n\n const k = l2 / (l1 + l2);\n const cm = { x: m2.x + dxm * k, y: m2.y + dym * k };\n\n const tx = s2.x - cm.x;\n const ty = s2.y - cm.y;\n\n return {\n c1: new Point(m1.x + tx, m1.y + ty),\n c2: new Point(m2.x + tx, m2.y + ty),\n };\n }\n\n constructor(\n public startPoint: Point,\n public control2: BasicPoint,\n public control1: BasicPoint,\n public endPoint: Point,\n public startWidth: number,\n public endWidth: number,\n ) {}\n\n // Returns approximated length. Code taken from https://www.lemoda.net/maths/bezier-length/index.html.\n public length(): number {\n const steps = 10;\n let length = 0;\n let px;\n let py;\n\n for (let i = 0; i <= steps; i += 1) {\n const t = i / steps;\n const cx = this.point(\n t,\n this.startPoint.x,\n this.control1.x,\n this.control2.x,\n this.endPoint.x,\n );\n const cy = this.point(\n t,\n this.startPoint.y,\n this.control1.y,\n this.control2.y,\n this.endPoint.y,\n );\n\n if (i > 0) {\n const xdiff = cx - (px as number);\n const ydiff = cy - (py as number);\n\n length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);\n }\n\n px = cx;\n py = cy;\n }\n\n return length;\n }\n\n // Calculate parametric value of x or y given t and the four point coordinates of a cubic bezier curve.\n private point(\n t: number,\n start: number,\n c1: number,\n c2: number,\n end: number,\n ): number {\n // prettier-ignore\n return ( start * (1.0 - t) * (1.0 - t) * (1.0 - t))\n + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)\n + (3.0 * c2 * (1.0 - t) * t * t)\n + ( end * t * t * t);\n }\n}\n","export class SignatureEventTarget {\n /* tslint:disable: variable-name */\n private _et: EventTarget;\n /* tslint:enable: variable-name */\n\n constructor() {\n try {\n this._et = new EventTarget();\n } catch (error) {\n // Using document as EventTarget to support iOS 13 and older.\n // Because EventTarget constructor just exists at iOS 14 and later.\n this._et = document;\n }\n }\n\n addEventListener(\n type: string,\n listener: EventListenerOrEventListenerObject | null,\n options?: boolean | AddEventListenerOptions,\n ): void {\n this._et.addEventListener(type, listener, options);\n }\n\n dispatchEvent(event: Event): boolean {\n return this._et.dispatchEvent(event);\n }\n\n removeEventListener(\n type: string,\n callback: EventListenerOrEventListenerObject | null,\n options?: boolean | EventListenerOptions,\n ): void {\n this._et.removeEventListener(type, callback, options);\n }\n}\n","/**\n * The main idea and some parts of the code (e.g. drawing variable width Bézier curve) are taken from:\n * http://corner.squareup.com/2012/07/smoother-signatures.html\n *\n * Implementation of interpolation using cubic Bézier curves is taken from:\n * https://web.archive.org/web/20160323213433/http://www.benknowscode.com/2012/09/path-interpolation-using-cubic-bezier_9742.html\n *\n * Algorithm for approximated length of a Bézier curve is taken from:\n * http://www.lemoda.net/maths/bezier-length/index.html\n */\n\nimport { Bezier } from './bezier';\nimport { BasicPoint, Point } from './point';\nimport { SignatureEventTarget } from './signature_event_target';\nimport { throttle } from './throttle';\n\ndeclare global {\n interface CSSStyleDeclaration {\n msTouchAction: string | null;\n }\n}\n\nexport interface SignatureEvent {\n event: MouseEvent | TouchEvent | PointerEvent;\n type: string;\n x: number;\n y: number;\n pressure: number;\n}\n\nexport interface FromDataOptions {\n clear?: boolean;\n}\n\nexport interface ToSVGOptions {\n includeBackgroundColor?: boolean;\n}\n\nexport interface PointGroupOptions {\n dotSize: number;\n minWidth: number;\n maxWidth: number;\n penColor: string;\n velocityFilterWeight: number;\n /**\n * This is the globalCompositeOperation for the line.\n * *default: 'source-over'*\n * @see https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/globalCompositeOperation\n */\n compositeOperation: GlobalCompositeOperation;\n}\n\nexport interface Options extends Partial {\n minDistance?: number;\n backgroundColor?: string;\n throttle?: number;\n canvasContextOptions?: CanvasRenderingContext2DSettings;\n}\n\nexport interface PointGroup extends PointGroupOptions {\n points: BasicPoint[];\n}\n\nexport default class SignaturePad extends SignatureEventTarget {\n // Public stuff\n public dotSize: number;\n public minWidth: number;\n public maxWidth: number;\n public penColor: string;\n public minDistance: number;\n public velocityFilterWeight: number;\n public compositeOperation: GlobalCompositeOperation;\n public backgroundColor: string;\n public throttle: number;\n public canvasContextOptions: CanvasRenderingContext2DSettings;\n\n // Private stuff\n /* tslint:disable: variable-name */\n private _ctx: CanvasRenderingContext2D;\n private _drawingStroke = false;\n private _isEmpty = true;\n private _lastPoints: Point[] = []; // Stores up to 4 most recent points; used to generate a new curve\n private _data: PointGroup[] = []; // Stores all points in groups (one group per line or dot)\n private _lastVelocity = 0;\n private _lastWidth = 0;\n private _strokeMoveUpdate: (event: SignatureEvent) => void;\n /* tslint:enable: variable-name */\n\n constructor(\n private canvas: HTMLCanvasElement,\n options: Options = {},\n ) {\n super();\n this.velocityFilterWeight = options.velocityFilterWeight || 0.7;\n this.minWidth = options.minWidth || 0.5;\n this.maxWidth = options.maxWidth || 2.5;\n this.throttle = ('throttle' in options ? options.throttle : 16) as number; // in milliseconds\n this.minDistance = (\n 'minDistance' in options ? options.minDistance : 5\n ) as number; // in pixels\n this.dotSize = options.dotSize || 0;\n this.penColor = options.penColor || 'black';\n this.backgroundColor = options.backgroundColor || 'rgba(0,0,0,0)';\n this.compositeOperation = options.compositeOperation || 'source-over';\n this.canvasContextOptions = (\n 'canvasContextOptions' in options ? options.canvasContextOptions : {}\n ) as CanvasRenderingContext2DSettings;\n\n this._strokeMoveUpdate = this.throttle\n ? throttle(SignaturePad.prototype._strokeUpdate, this.throttle)\n : SignaturePad.prototype._strokeUpdate;\n this._ctx = canvas.getContext(\n '2d',\n this.canvasContextOptions,\n ) as CanvasRenderingContext2D;\n\n this.clear();\n\n // Enable mouse and touch event handlers\n this.on();\n }\n\n public clear(): void {\n const { _ctx: ctx, canvas } = this;\n\n // Clear canvas using background color\n ctx.fillStyle = this.backgroundColor;\n ctx.clearRect(0, 0, canvas.width, canvas.height);\n ctx.fillRect(0, 0, canvas.width, canvas.height);\n\n this._data = [];\n this._reset(this._getPointGroupOptions());\n this._isEmpty = true;\n }\n\n public fromDataURL(\n dataUrl: string,\n options: {\n ratio?: number;\n width?: number;\n height?: number;\n xOffset?: number;\n yOffset?: number;\n } = {},\n ): Promise {\n return new Promise((resolve, reject) => {\n const image = new Image();\n const ratio = options.ratio || window.devicePixelRatio || 1;\n const width = options.width || this.canvas.width / ratio;\n const height = options.height || this.canvas.height / ratio;\n const xOffset = options.xOffset || 0;\n const yOffset = options.yOffset || 0;\n\n this._reset(this._getPointGroupOptions());\n\n image.onload = (): void => {\n this._ctx.drawImage(image, xOffset, yOffset, width, height);\n resolve();\n };\n image.onerror = (error): void => {\n reject(error);\n };\n image.crossOrigin = 'anonymous';\n image.src = dataUrl;\n\n this._isEmpty = false;\n });\n }\n\n public toDataURL(\n type: 'image/svg+xml',\n encoderOptions?: ToSVGOptions,\n ): string;\n public toDataURL(type?: string, encoderOptions?: number): string;\n public toDataURL(\n type = 'image/png',\n encoderOptions?: number | ToSVGOptions | undefined,\n ): string {\n switch (type) {\n case 'image/svg+xml':\n if (typeof encoderOptions !== 'object') {\n encoderOptions = undefined;\n }\n return `data:image/svg+xml;base64,${btoa(\n this.toSVG(encoderOptions as ToSVGOptions),\n )}`;\n default:\n if (typeof encoderOptions !== 'number') {\n encoderOptions = undefined;\n }\n return this.canvas.toDataURL(type, encoderOptions);\n }\n }\n\n public on(): void {\n // Disable panning/zooming when touching canvas element\n this.canvas.style.touchAction = 'none';\n this.canvas.style.msTouchAction = 'none';\n this.canvas.style.userSelect = 'none';\n\n const isIOS =\n /Macintosh/.test(navigator.userAgent) && 'ontouchstart' in document;\n\n // The \"Scribble\" feature of iOS intercepts point events. So that we can\n // lose some of them when tapping rapidly. Use touch events for iOS\n // platforms to prevent it. See\n // https://developer.apple.com/forums/thread/664108 for more information.\n if (window.PointerEvent && !isIOS) {\n this._handlePointerEvents();\n } else {\n this._handleMouseEvents();\n\n if ('ontouchstart' in window) {\n this._handleTouchEvents();\n }\n }\n }\n\n public off(): void {\n // Enable panning/zooming when touching canvas element\n this.canvas.style.touchAction = 'auto';\n this.canvas.style.msTouchAction = 'auto';\n this.canvas.style.userSelect = 'auto';\n\n this.canvas.removeEventListener('pointerdown', this._handlePointerDown);\n this.canvas.removeEventListener('mousedown', this._handleMouseDown);\n this.canvas.removeEventListener('touchstart', this._handleTouchStart);\n\n this._removeMoveUpEventListeners();\n }\n\n private _getListenerFunctions() {\n const canvasWindow =\n window.document === this.canvas.ownerDocument\n ? window\n : this.canvas.ownerDocument.defaultView ?? this.canvas.ownerDocument;\n\n return {\n addEventListener: canvasWindow.addEventListener.bind(\n canvasWindow,\n ) as typeof window.addEventListener,\n removeEventListener: canvasWindow.removeEventListener.bind(\n canvasWindow,\n ) as typeof window.removeEventListener,\n };\n }\n\n private _removeMoveUpEventListeners(): void {\n const { removeEventListener } = this._getListenerFunctions();\n removeEventListener('pointermove', this._handlePointerMove);\n removeEventListener('pointerup', this._handlePointerUp);\n\n removeEventListener('mousemove', this._handleMouseMove);\n removeEventListener('mouseup', this._handleMouseUp);\n\n removeEventListener('touchmove', this._handleTouchMove);\n removeEventListener('touchend', this._handleTouchEnd);\n }\n\n public isEmpty(): boolean {\n return this._isEmpty;\n }\n\n public fromData(\n pointGroups: PointGroup[],\n { clear = true }: FromDataOptions = {},\n ): void {\n if (clear) {\n this.clear();\n }\n\n this._fromData(\n pointGroups,\n this._drawCurve.bind(this),\n this._drawDot.bind(this),\n );\n\n this._data = this._data.concat(pointGroups);\n }\n\n public toData(): PointGroup[] {\n return this._data;\n }\n\n public _isLeftButtonPressed(event: MouseEvent, only?: boolean): boolean {\n if (only) {\n return event.buttons === 1;\n }\n\n return (event.buttons & 1) === 1;\n }\n private _pointerEventToSignatureEvent(\n event: MouseEvent | PointerEvent,\n ): SignatureEvent {\n return {\n event: event,\n type: event.type,\n x: event.clientX,\n y: event.clientY,\n pressure: 'pressure' in event ? event.pressure : 0,\n };\n }\n\n private _touchEventToSignatureEvent(event: TouchEvent): SignatureEvent {\n const touch = event.changedTouches[0];\n return {\n event: event,\n type: event.type,\n x: touch.clientX,\n y: touch.clientY,\n pressure: touch.force,\n };\n }\n\n // Event handlers\n private _handleMouseDown = (event: MouseEvent): void => {\n if (!this._isLeftButtonPressed(event, true) || this._drawingStroke) {\n return;\n }\n this._strokeBegin(this._pointerEventToSignatureEvent(event));\n };\n\n private _handleMouseMove = (event: MouseEvent): void => {\n if (!this._isLeftButtonPressed(event, true) || !this._drawingStroke) {\n // Stop when not pressing primary button or pressing multiple buttons\n this._strokeEnd(this._pointerEventToSignatureEvent(event), false);\n return;\n }\n\n this._strokeMoveUpdate(this._pointerEventToSignatureEvent(event));\n };\n\n private _handleMouseUp = (event: MouseEvent): void => {\n if (this._isLeftButtonPressed(event)) {\n return;\n }\n\n this._strokeEnd(this._pointerEventToSignatureEvent(event));\n };\n\n private _handleTouchStart = (event: TouchEvent): void => {\n if (event.targetTouches.length !== 1 || this._drawingStroke) {\n return;\n }\n\n // Prevent scrolling.\n if (event.cancelable) {\n event.preventDefault();\n }\n\n this._strokeBegin(this._touchEventToSignatureEvent(event));\n };\n\n private _handleTouchMove = (event: TouchEvent): void => {\n if (event.targetTouches.length !== 1) {\n return;\n }\n\n // Prevent scrolling.\n if (event.cancelable) {\n event.preventDefault();\n }\n\n if (!this._drawingStroke) {\n this._strokeEnd(this._touchEventToSignatureEvent(event), false);\n return;\n }\n\n this._strokeMoveUpdate(this._touchEventToSignatureEvent(event));\n };\n\n private _handleTouchEnd = (event: TouchEvent): void => {\n if (event.targetTouches.length !== 0) {\n return;\n }\n\n if (event.cancelable) {\n event.preventDefault();\n }\n\n this.canvas.removeEventListener('touchmove', this._handleTouchMove);\n\n this._strokeEnd(this._touchEventToSignatureEvent(event));\n };\n\n private _handlePointerDown = (event: PointerEvent): void => {\n if (!this._isLeftButtonPressed(event) || this._drawingStroke) {\n return;\n }\n\n event.preventDefault();\n\n this._strokeBegin(this._pointerEventToSignatureEvent(event));\n };\n\n private _handlePointerMove = (event: PointerEvent): void => {\n if (!this._isLeftButtonPressed(event, true) || !this._drawingStroke) {\n // Stop when primary button not pressed or multiple buttons pressed\n this._strokeEnd(this._pointerEventToSignatureEvent(event), false);\n return;\n }\n\n event.preventDefault();\n this._strokeMoveUpdate(this._pointerEventToSignatureEvent(event));\n };\n\n private _handlePointerUp = (event: PointerEvent): void => {\n if (this._isLeftButtonPressed(event)) {\n return;\n }\n\n event.preventDefault();\n this._strokeEnd(this._pointerEventToSignatureEvent(event));\n };\n\n private _getPointGroupOptions(group?: PointGroup): PointGroupOptions {\n return {\n penColor: group && 'penColor' in group ? group.penColor : this.penColor,\n dotSize: group && 'dotSize' in group ? group.dotSize : this.dotSize,\n minWidth: group && 'minWidth' in group ? group.minWidth : this.minWidth,\n maxWidth: group && 'maxWidth' in group ? group.maxWidth : this.maxWidth,\n velocityFilterWeight:\n group && 'velocityFilterWeight' in group\n ? group.velocityFilterWeight\n : this.velocityFilterWeight,\n compositeOperation:\n group && 'compositeOperation' in group\n ? group.compositeOperation\n : this.compositeOperation,\n };\n }\n\n // Private methods\n private _strokeBegin(event: SignatureEvent): void {\n const cancelled = !this.dispatchEvent(\n new CustomEvent('beginStroke', { detail: event, cancelable: true }),\n );\n if (cancelled) {\n return;\n }\n\n const { addEventListener } = this._getListenerFunctions();\n switch (event.event.type) {\n case 'mousedown':\n addEventListener('mousemove', this._handleMouseMove);\n addEventListener('mouseup', this._handleMouseUp);\n break;\n case 'touchstart':\n addEventListener('touchmove', this._handleTouchMove);\n addEventListener('touchend', this._handleTouchEnd);\n break;\n case 'pointerdown':\n addEventListener('pointermove', this._handlePointerMove);\n addEventListener('pointerup', this._handlePointerUp);\n break;\n default:\n // do nothing\n }\n\n this._drawingStroke = true;\n\n const pointGroupOptions = this._getPointGroupOptions();\n\n const newPointGroup: PointGroup = {\n ...pointGroupOptions,\n points: [],\n };\n\n this._data.push(newPointGroup);\n this._reset(pointGroupOptions);\n this._strokeUpdate(event);\n }\n\n private _strokeUpdate(event: SignatureEvent): void {\n if (!this._drawingStroke) {\n return;\n }\n\n if (this._data.length === 0) {\n // This can happen if clear() was called while a signature is still in progress,\n // or if there is a race condition between start/update events.\n this._strokeBegin(event);\n return;\n }\n\n this.dispatchEvent(\n new CustomEvent('beforeUpdateStroke', { detail: event }),\n );\n\n const point = this._createPoint(event.x, event.y, event.pressure);\n const lastPointGroup = this._data[this._data.length - 1];\n const lastPoints = lastPointGroup.points;\n const lastPoint =\n lastPoints.length > 0 && lastPoints[lastPoints.length - 1];\n const isLastPointTooClose = lastPoint\n ? point.distanceTo(lastPoint) <= this.minDistance\n : false;\n const pointGroupOptions = this._getPointGroupOptions(lastPointGroup);\n\n // Skip this point if it's too close to the previous one\n if (!lastPoint || !(lastPoint && isLastPointTooClose)) {\n const curve = this._addPoint(point, pointGroupOptions);\n\n if (!lastPoint) {\n this._drawDot(point, pointGroupOptions);\n } else if (curve) {\n this._drawCurve(curve, pointGroupOptions);\n }\n\n lastPoints.push({\n time: point.time,\n x: point.x,\n y: point.y,\n pressure: point.pressure,\n });\n }\n\n this.dispatchEvent(new CustomEvent('afterUpdateStroke', { detail: event }));\n }\n\n private _strokeEnd(event: SignatureEvent, shouldUpdate = true): void {\n this._removeMoveUpEventListeners();\n\n if (!this._drawingStroke) {\n return;\n }\n\n if (shouldUpdate) {\n this._strokeUpdate(event);\n }\n\n this._drawingStroke = false;\n this.dispatchEvent(new CustomEvent('endStroke', { detail: event }));\n }\n\n private _handlePointerEvents(): void {\n this._drawingStroke = false;\n\n this.canvas.addEventListener('pointerdown', this._handlePointerDown);\n }\n\n private _handleMouseEvents(): void {\n this._drawingStroke = false;\n\n this.canvas.addEventListener('mousedown', this._handleMouseDown);\n }\n\n private _handleTouchEvents(): void {\n this.canvas.addEventListener('touchstart', this._handleTouchStart);\n }\n\n // Called when a new line is started\n private _reset(options: PointGroupOptions): void {\n this._lastPoints = [];\n this._lastVelocity = 0;\n this._lastWidth = (options.minWidth + options.maxWidth) / 2;\n this._ctx.fillStyle = options.penColor;\n this._ctx.globalCompositeOperation = options.compositeOperation;\n }\n\n private _createPoint(x: number, y: number, pressure: number): Point {\n const rect = this.canvas.getBoundingClientRect();\n\n return new Point(\n x - rect.left,\n y - rect.top,\n pressure,\n new Date().getTime(),\n );\n }\n\n // Add point to _lastPoints array and generate a new curve if there are enough points (i.e. 3)\n private _addPoint(point: Point, options: PointGroupOptions): Bezier | null {\n const { _lastPoints } = this;\n\n _lastPoints.push(point);\n\n if (_lastPoints.length > 2) {\n // To reduce the initial lag make it work with 3 points\n // by copying the first point to the beginning.\n if (_lastPoints.length === 3) {\n _lastPoints.unshift(_lastPoints[0]);\n }\n\n // _points array will always have 4 points here.\n const widths = this._calculateCurveWidths(\n _lastPoints[1],\n _lastPoints[2],\n options,\n );\n const curve = Bezier.fromPoints(_lastPoints, widths);\n\n // Remove the first element from the list, so that there are no more than 4 points at any time.\n _lastPoints.shift();\n\n return curve;\n }\n\n return null;\n }\n\n private _calculateCurveWidths(\n startPoint: Point,\n endPoint: Point,\n options: PointGroupOptions,\n ): { start: number; end: number } {\n const velocity =\n options.velocityFilterWeight * endPoint.velocityFrom(startPoint) +\n (1 - options.velocityFilterWeight) * this._lastVelocity;\n\n const newWidth = this._strokeWidth(velocity, options);\n\n const widths = {\n end: newWidth,\n start: this._lastWidth,\n };\n\n this._lastVelocity = velocity;\n this._lastWidth = newWidth;\n\n return widths;\n }\n\n private _strokeWidth(velocity: number, options: PointGroupOptions): number {\n return Math.max(options.maxWidth / (velocity + 1), options.minWidth);\n }\n\n private _drawCurveSegment(x: number, y: number, width: number): void {\n const ctx = this._ctx;\n\n ctx.moveTo(x, y);\n ctx.arc(x, y, width, 0, 2 * Math.PI, false);\n this._isEmpty = false;\n }\n\n private _drawCurve(curve: Bezier, options: PointGroupOptions): void {\n const ctx = this._ctx;\n const widthDelta = curve.endWidth - curve.startWidth;\n // '2' is just an arbitrary number here. If only length is used, then\n // there are gaps between curve segments :/\n const drawSteps = Math.ceil(curve.length()) * 2;\n\n ctx.beginPath();\n ctx.fillStyle = options.penColor;\n\n for (let i = 0; i < drawSteps; i += 1) {\n // Calculate the Bezier (x, y) coordinate for this step.\n const t = i / drawSteps;\n const tt = t * t;\n const ttt = tt * t;\n const u = 1 - t;\n const uu = u * u;\n const uuu = uu * u;\n\n let x = uuu * curve.startPoint.x;\n x += 3 * uu * t * curve.control1.x;\n x += 3 * u * tt * curve.control2.x;\n x += ttt * curve.endPoint.x;\n\n let y = uuu * curve.startPoint.y;\n y += 3 * uu * t * curve.control1.y;\n y += 3 * u * tt * curve.control2.y;\n y += ttt * curve.endPoint.y;\n\n const width = Math.min(\n curve.startWidth + ttt * widthDelta,\n options.maxWidth,\n );\n this._drawCurveSegment(x, y, width);\n }\n\n ctx.closePath();\n ctx.fill();\n }\n\n private _drawDot(point: BasicPoint, options: PointGroupOptions): void {\n const ctx = this._ctx;\n const width =\n options.dotSize > 0\n ? options.dotSize\n : (options.minWidth + options.maxWidth) / 2;\n\n ctx.beginPath();\n this._drawCurveSegment(point.x, point.y, width);\n ctx.closePath();\n ctx.fillStyle = options.penColor;\n ctx.fill();\n }\n\n private _fromData(\n pointGroups: PointGroup[],\n drawCurve: SignaturePad['_drawCurve'],\n drawDot: SignaturePad['_drawDot'],\n ): void {\n for (const group of pointGroups) {\n const { points } = group;\n const pointGroupOptions = this._getPointGroupOptions(group);\n\n if (points.length > 1) {\n for (let j = 0; j < points.length; j += 1) {\n const basicPoint = points[j];\n const point = new Point(\n basicPoint.x,\n basicPoint.y,\n basicPoint.pressure,\n basicPoint.time,\n );\n\n if (j === 0) {\n this._reset(pointGroupOptions);\n }\n\n const curve = this._addPoint(point, pointGroupOptions);\n\n if (curve) {\n drawCurve(curve, pointGroupOptions);\n }\n }\n } else {\n this._reset(pointGroupOptions);\n\n drawDot(points[0], pointGroupOptions);\n }\n }\n }\n\n public toSVG({ includeBackgroundColor = false }: ToSVGOptions = {}): string {\n const pointGroups = this._data;\n const ratio = Math.max(window.devicePixelRatio || 1, 1);\n const minX = 0;\n const minY = 0;\n const maxX = this.canvas.width / ratio;\n const maxY = this.canvas.height / ratio;\n const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');\n\n svg.setAttribute('xmlns', 'http://www.w3.org/2000/svg');\n svg.setAttribute('xmlns:xlink', 'http://www.w3.org/1999/xlink');\n svg.setAttribute('viewBox', `${minX} ${minY} ${maxX} ${maxY}`);\n svg.setAttribute('width', maxX.toString());\n svg.setAttribute('height', maxY.toString());\n\n if (includeBackgroundColor && this.backgroundColor) {\n const rect = document.createElement('rect');\n rect.setAttribute('width', '100%');\n rect.setAttribute('height', '100%');\n rect.setAttribute('fill', this.backgroundColor);\n\n svg.appendChild(rect);\n }\n\n this._fromData(\n pointGroups,\n\n (curve, { penColor }) => {\n const path = document.createElement('path');\n\n // Need to check curve for NaN values, these pop up when drawing\n // lines on the canvas that are not continuous. E.g. Sharp corners\n // or stopping mid-stroke and than continuing without lifting mouse.\n /* eslint-disable no-restricted-globals */\n if (\n !isNaN(curve.control1.x) &&\n !isNaN(curve.control1.y) &&\n !isNaN(curve.control2.x) &&\n !isNaN(curve.control2.y)\n ) {\n const attr =\n `M ${curve.startPoint.x.toFixed(3)},${curve.startPoint.y.toFixed(\n 3,\n )} ` +\n `C ${curve.control1.x.toFixed(3)},${curve.control1.y.toFixed(3)} ` +\n `${curve.control2.x.toFixed(3)},${curve.control2.y.toFixed(3)} ` +\n `${curve.endPoint.x.toFixed(3)},${curve.endPoint.y.toFixed(3)}`;\n path.setAttribute('d', attr);\n path.setAttribute('stroke-width', (curve.endWidth * 2.25).toFixed(3));\n path.setAttribute('stroke', penColor);\n path.setAttribute('fill', 'none');\n path.setAttribute('stroke-linecap', 'round');\n\n svg.appendChild(path);\n }\n /* eslint-enable no-restricted-globals */\n },\n\n (point, { penColor, dotSize, minWidth, maxWidth }) => {\n const circle = document.createElement('circle');\n const size = dotSize > 0 ? dotSize : (minWidth + maxWidth) / 2;\n circle.setAttribute('r', size.toString());\n circle.setAttribute('cx', point.x.toString());\n circle.setAttribute('cy', point.y.toString());\n circle.setAttribute('fill', penColor);\n\n svg.appendChild(circle);\n },\n );\n\n return svg.outerHTML;\n }\n}\n","/* eslint-disable @typescript-eslint/no-explicit-any, @typescript-eslint/no-this-alias */\n// Slightly simplified version of http://stackoverflow.com/a/27078401/815507\n\nexport function throttle(\n fn: (...args: any[]) => any,\n wait = 250,\n): (this: any, ...args: any[]) => any {\n let previous = 0;\n let timeout: number | null = null;\n let result: any;\n let storedContext: any;\n let storedArgs: any[];\n\n const later = (): void => {\n previous = Date.now();\n timeout = null;\n result = fn.apply(storedContext, storedArgs);\n\n if (!timeout) {\n storedContext = null;\n storedArgs = [];\n }\n };\n\n return function wrapper(this: any, ...args: any[]): any {\n const now = Date.now();\n const remaining = wait - (now - previous);\n\n storedContext = this;\n storedArgs = args;\n\n if (remaining <= 0 || remaining > wait) {\n if (timeout) {\n clearTimeout(timeout);\n timeout = null;\n }\n\n previous = now;\n result = fn.apply(storedContext, storedArgs);\n\n if (!timeout) {\n storedContext = null;\n storedArgs = [];\n }\n } else if (!timeout) {\n timeout = window.setTimeout(later, remaining);\n }\n\n return result;\n };\n}\n"],"names":["Point","constructor","x","y","pressure","time","isNaN","Error","this","Date","now","distanceTo","start","Math","sqrt","pow","equals","other","velocityFrom","Bezier","fromPoints","points","widths","c2","calculateControlPoints","c3","c1","end","s1","s2","s3","dx1","dy1","dx2","dy2","m1","m2","l1","l2","k","cm","tx","ty","startPoint","control2","control1","endPoint","startWidth","endWidth","length","px","py","i","t","cx","point","cy","xdiff","ydiff","SignatureEventTarget","_et","EventTarget","error","document","addEventListener","type","listener","options","dispatchEvent","event","removeEventListener","callback","SignaturePad","canvas","super","_drawingStroke","_isEmpty","_lastPoints","_data","_lastVelocity","_lastWidth","_handleMouseDown","_isLeftButtonPressed","_strokeBegin","_pointerEventToSignatureEvent","_handleMouseMove","_strokeMoveUpdate","_strokeEnd","_handleMouseUp","_handleTouchStart","targetTouches","cancelable","preventDefault","_touchEventToSignatureEvent","_handleTouchMove","_handleTouchEnd","_handlePointerDown","_handlePointerMove","_handlePointerUp","velocityFilterWeight","minWidth","maxWidth","throttle","minDistance","dotSize","penColor","backgroundColor","compositeOperation","canvasContextOptions","fn","wait","result","storedContext","storedArgs","previous","timeout","later","apply","args","remaining","clearTimeout","window","setTimeout","prototype","_strokeUpdate","_ctx","getContext","clear","on","ctx","fillStyle","clearRect","width","height","fillRect","_reset","_getPointGroupOptions","fromDataURL","dataUrl","Promise","resolve","reject","image","Image","ratio","devicePixelRatio","xOffset","yOffset","onload","drawImage","onerror","crossOrigin","src","toDataURL","encoderOptions","undefined","btoa","toSVG","style","touchAction","msTouchAction","userSelect","isIOS","test","navigator","userAgent","PointerEvent","_handlePointerEvents","_handleMouseEvents","_handleTouchEvents","off","_removeMoveUpEventListeners","_getListenerFunctions","canvasWindow","ownerDocument","_a","defaultView","bind","isEmpty","fromData","pointGroups","_fromData","_drawCurve","_drawDot","concat","toData","only","buttons","clientX","clientY","touch","changedTouches","force","group","CustomEvent","detail","pointGroupOptions","newPointGroup","push","_createPoint","lastPointGroup","lastPoints","lastPoint","isLastPointTooClose","curve","_addPoint","shouldUpdate","globalCompositeOperation","rect","getBoundingClientRect","left","top","getTime","unshift","_calculateCurveWidths","shift","velocity","newWidth","_strokeWidth","max","_drawCurveSegment","moveTo","arc","PI","widthDelta","drawSteps","ceil","beginPath","tt","ttt","u","uu","uuu","min","closePath","fill","drawCurve","drawDot","j","basicPoint","includeBackgroundColor","maxX","maxY","svg","createElementNS","setAttribute","toString","createElement","appendChild","path","attr","toFixed","circle","size","outerHTML"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"signature_pad.umd.min.js","sources":["../src/point.ts","../src/bezier.ts","../src/signature_event_target.ts","../src/signature_pad.ts","../src/throttle.ts"],"sourcesContent":["// Interface for point data structure used e.g. in SignaturePad#fromData method\nexport interface BasicPoint {\n x: number;\n y: number;\n pressure: number;\n time: number;\n}\n\nexport class Point implements BasicPoint {\n public x: number;\n public y: number;\n public pressure: number;\n public time: number;\n\n constructor(x: number, y: number, pressure?: number, time?: number) {\n if (isNaN(x) || isNaN(y)) {\n throw new Error(`Point is invalid: (${x}, ${y})`);\n }\n this.x = +x;\n this.y = +y;\n this.pressure = pressure || 0;\n this.time = time || Date.now();\n }\n\n public distanceTo(start: BasicPoint): number {\n return Math.sqrt(\n Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2),\n );\n }\n\n public equals(other: BasicPoint): boolean {\n return (\n this.x === other.x &&\n this.y === other.y &&\n this.pressure === other.pressure &&\n this.time === other.time\n );\n }\n\n public velocityFrom(start: BasicPoint): number {\n return this.time !== start.time\n ? this.distanceTo(start) / (this.time - start.time)\n : 0;\n }\n}\n","import { BasicPoint, Point } from './point';\n\nexport class Bezier {\n public static fromPoints(\n points: Point[],\n widths: { start: number; end: number },\n ): Bezier {\n const c2 = this.calculateControlPoints(points[0], points[1], points[2]).c2;\n const c3 = this.calculateControlPoints(points[1], points[2], points[3]).c1;\n\n return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);\n }\n\n private static calculateControlPoints(\n s1: BasicPoint,\n s2: BasicPoint,\n s3: BasicPoint,\n ): {\n c1: BasicPoint;\n c2: BasicPoint;\n } {\n const dx1 = s1.x - s2.x;\n const dy1 = s1.y - s2.y;\n const dx2 = s2.x - s3.x;\n const dy2 = s2.y - s3.y;\n\n const m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };\n const m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };\n\n const l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);\n const l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);\n\n const dxm = m1.x - m2.x;\n const dym = m1.y - m2.y;\n\n const k = l2 / (l1 + l2);\n const cm = { x: m2.x + dxm * k, y: m2.y + dym * k };\n\n const tx = s2.x - cm.x;\n const ty = s2.y - cm.y;\n\n return {\n c1: new Point(m1.x + tx, m1.y + ty),\n c2: new Point(m2.x + tx, m2.y + ty),\n };\n }\n\n constructor(\n public startPoint: Point,\n public control2: BasicPoint,\n public control1: BasicPoint,\n public endPoint: Point,\n public startWidth: number,\n public endWidth: number,\n ) {}\n\n // Returns approximated length. Code taken from https://www.lemoda.net/maths/bezier-length/index.html.\n public length(): number {\n const steps = 10;\n let length = 0;\n let px;\n let py;\n\n for (let i = 0; i <= steps; i += 1) {\n const t = i / steps;\n const cx = this.point(\n t,\n this.startPoint.x,\n this.control1.x,\n this.control2.x,\n this.endPoint.x,\n );\n const cy = this.point(\n t,\n this.startPoint.y,\n this.control1.y,\n this.control2.y,\n this.endPoint.y,\n );\n\n if (i > 0) {\n const xdiff = cx - (px as number);\n const ydiff = cy - (py as number);\n\n length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);\n }\n\n px = cx;\n py = cy;\n }\n\n return length;\n }\n\n // Calculate parametric value of x or y given t and the four point coordinates of a cubic bezier curve.\n private point(\n t: number,\n start: number,\n c1: number,\n c2: number,\n end: number,\n ): number {\n // prettier-ignore\n return ( start * (1.0 - t) * (1.0 - t) * (1.0 - t))\n + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)\n + (3.0 * c2 * (1.0 - t) * t * t)\n + ( end * t * t * t);\n }\n}\n","export class SignatureEventTarget {\n /* tslint:disable: variable-name */\n private _et: EventTarget;\n /* tslint:enable: variable-name */\n\n constructor() {\n try {\n this._et = new EventTarget();\n } catch (error) {\n // Using document as EventTarget to support iOS 13 and older.\n // Because EventTarget constructor just exists at iOS 14 and later.\n this._et = document;\n }\n }\n\n addEventListener(\n type: string,\n listener: EventListenerOrEventListenerObject | null,\n options?: boolean | AddEventListenerOptions,\n ): void {\n this._et.addEventListener(type, listener, options);\n }\n\n dispatchEvent(event: Event): boolean {\n return this._et.dispatchEvent(event);\n }\n\n removeEventListener(\n type: string,\n callback: EventListenerOrEventListenerObject | null,\n options?: boolean | EventListenerOptions,\n ): void {\n this._et.removeEventListener(type, callback, options);\n }\n}\n","/**\n * The main idea and some parts of the code (e.g. drawing variable width Bézier curve) are taken from:\n * http://corner.squareup.com/2012/07/smoother-signatures.html\n *\n * Implementation of interpolation using cubic Bézier curves is taken from:\n * https://web.archive.org/web/20160323213433/http://www.benknowscode.com/2012/09/path-interpolation-using-cubic-bezier_9742.html\n *\n * Algorithm for approximated length of a Bézier curve is taken from:\n * http://www.lemoda.net/maths/bezier-length/index.html\n */\n\nimport { Bezier } from './bezier';\nimport { BasicPoint, Point } from './point';\nimport { SignatureEventTarget } from './signature_event_target';\nimport { throttle } from './throttle';\n\ndeclare global {\n interface CSSStyleDeclaration {\n msTouchAction: string | null;\n }\n}\n\nexport interface SignatureEvent {\n event: MouseEvent | TouchEvent | PointerEvent;\n type: string;\n x: number;\n y: number;\n pressure: number;\n}\n\nexport interface FromDataOptions {\n clear?: boolean;\n}\n\nexport interface ToSVGOptions {\n includeBackgroundColor?: boolean;\n}\n\nexport interface PointGroupOptions {\n dotSize: number;\n minWidth: number;\n maxWidth: number;\n penColor: string;\n highlightColor: string;\n highlightSize: number;\n velocityFilterWeight: number;\n /**\n * This is the globalCompositeOperation for the line.\n * *default: 'source-over'*\n * @see https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/globalCompositeOperation\n */\n compositeOperation: GlobalCompositeOperation;\n}\n\nexport interface Options extends Partial {\n minDistance?: number;\n backgroundColor?: string;\n throttle?: number;\n canvasContextOptions?: CanvasRenderingContext2DSettings;\n}\n\nexport interface PointGroup extends PointGroupOptions {\n points: BasicPoint[];\n}\n\nexport default class SignaturePad extends SignatureEventTarget {\n // Public stuff\n public dotSize: number;\n public minWidth: number;\n public maxWidth: number;\n public penColor: string;\n public highlightColor: string;\n public highlightSize: number;\n public minDistance: number;\n public velocityFilterWeight: number;\n public compositeOperation: GlobalCompositeOperation;\n public backgroundColor: string;\n public throttle: number;\n public canvasContextOptions: CanvasRenderingContext2DSettings;\n\n // Private stuff\n /* tslint:disable: variable-name */\n private _ctx: CanvasRenderingContext2D;\n private _drawingStroke = false;\n private _isEmpty = true;\n private _lastPoints: Point[] = []; // Stores up to 4 most recent points; used to generate a new curve\n private _data: PointGroup[] = []; // Stores all points in groups (one group per line or dot)\n private _lastVelocity = 0;\n private _lastWidth = 0;\n private _strokeMoveUpdate: (event: SignatureEvent) => void;\n /* tslint:enable: variable-name */\n\n constructor(\n private canvas: HTMLCanvasElement,\n options: Options = {},\n ) {\n super();\n this.velocityFilterWeight = options.velocityFilterWeight ?? 0.7;\n this.minWidth = options.minWidth ?? 0.5;\n this.maxWidth = options.maxWidth ?? 2.5;\n this.throttle = options.throttle ?? 16; // in milliseconds\n this.minDistance = options.minDistance ?? 5; // in pixels\n this.dotSize = options.dotSize ?? 0;\n this.penColor = options.penColor ?? 'black';\n this.highlightColor = options.highlightColor ?? '';\n this.highlightSize = options.highlightSize ?? 1;\n this.backgroundColor = options.backgroundColor ?? 'rgba(0,0,0,0)';\n this.compositeOperation = options.compositeOperation ?? 'source-over';\n this.canvasContextOptions = options.canvasContextOptions ?? {};\n\n this._strokeMoveUpdate = this.throttle\n ? throttle(SignaturePad.prototype._strokeUpdate, this.throttle)\n : SignaturePad.prototype._strokeUpdate;\n this._ctx = canvas.getContext(\n '2d',\n this.canvasContextOptions,\n ) as CanvasRenderingContext2D;\n\n this.clear();\n\n // Enable mouse and touch event handlers\n this.on();\n }\n\n public clear(): void {\n const { _ctx: ctx, canvas } = this;\n\n // Clear canvas using background color\n ctx.fillStyle = this.backgroundColor;\n ctx.clearRect(0, 0, canvas.width, canvas.height);\n ctx.fillRect(0, 0, canvas.width, canvas.height);\n\n this._data = [];\n this._reset(this._getPointGroupOptions());\n this._isEmpty = true;\n }\n\n public fromDataURL(\n dataUrl: string,\n options: {\n ratio?: number;\n width?: number;\n height?: number;\n xOffset?: number;\n yOffset?: number;\n } = {},\n ): Promise {\n return new Promise((resolve, reject) => {\n const image = new Image();\n const ratio = options.ratio || window.devicePixelRatio || 1;\n const width = options.width || this.canvas.width / ratio;\n const height = options.height || this.canvas.height / ratio;\n const xOffset = options.xOffset || 0;\n const yOffset = options.yOffset || 0;\n\n this._reset(this._getPointGroupOptions());\n\n image.onload = (): void => {\n this._ctx.drawImage(image, xOffset, yOffset, width, height);\n resolve();\n };\n image.onerror = (error): void => {\n reject(error);\n };\n image.crossOrigin = 'anonymous';\n image.src = dataUrl;\n\n this._isEmpty = false;\n });\n }\n\n public toDataURL(\n type: 'image/svg+xml',\n encoderOptions?: ToSVGOptions,\n ): string;\n public toDataURL(type?: string, encoderOptions?: number): string;\n public toDataURL(\n type = 'image/png',\n encoderOptions?: number | ToSVGOptions | undefined,\n ): string {\n switch (type) {\n case 'image/svg+xml':\n if (typeof encoderOptions !== 'object') {\n encoderOptions = undefined;\n }\n return `data:image/svg+xml;base64,${btoa(\n this.toSVG(encoderOptions as ToSVGOptions),\n )}`;\n default:\n if (typeof encoderOptions !== 'number') {\n encoderOptions = undefined;\n }\n return this.canvas.toDataURL(type, encoderOptions);\n }\n }\n\n public on(): void {\n // Disable panning/zooming when touching canvas element\n this.canvas.style.touchAction = 'none';\n this.canvas.style.msTouchAction = 'none';\n this.canvas.style.userSelect = 'none';\n\n const isIOS =\n /Macintosh/.test(navigator.userAgent) && 'ontouchstart' in document;\n\n // The \"Scribble\" feature of iOS intercepts point events. So that we can\n // lose some of them when tapping rapidly. Use touch events for iOS\n // platforms to prevent it. See\n // https://developer.apple.com/forums/thread/664108 for more information.\n if (window.PointerEvent && !isIOS) {\n this._handlePointerEvents();\n } else {\n this._handleMouseEvents();\n\n if ('ontouchstart' in window) {\n this._handleTouchEvents();\n }\n }\n }\n\n public off(): void {\n // Enable panning/zooming when touching canvas element\n this.canvas.style.touchAction = 'auto';\n this.canvas.style.msTouchAction = 'auto';\n this.canvas.style.userSelect = 'auto';\n\n this.canvas.removeEventListener('pointerdown', this._handlePointerDown);\n this.canvas.removeEventListener('mousedown', this._handleMouseDown);\n this.canvas.removeEventListener('touchstart', this._handleTouchStart);\n\n this._removeMoveUpEventListeners();\n }\n\n private _getListenerFunctions() {\n const canvasWindow =\n window.document === this.canvas.ownerDocument\n ? window\n : this.canvas.ownerDocument.defaultView ?? this.canvas.ownerDocument;\n\n return {\n addEventListener: canvasWindow.addEventListener.bind(\n canvasWindow,\n ) as typeof window.addEventListener,\n removeEventListener: canvasWindow.removeEventListener.bind(\n canvasWindow,\n ) as typeof window.removeEventListener,\n };\n }\n\n private _removeMoveUpEventListeners(): void {\n const { removeEventListener } = this._getListenerFunctions();\n removeEventListener('pointermove', this._handlePointerMove);\n removeEventListener('pointerup', this._handlePointerUp);\n\n removeEventListener('mousemove', this._handleMouseMove);\n removeEventListener('mouseup', this._handleMouseUp);\n\n removeEventListener('touchmove', this._handleTouchMove);\n removeEventListener('touchend', this._handleTouchEnd);\n }\n\n public isEmpty(): boolean {\n return this._isEmpty;\n }\n\n public fromData(\n pointGroups: PointGroup[],\n { clear = true }: FromDataOptions = {},\n ): void {\n if (clear) {\n this.clear();\n }\n\n this._fromData(\n pointGroups,\n this._drawCurve.bind(this),\n this._drawDot.bind(this),\n );\n\n this._data = this._data.concat(pointGroups);\n }\n\n public toData(): PointGroup[] {\n return this._data;\n }\n\n public _isLeftButtonPressed(event: MouseEvent, only?: boolean): boolean {\n if (only) {\n return event.buttons === 1;\n }\n\n return (event.buttons & 1) === 1;\n }\n private _pointerEventToSignatureEvent(\n event: MouseEvent | PointerEvent,\n ): SignatureEvent {\n return {\n event: event,\n type: event.type,\n x: event.clientX,\n y: event.clientY,\n pressure: 'pressure' in event ? event.pressure : 0,\n };\n }\n\n private _touchEventToSignatureEvent(event: TouchEvent): SignatureEvent {\n const touch = event.changedTouches[0];\n return {\n event: event,\n type: event.type,\n x: touch.clientX,\n y: touch.clientY,\n pressure: touch.force,\n };\n }\n\n // Event handlers\n private _handleMouseDown = (event: MouseEvent): void => {\n if (!this._isLeftButtonPressed(event, true) || this._drawingStroke) {\n return;\n }\n this._strokeBegin(this._pointerEventToSignatureEvent(event));\n };\n\n private _handleMouseMove = (event: MouseEvent): void => {\n if (!this._isLeftButtonPressed(event, true) || !this._drawingStroke) {\n // Stop when not pressing primary button or pressing multiple buttons\n this._strokeEnd(this._pointerEventToSignatureEvent(event), false);\n return;\n }\n\n this._strokeMoveUpdate(this._pointerEventToSignatureEvent(event));\n };\n\n private _handleMouseUp = (event: MouseEvent): void => {\n if (this._isLeftButtonPressed(event)) {\n return;\n }\n\n this._strokeEnd(this._pointerEventToSignatureEvent(event));\n };\n\n private _handleTouchStart = (event: TouchEvent): void => {\n if (event.targetTouches.length !== 1 || this._drawingStroke) {\n return;\n }\n\n // Prevent scrolling.\n if (event.cancelable) {\n event.preventDefault();\n }\n\n this._strokeBegin(this._touchEventToSignatureEvent(event));\n };\n\n private _handleTouchMove = (event: TouchEvent): void => {\n if (event.targetTouches.length !== 1) {\n return;\n }\n\n // Prevent scrolling.\n if (event.cancelable) {\n event.preventDefault();\n }\n\n if (!this._drawingStroke) {\n this._strokeEnd(this._touchEventToSignatureEvent(event), false);\n return;\n }\n\n this._strokeMoveUpdate(this._touchEventToSignatureEvent(event));\n };\n\n private _handleTouchEnd = (event: TouchEvent): void => {\n if (event.targetTouches.length !== 0) {\n return;\n }\n\n if (event.cancelable) {\n event.preventDefault();\n }\n\n this.canvas.removeEventListener('touchmove', this._handleTouchMove);\n\n this._strokeEnd(this._touchEventToSignatureEvent(event));\n };\n\n private _handlePointerDown = (event: PointerEvent): void => {\n if (!this._isLeftButtonPressed(event) || this._drawingStroke) {\n return;\n }\n\n event.preventDefault();\n\n this._strokeBegin(this._pointerEventToSignatureEvent(event));\n };\n\n private _handlePointerMove = (event: PointerEvent): void => {\n if (!this._isLeftButtonPressed(event, true) || !this._drawingStroke) {\n // Stop when primary button not pressed or multiple buttons pressed\n this._strokeEnd(this._pointerEventToSignatureEvent(event), false);\n return;\n }\n\n event.preventDefault();\n this._strokeMoveUpdate(this._pointerEventToSignatureEvent(event));\n };\n\n private _handlePointerUp = (event: PointerEvent): void => {\n if (this._isLeftButtonPressed(event)) {\n return;\n }\n\n event.preventDefault();\n this._strokeEnd(this._pointerEventToSignatureEvent(event));\n };\n\n private _getPointGroupOptions(group?: PointGroup): PointGroupOptions {\n return {\n penColor: group && 'penColor' in group ? group.penColor : this.penColor,\n highlightColor:\n group && 'highlightColor' in group\n ? group.highlightColor\n : this.highlightColor,\n highlightSize:\n group && 'highlightSize' in group\n ? group.highlightSize\n : this.highlightSize,\n dotSize: group && 'dotSize' in group ? group.dotSize : this.dotSize,\n minWidth: group && 'minWidth' in group ? group.minWidth : this.minWidth,\n maxWidth: group && 'maxWidth' in group ? group.maxWidth : this.maxWidth,\n velocityFilterWeight:\n group && 'velocityFilterWeight' in group\n ? group.velocityFilterWeight\n : this.velocityFilterWeight,\n compositeOperation:\n group && 'compositeOperation' in group\n ? group.compositeOperation\n : this.compositeOperation,\n };\n }\n\n // Private methods\n private _strokeBegin(event: SignatureEvent): void {\n const cancelled = !this.dispatchEvent(\n new CustomEvent('beginStroke', { detail: event, cancelable: true }),\n );\n if (cancelled) {\n return;\n }\n\n const { addEventListener } = this._getListenerFunctions();\n switch (event.event.type) {\n case 'mousedown':\n addEventListener('mousemove', this._handleMouseMove);\n addEventListener('mouseup', this._handleMouseUp);\n break;\n case 'touchstart':\n addEventListener('touchmove', this._handleTouchMove);\n addEventListener('touchend', this._handleTouchEnd);\n break;\n case 'pointerdown':\n addEventListener('pointermove', this._handlePointerMove);\n addEventListener('pointerup', this._handlePointerUp);\n break;\n default:\n // do nothing\n }\n\n this._drawingStroke = true;\n\n const pointGroupOptions = this._getPointGroupOptions();\n\n const newPointGroup: PointGroup = {\n ...pointGroupOptions,\n points: [],\n };\n\n this._data.push(newPointGroup);\n this._reset(pointGroupOptions);\n this._strokeUpdate(event);\n }\n\n private _strokeUpdate(event: SignatureEvent): void {\n if (!this._drawingStroke) {\n return;\n }\n\n if (this._data.length === 0) {\n // This can happen if clear() was called while a signature is still in progress,\n // or if there is a race condition between start/update events.\n this._strokeBegin(event);\n return;\n }\n\n this.dispatchEvent(\n new CustomEvent('beforeUpdateStroke', { detail: event }),\n );\n\n const point = this._createPoint(event.x, event.y, event.pressure);\n const lastPointGroup = this._data[this._data.length - 1];\n const lastPoints = lastPointGroup.points;\n const lastPoint =\n lastPoints.length > 0 && lastPoints[lastPoints.length - 1];\n const isLastPointTooClose = lastPoint\n ? point.distanceTo(lastPoint) <= this.minDistance\n : false;\n const pointGroupOptions = this._getPointGroupOptions(lastPointGroup);\n\n // Skip this point if it's too close to the previous one\n if (!lastPoint || !(lastPoint && isLastPointTooClose)) {\n const curve = this._addPoint(point, pointGroupOptions);\n\n lastPoints.push({\n time: point.time,\n x: point.x,\n y: point.y,\n pressure: point.pressure,\n });\n\n if (!lastPoint) {\n if (\n pointGroupOptions.highlightColor &&\n pointGroupOptions.highlightSize\n ) {\n this._drawDot(point, pointGroupOptions, true);\n }\n this._drawDot(point, pointGroupOptions, false);\n } else if (curve) {\n if (\n pointGroupOptions.highlightColor &&\n pointGroupOptions.highlightSize\n ) {\n this._drawAll(\n lastPoints,\n pointGroupOptions,\n this._drawCurve.bind(this),\n true,\n );\n this._drawAll(\n lastPoints,\n pointGroupOptions,\n this._drawCurve.bind(this),\n false,\n );\n } else {\n this._drawCurve(curve, pointGroupOptions, false);\n }\n }\n }\n\n this.dispatchEvent(new CustomEvent('afterUpdateStroke', { detail: event }));\n }\n\n private _strokeEnd(event: SignatureEvent, shouldUpdate = true): void {\n this._removeMoveUpEventListeners();\n\n if (!this._drawingStroke) {\n return;\n }\n\n if (shouldUpdate) {\n this._strokeUpdate(event);\n }\n\n this._drawingStroke = false;\n this.dispatchEvent(new CustomEvent('endStroke', { detail: event }));\n }\n\n private _handlePointerEvents(): void {\n this._drawingStroke = false;\n\n this.canvas.addEventListener('pointerdown', this._handlePointerDown);\n }\n\n private _handleMouseEvents(): void {\n this._drawingStroke = false;\n\n this.canvas.addEventListener('mousedown', this._handleMouseDown);\n }\n\n private _handleTouchEvents(): void {\n this.canvas.addEventListener('touchstart', this._handleTouchStart);\n }\n\n // Called when a new line is started\n private _reset(options: PointGroupOptions): void {\n this._lastPoints = [];\n this._lastVelocity = 0;\n this._lastWidth = (options.minWidth + options.maxWidth) / 2;\n this._ctx.fillStyle = options.penColor;\n this._ctx.globalCompositeOperation = options.compositeOperation;\n }\n\n private _createPoint(x: number, y: number, pressure: number): Point {\n const rect = this.canvas.getBoundingClientRect();\n\n return new Point(\n x - rect.left,\n y - rect.top,\n pressure,\n new Date().getTime(),\n );\n }\n\n // Add point to _lastPoints array and generate a new curve if there are enough points (i.e. 3)\n private _addPoint(point: Point, options: PointGroupOptions): Bezier | null {\n const { _lastPoints } = this;\n\n _lastPoints.push(point);\n\n if (_lastPoints.length > 2) {\n // To reduce the initial lag make it work with 3 points\n // by copying the first point to the beginning.\n if (_lastPoints.length === 3) {\n _lastPoints.unshift(_lastPoints[0]);\n }\n\n // _points array will always have 4 points here.\n const widths = this._calculateCurveWidths(\n _lastPoints[1],\n _lastPoints[2],\n options,\n );\n const curve = Bezier.fromPoints(_lastPoints, widths);\n\n // Remove the first element from the list, so that there are no more than 4 points at any time.\n _lastPoints.shift();\n\n return curve;\n }\n\n return null;\n }\n\n private _calculateCurveWidths(\n startPoint: Point,\n endPoint: Point,\n options: PointGroupOptions,\n ): { start: number; end: number } {\n const velocity =\n options.velocityFilterWeight * endPoint.velocityFrom(startPoint) +\n (1 - options.velocityFilterWeight) * this._lastVelocity;\n\n const newWidth = this._strokeWidth(velocity, options);\n\n const widths = {\n end: newWidth,\n start: this._lastWidth,\n };\n\n this._lastVelocity = velocity;\n this._lastWidth = newWidth;\n\n return widths;\n }\n\n private _strokeWidth(velocity: number, options: PointGroupOptions): number {\n return Math.max(options.maxWidth / (velocity + 1), options.minWidth);\n }\n\n private _drawCurveSegment(x: number, y: number, width: number): void {\n const ctx = this._ctx;\n\n ctx.moveTo(x, y);\n ctx.arc(x, y, width, 0, 2 * Math.PI, false);\n this._isEmpty = false;\n }\n\n private _drawCurve(\n curve: Bezier,\n options: PointGroupOptions,\n isHighlight: boolean,\n ): void {\n const ctx = this._ctx;\n const widthDelta = curve.endWidth - curve.startWidth;\n // '2' is just an arbitrary number here. If only length is used, then\n // there are gaps between curve segments :/\n const drawSteps = Math.ceil(curve.length()) * 2;\n\n ctx.beginPath();\n ctx.fillStyle = isHighlight ? options.highlightColor : options.penColor;\n\n for (let i = 0; i < drawSteps; i += 1) {\n // Calculate the Bezier (x, y) coordinate for this step.\n const t = i / drawSteps;\n const tt = t * t;\n const ttt = tt * t;\n const u = 1 - t;\n const uu = u * u;\n const uuu = uu * u;\n\n let x = uuu * curve.startPoint.x;\n x += 3 * uu * t * curve.control1.x;\n x += 3 * u * tt * curve.control2.x;\n x += ttt * curve.endPoint.x;\n\n let y = uuu * curve.startPoint.y;\n y += 3 * uu * t * curve.control1.y;\n y += 3 * u * tt * curve.control2.y;\n y += ttt * curve.endPoint.y;\n\n const width = Math.min(\n curve.startWidth + ttt * widthDelta,\n options.maxWidth,\n );\n this._drawCurveSegment(\n x,\n y,\n width + (isHighlight ? options.highlightSize * 2 : 0),\n );\n }\n\n ctx.closePath();\n ctx.fill();\n }\n\n private _drawDot(\n point: BasicPoint,\n options: PointGroupOptions,\n isHighlight: boolean,\n ): void {\n const ctx = this._ctx;\n const width =\n options.dotSize > 0\n ? options.dotSize\n : (options.minWidth + options.maxWidth) / 2;\n\n ctx.beginPath();\n this._drawCurveSegment(\n point.x,\n point.y,\n width + (isHighlight ? options.highlightSize * 2 : 0),\n );\n ctx.closePath();\n ctx.fillStyle = isHighlight ? options.highlightColor : options.penColor;\n ctx.fill();\n }\n\n private _fromData(\n pointGroups: PointGroup[],\n drawCurve: typeof this._drawCurve,\n drawDot: typeof this._drawDot,\n ): void {\n for (const group of pointGroups) {\n const { points } = group;\n const pointGroupOptions = this._getPointGroupOptions(group);\n\n if (points.length > 1) {\n if (\n pointGroupOptions.highlightColor &&\n pointGroupOptions.highlightSize\n ) {\n this._drawAll(points, pointGroupOptions, drawCurve, true);\n }\n this._drawAll(points, pointGroupOptions, drawCurve, false);\n } else {\n this._reset(pointGroupOptions);\n if (\n pointGroupOptions.highlightColor &&\n pointGroupOptions.highlightSize\n ) {\n drawDot(points[0], pointGroupOptions, true);\n }\n drawDot(points[0], pointGroupOptions, false);\n }\n }\n }\n\n private _drawAll(\n points: BasicPoint[],\n pointGroupOptions: PointGroupOptions,\n drawCurve: typeof this._drawCurve,\n isHighlight: boolean,\n ): void {\n for (let j = 0; j < points.length; j += 1) {\n const basicPoint = points[j];\n const point = new Point(\n basicPoint.x,\n basicPoint.y,\n basicPoint.pressure,\n basicPoint.time,\n );\n\n if (j === 0) {\n this._reset(pointGroupOptions);\n }\n\n const curve = this._addPoint(point, pointGroupOptions);\n\n if (curve) {\n drawCurve(curve, pointGroupOptions, isHighlight);\n }\n }\n }\n\n public toSVG({ includeBackgroundColor = false }: ToSVGOptions = {}): string {\n const pointGroups = this._data;\n const ratio = Math.max(window.devicePixelRatio || 1, 1);\n const minX = 0;\n const minY = 0;\n const maxX = this.canvas.width / ratio;\n const maxY = this.canvas.height / ratio;\n const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');\n\n svg.setAttribute('xmlns', 'http://www.w3.org/2000/svg');\n svg.setAttribute('xmlns:xlink', 'http://www.w3.org/1999/xlink');\n svg.setAttribute('viewBox', `${minX} ${minY} ${maxX} ${maxY}`);\n svg.setAttribute('width', maxX.toString());\n svg.setAttribute('height', maxY.toString());\n\n if (includeBackgroundColor && this.backgroundColor) {\n const rect = document.createElement('rect');\n rect.setAttribute('width', '100%');\n rect.setAttribute('height', '100%');\n rect.setAttribute('fill', this.backgroundColor);\n\n svg.appendChild(rect);\n }\n\n this._fromData(\n pointGroups,\n\n (curve, { penColor, highlightColor, highlightSize }, isHighlight) => {\n const path = document.createElement('path');\n\n // Need to check curve for NaN values, these pop up when drawing\n // lines on the canvas that are not continuous. E.g. Sharp corners\n // or stopping mid-stroke and than continuing without lifting mouse.\n /* eslint-disable no-restricted-globals */\n if (\n !isNaN(curve.control1.x) &&\n !isNaN(curve.control1.y) &&\n !isNaN(curve.control2.x) &&\n !isNaN(curve.control2.y)\n ) {\n const attr =\n `M ${curve.startPoint.x.toFixed(3)},${curve.startPoint.y.toFixed(\n 3,\n )} ` +\n `C ${curve.control1.x.toFixed(3)},${curve.control1.y.toFixed(3)} ` +\n `${curve.control2.x.toFixed(3)},${curve.control2.y.toFixed(3)} ` +\n `${curve.endPoint.x.toFixed(3)},${curve.endPoint.y.toFixed(3)}`;\n path.setAttribute('d', attr);\n path.setAttribute(\n 'stroke-width',\n (\n (curve.endWidth + (isHighlight ? highlightSize * 2 : 0)) *\n 2.25\n ).toFixed(3),\n );\n path.setAttribute('stroke', isHighlight ? highlightColor : penColor);\n path.setAttribute('fill', 'none');\n path.setAttribute('stroke-linecap', 'round');\n\n svg.appendChild(path);\n }\n /* eslint-enable no-restricted-globals */\n },\n\n (\n point,\n {\n penColor,\n highlightColor,\n highlightSize,\n dotSize,\n minWidth,\n maxWidth,\n },\n isHighlight,\n ) => {\n const circle = document.createElement('circle');\n const size = dotSize > 0 ? dotSize : (minWidth + maxWidth) / 2;\n circle.setAttribute(\n 'r',\n (size + (isHighlight ? highlightSize * 2 : 0)).toString(),\n );\n circle.setAttribute('cx', point.x.toString());\n circle.setAttribute('cy', point.y.toString());\n circle.setAttribute('fill', isHighlight ? highlightColor : penColor);\n\n svg.appendChild(circle);\n },\n );\n\n return svg.outerHTML;\n }\n}\n","/* eslint-disable @typescript-eslint/no-explicit-any, @typescript-eslint/no-this-alias */\n// Slightly simplified version of http://stackoverflow.com/a/27078401/815507\n\nexport function throttle(\n fn: (...args: any[]) => any,\n wait = 250,\n): (this: any, ...args: any[]) => any {\n let previous = 0;\n let timeout: number | null = null;\n let result: any;\n let storedContext: any;\n let storedArgs: any[];\n\n const later = (): void => {\n previous = Date.now();\n timeout = null;\n result = fn.apply(storedContext, storedArgs);\n\n if (!timeout) {\n storedContext = null;\n storedArgs = [];\n }\n };\n\n return function wrapper(this: any, ...args: any[]): any {\n const now = Date.now();\n const remaining = wait - (now - previous);\n\n storedContext = this;\n storedArgs = args;\n\n if (remaining <= 0 || remaining > wait) {\n if (timeout) {\n clearTimeout(timeout);\n timeout = null;\n }\n\n previous = now;\n result = fn.apply(storedContext, storedArgs);\n\n if (!timeout) {\n storedContext = null;\n storedArgs = [];\n }\n } else if (!timeout) {\n timeout = window.setTimeout(later, remaining);\n }\n\n return result;\n };\n}\n"],"names":["Point","constructor","x","y","pressure","time","isNaN","Error","this","Date","now","distanceTo","start","Math","sqrt","pow","equals","other","velocityFrom","Bezier","fromPoints","points","widths","c2","calculateControlPoints","c3","c1","end","s1","s2","s3","dx1","dy1","dx2","dy2","m1","m2","l1","l2","k","cm","tx","ty","startPoint","control2","control1","endPoint","startWidth","endWidth","length","px","py","i","t","cx","point","cy","xdiff","ydiff","SignatureEventTarget","_et","EventTarget","error","document","addEventListener","type","listener","options","dispatchEvent","event","removeEventListener","callback","SignaturePad","canvas","super","_drawingStroke","_isEmpty","_lastPoints","_data","_lastVelocity","_lastWidth","_handleMouseDown","_isLeftButtonPressed","_strokeBegin","_pointerEventToSignatureEvent","_handleMouseMove","_strokeMoveUpdate","_strokeEnd","_handleMouseUp","_handleTouchStart","targetTouches","cancelable","preventDefault","_touchEventToSignatureEvent","_handleTouchMove","_handleTouchEnd","_handlePointerDown","_handlePointerMove","_handlePointerUp","velocityFilterWeight","_a","minWidth","_b","maxWidth","_c","throttle","_d","minDistance","_e","dotSize","_f","penColor","_g","highlightColor","_h","highlightSize","_j","backgroundColor","_k","compositeOperation","_l","canvasContextOptions","_m","fn","wait","result","storedContext","storedArgs","previous","timeout","later","apply","args","remaining","clearTimeout","window","setTimeout","prototype","_strokeUpdate","_ctx","getContext","clear","on","ctx","fillStyle","clearRect","width","height","fillRect","_reset","_getPointGroupOptions","fromDataURL","dataUrl","Promise","resolve","reject","image","Image","ratio","devicePixelRatio","xOffset","yOffset","onload","drawImage","onerror","crossOrigin","src","toDataURL","encoderOptions","undefined","btoa","toSVG","style","touchAction","msTouchAction","userSelect","isIOS","test","navigator","userAgent","PointerEvent","_handlePointerEvents","_handleMouseEvents","_handleTouchEvents","off","_removeMoveUpEventListeners","_getListenerFunctions","canvasWindow","ownerDocument","defaultView","bind","isEmpty","fromData","pointGroups","_fromData","_drawCurve","_drawDot","concat","toData","only","buttons","clientX","clientY","touch","changedTouches","force","group","CustomEvent","detail","pointGroupOptions","newPointGroup","push","_createPoint","lastPointGroup","lastPoints","lastPoint","isLastPointTooClose","curve","_addPoint","_drawAll","shouldUpdate","globalCompositeOperation","rect","getBoundingClientRect","left","top","getTime","unshift","_calculateCurveWidths","shift","velocity","newWidth","_strokeWidth","max","_drawCurveSegment","moveTo","arc","PI","isHighlight","widthDelta","drawSteps","ceil","beginPath","tt","ttt","u","uu","uuu","min","closePath","fill","drawCurve","drawDot","j","basicPoint","includeBackgroundColor","maxX","maxY","svg","createElementNS","setAttribute","toString","createElement","appendChild","path","attr","toFixed","circle","size","outerHTML"],"mappings":"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"} \ No newline at end of file diff --git a/src/signature_pad.ts b/src/signature_pad.ts index 35b8a4c5..4bbe34d7 100644 --- a/src/signature_pad.ts +++ b/src/signature_pad.ts @@ -41,6 +41,8 @@ export interface PointGroupOptions { minWidth: number; maxWidth: number; penColor: string; + highlightColor: string; + highlightSize: number; velocityFilterWeight: number; /** * This is the globalCompositeOperation for the line. @@ -67,6 +69,8 @@ export default class SignaturePad extends SignatureEventTarget { public minWidth: number; public maxWidth: number; public penColor: string; + public highlightColor: string; + public highlightSize: number; public minDistance: number; public velocityFilterWeight: number; public compositeOperation: GlobalCompositeOperation; @@ -91,20 +95,18 @@ export default class SignaturePad extends SignatureEventTarget { options: Options = {}, ) { super(); - this.velocityFilterWeight = options.velocityFilterWeight || 0.7; - this.minWidth = options.minWidth || 0.5; - this.maxWidth = options.maxWidth || 2.5; - this.throttle = ('throttle' in options ? options.throttle : 16) as number; // in milliseconds - this.minDistance = ( - 'minDistance' in options ? options.minDistance : 5 - ) as number; // in pixels - this.dotSize = options.dotSize || 0; - this.penColor = options.penColor || 'black'; - this.backgroundColor = options.backgroundColor || 'rgba(0,0,0,0)'; - this.compositeOperation = options.compositeOperation || 'source-over'; - this.canvasContextOptions = ( - 'canvasContextOptions' in options ? options.canvasContextOptions : {} - ) as CanvasRenderingContext2DSettings; + this.velocityFilterWeight = options.velocityFilterWeight ?? 0.7; + this.minWidth = options.minWidth ?? 0.5; + this.maxWidth = options.maxWidth ?? 2.5; + this.throttle = options.throttle ?? 16; // in milliseconds + this.minDistance = options.minDistance ?? 5; // in pixels + this.dotSize = options.dotSize ?? 0; + this.penColor = options.penColor ?? 'black'; + this.highlightColor = options.highlightColor ?? ''; + this.highlightSize = options.highlightSize ?? 1; + this.backgroundColor = options.backgroundColor ?? 'rgba(0,0,0,0)'; + this.compositeOperation = options.compositeOperation ?? 'source-over'; + this.canvasContextOptions = options.canvasContextOptions ?? {}; this._strokeMoveUpdate = this.throttle ? throttle(SignaturePad.prototype._strokeUpdate, this.throttle) @@ -416,6 +418,14 @@ export default class SignaturePad extends SignatureEventTarget { private _getPointGroupOptions(group?: PointGroup): PointGroupOptions { return { penColor: group && 'penColor' in group ? group.penColor : this.penColor, + highlightColor: + group && 'highlightColor' in group + ? group.highlightColor + : this.highlightColor, + highlightSize: + group && 'highlightSize' in group + ? group.highlightSize + : this.highlightSize, dotSize: group && 'dotSize' in group ? group.dotSize : this.dotSize, minWidth: group && 'minWidth' in group ? group.minWidth : this.minWidth, maxWidth: group && 'maxWidth' in group ? group.maxWidth : this.maxWidth, @@ -501,18 +511,42 @@ export default class SignaturePad extends SignatureEventTarget { if (!lastPoint || !(lastPoint && isLastPointTooClose)) { const curve = this._addPoint(point, pointGroupOptions); - if (!lastPoint) { - this._drawDot(point, pointGroupOptions); - } else if (curve) { - this._drawCurve(curve, pointGroupOptions); - } - lastPoints.push({ time: point.time, x: point.x, y: point.y, pressure: point.pressure, }); + + if (!lastPoint) { + if ( + pointGroupOptions.highlightColor && + pointGroupOptions.highlightSize + ) { + this._drawDot(point, pointGroupOptions, true); + } + this._drawDot(point, pointGroupOptions, false); + } else if (curve) { + if ( + pointGroupOptions.highlightColor && + pointGroupOptions.highlightSize + ) { + this._drawAll( + lastPoints, + pointGroupOptions, + this._drawCurve.bind(this), + true, + ); + this._drawAll( + lastPoints, + pointGroupOptions, + this._drawCurve.bind(this), + false, + ); + } else { + this._drawCurve(curve, pointGroupOptions, false); + } + } } this.dispatchEvent(new CustomEvent('afterUpdateStroke', { detail: event })); @@ -633,7 +667,11 @@ export default class SignaturePad extends SignatureEventTarget { this._isEmpty = false; } - private _drawCurve(curve: Bezier, options: PointGroupOptions): void { + private _drawCurve( + curve: Bezier, + options: PointGroupOptions, + isHighlight: boolean, + ): void { const ctx = this._ctx; const widthDelta = curve.endWidth - curve.startWidth; // '2' is just an arbitrary number here. If only length is used, then @@ -641,7 +679,7 @@ export default class SignaturePad extends SignatureEventTarget { const drawSteps = Math.ceil(curve.length()) * 2; ctx.beginPath(); - ctx.fillStyle = options.penColor; + ctx.fillStyle = isHighlight ? options.highlightColor : options.penColor; for (let i = 0; i < drawSteps; i += 1) { // Calculate the Bezier (x, y) coordinate for this step. @@ -666,14 +704,22 @@ export default class SignaturePad extends SignatureEventTarget { curve.startWidth + ttt * widthDelta, options.maxWidth, ); - this._drawCurveSegment(x, y, width); + this._drawCurveSegment( + x, + y, + width + (isHighlight ? options.highlightSize * 2 : 0), + ); } ctx.closePath(); ctx.fill(); } - private _drawDot(point: BasicPoint, options: PointGroupOptions): void { + private _drawDot( + point: BasicPoint, + options: PointGroupOptions, + isHighlight: boolean, + ): void { const ctx = this._ctx; const width = options.dotSize > 0 @@ -681,45 +727,69 @@ export default class SignaturePad extends SignatureEventTarget { : (options.minWidth + options.maxWidth) / 2; ctx.beginPath(); - this._drawCurveSegment(point.x, point.y, width); + this._drawCurveSegment( + point.x, + point.y, + width + (isHighlight ? options.highlightSize * 2 : 0), + ); ctx.closePath(); - ctx.fillStyle = options.penColor; + ctx.fillStyle = isHighlight ? options.highlightColor : options.penColor; ctx.fill(); } private _fromData( pointGroups: PointGroup[], - drawCurve: SignaturePad['_drawCurve'], - drawDot: SignaturePad['_drawDot'], + drawCurve: typeof this._drawCurve, + drawDot: typeof this._drawDot, ): void { for (const group of pointGroups) { const { points } = group; const pointGroupOptions = this._getPointGroupOptions(group); if (points.length > 1) { - for (let j = 0; j < points.length; j += 1) { - const basicPoint = points[j]; - const point = new Point( - basicPoint.x, - basicPoint.y, - basicPoint.pressure, - basicPoint.time, - ); - - if (j === 0) { - this._reset(pointGroupOptions); - } - - const curve = this._addPoint(point, pointGroupOptions); - - if (curve) { - drawCurve(curve, pointGroupOptions); - } + if ( + pointGroupOptions.highlightColor && + pointGroupOptions.highlightSize + ) { + this._drawAll(points, pointGroupOptions, drawCurve, true); } + this._drawAll(points, pointGroupOptions, drawCurve, false); } else { this._reset(pointGroupOptions); + if ( + pointGroupOptions.highlightColor && + pointGroupOptions.highlightSize + ) { + drawDot(points[0], pointGroupOptions, true); + } + drawDot(points[0], pointGroupOptions, false); + } + } + } + + private _drawAll( + points: BasicPoint[], + pointGroupOptions: PointGroupOptions, + drawCurve: typeof this._drawCurve, + isHighlight: boolean, + ): void { + for (let j = 0; j < points.length; j += 1) { + const basicPoint = points[j]; + const point = new Point( + basicPoint.x, + basicPoint.y, + basicPoint.pressure, + basicPoint.time, + ); + + if (j === 0) { + this._reset(pointGroupOptions); + } - drawDot(points[0], pointGroupOptions); + const curve = this._addPoint(point, pointGroupOptions); + + if (curve) { + drawCurve(curve, pointGroupOptions, isHighlight); } } } @@ -751,7 +821,7 @@ export default class SignaturePad extends SignatureEventTarget { this._fromData( pointGroups, - (curve, { penColor }) => { + (curve, { penColor, highlightColor, highlightSize }, isHighlight) => { const path = document.createElement('path'); // Need to check curve for NaN values, these pop up when drawing @@ -772,8 +842,14 @@ export default class SignaturePad extends SignatureEventTarget { `${curve.control2.x.toFixed(3)},${curve.control2.y.toFixed(3)} ` + `${curve.endPoint.x.toFixed(3)},${curve.endPoint.y.toFixed(3)}`; path.setAttribute('d', attr); - path.setAttribute('stroke-width', (curve.endWidth * 2.25).toFixed(3)); - path.setAttribute('stroke', penColor); + path.setAttribute( + 'stroke-width', + ( + (curve.endWidth + (isHighlight ? highlightSize * 2 : 0)) * + 2.25 + ).toFixed(3), + ); + path.setAttribute('stroke', isHighlight ? highlightColor : penColor); path.setAttribute('fill', 'none'); path.setAttribute('stroke-linecap', 'round'); @@ -782,13 +858,27 @@ export default class SignaturePad extends SignatureEventTarget { /* eslint-enable no-restricted-globals */ }, - (point, { penColor, dotSize, minWidth, maxWidth }) => { + ( + point, + { + penColor, + highlightColor, + highlightSize, + dotSize, + minWidth, + maxWidth, + }, + isHighlight, + ) => { const circle = document.createElement('circle'); const size = dotSize > 0 ? dotSize : (minWidth + maxWidth) / 2; - circle.setAttribute('r', size.toString()); + circle.setAttribute( + 'r', + (size + (isHighlight ? highlightSize * 2 : 0)).toString(), + ); circle.setAttribute('cx', point.x.toString()); circle.setAttribute('cy', point.y.toString()); - circle.setAttribute('fill', penColor); + circle.setAttribute('fill', isHighlight ? highlightColor : penColor); svg.appendChild(circle); }, diff --git a/tests/fixtures/face.ts b/tests/fixtures/face.ts index bdc19b7b..0b256b9a 100644 --- a/tests/fixtures/face.ts +++ b/tests/fixtures/face.ts @@ -3,6 +3,8 @@ import type { PointGroup } from '../../src/signature_pad'; export const face: PointGroup[] = [ { penColor: 'black', + highlightColor: '', + highlightSize: 0, dotSize: 0, minWidth: 0.5, maxWidth: 2.5, @@ -19,6 +21,8 @@ export const face: PointGroup[] = [ }, { penColor: 'black', + highlightColor: '', + highlightSize: 0, dotSize: 0, minWidth: 0.5, maxWidth: 2.5, @@ -35,6 +39,8 @@ export const face: PointGroup[] = [ }, { penColor: 'black', + highlightColor: '', + highlightSize: 0, dotSize: 0, minWidth: 0.5, maxWidth: 2.5, diff --git a/tests/fixtures/square.ts b/tests/fixtures/square.ts index f91ce800..eb89a4db 100644 --- a/tests/fixtures/square.ts +++ b/tests/fixtures/square.ts @@ -3,6 +3,8 @@ import type { PointGroup } from '../../src/signature_pad'; export const square: PointGroup[] = [ { penColor: 'black', + highlightColor: '', + highlightSize: 0, dotSize: 0, minWidth: 0.5, maxWidth: 2.5,