From cef65235537d902a6023b5f6408a0342c89f0bd0 Mon Sep 17 00:00:00 2001 From: James McIntosh Date: Wed, 15 Aug 2018 12:44:17 +1200 Subject: [PATCH 1/4] Upgrade to version 2.3.2 of Szimek's Signature Pad --- injectedJavaScript/signaturePad.js | 132 ++++++++++++++++++----------- 1 file changed, 83 insertions(+), 49 deletions(-) diff --git a/injectedJavaScript/signaturePad.js b/injectedJavaScript/signaturePad.js index 6f6bc40..d03c881 100644 --- a/injectedJavaScript/signaturePad.js +++ b/injectedJavaScript/signaturePad.js @@ -1,5 +1,5 @@ /*! - * Signature Pad v2.1.0 + * Signature Pad v2.3.2 * https://github.com/szimek/signature_pad * * Copyright 2017 Szymon Nowak @@ -16,8 +16,7 @@ * */ -var content = ` - +export default ` (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : typeof define === 'function' && define.amd ? define(factory) : @@ -38,6 +37,10 @@ Point.prototype.distanceTo = function (start) { return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2)); }; +Point.prototype.equals = function (other) { + return this.x === other.x && this.y === other.y && this.time === other.time; +}; + function Bezier(startPoint, control1, control2, endPoint) { this.startPoint = startPoint; this.control1 = control1; @@ -45,7 +48,7 @@ function Bezier(startPoint, control1, control2, endPoint) { this.endPoint = endPoint; } -/* Returns approximated length. */ +/* // Returns approximated length. */ Bezier.prototype.length = function () { var steps = 10; var length = 0; @@ -75,7 +78,7 @@ Bezier.prototype._point = function (t, start, c1, c2, end) { /* eslint-disable */ -/* http://stackoverflow.com/a/27078401/815507 */ +/* // http://stackoverflow.com/a/27078401/815507 */ function throttle(func, wait, options) { var context, args, result; var timeout = null; @@ -115,7 +118,8 @@ function SignaturePad(canvas, options) { this.velocityFilterWeight = opts.velocityFilterWeight || 0.7; this.minWidth = opts.minWidth || 0.5; this.maxWidth = opts.maxWidth || 2.5; - this.throttle = opts.throttle || 16; /* in milliseconds */ + this.throttle = 'throttle' in opts ? opts.throttle : 16; /* // in miliseconds */ + this.minDistance = 'minDistance' in opts ? opts.minDistance : 5; if (this.throttle) { this._strokeMoveUpdate = throttle(SignaturePad.prototype._strokeUpdate, this.throttle); @@ -135,8 +139,8 @@ function SignaturePad(canvas, options) { this._ctx = canvas.getContext('2d'); this.clear(); - /* We need add these inline so they are available to unbind while still having - access to 'self' we could use _.bind but it's not worth adding a dependency. */ + /* // We need add these inline so they are available to unbind while still having */ + /* // access to 'self' we could use _.bind but it's not worth adding a dependency. */ this._handleMouseDown = function (event) { if (event.which === 1) { self._mouseButtonDown = true; @@ -165,7 +169,7 @@ function SignaturePad(canvas, options) { }; this._handleTouchMove = function (event) { - /* Prevent scrolling. */ + /* // Prevent scrolling. */ event.preventDefault(); var touch = event.targetTouches[0]; @@ -180,11 +184,11 @@ function SignaturePad(canvas, options) { } }; - /* Enable mouse and touch event handlers */ + /* // Enable mouse and touch event handlers */ this.on(); } -/* Public methods */ +/* // Public methods */ SignaturePad.prototype.clear = function () { var ctx = this._ctx; var canvas = this._canvas; @@ -201,10 +205,12 @@ SignaturePad.prototype.clear = function () { SignaturePad.prototype.fromDataURL = function (dataUrl) { var _this = this; + var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; + var image = new Image(); - var ratio = window.devicePixelRatio || 1; - var width = this._canvas.width / ratio; - var height = this._canvas.height / ratio; + var ratio = options.ratio || window.devicePixelRatio || 1; + var width = options.width || this._canvas.width / ratio; + var height = options.height || this._canvas.height / ratio; this._reset(); image.src = dataUrl; @@ -248,7 +254,7 @@ SignaturePad.prototype.isEmpty = function () { return this._isEmpty; }; -/* Private methods */ +/* // Private methods */ SignaturePad.prototype._strokeBegin = function (event) { this._data.push([]); this._reset(); @@ -264,30 +270,53 @@ SignaturePad.prototype._strokeUpdate = function (event) { var y = event.clientY; var point = this._createPoint(x, y); + var lastPointGroup = this._data[this._data.length - 1]; + var lastPoint = lastPointGroup && lastPointGroup[lastPointGroup.length - 1]; + var isLastPointTooClose = lastPoint && point.distanceTo(lastPoint) < this.minDistance; + + /* // Skip this point if it's too close to the previous one */ + if (!(lastPoint && isLastPointTooClose)) { + var _addPoint = this._addPoint(point), + curve = _addPoint.curve, + widths = _addPoint.widths; + + if (curve && widths) { + this._drawCurve(curve, widths.start, widths.end); + } - var _addPoint = this._addPoint(point), - curve = _addPoint.curve, - widths = _addPoint.widths; - - if (curve && widths) { - this._drawCurve(curve, widths.start, widths.end); + this._data[this._data.length - 1].push({ + x: point.x, + y: point.y, + time: point.time, + color: this.penColor + }); } - - this._data[this._data.length - 1].push({ - x: point.x, - y: point.y, - time: point.time - }); }; SignaturePad.prototype._strokeEnd = function (event) { var canDrawCurve = this.points.length > 2; - var point = this.points[0]; + var point = this.points[0]; /* // Point instance */ if (!canDrawCurve && point) { this._drawDot(point); } + if (point) { + var lastPointGroup = this._data[this._data.length - 1]; + var lastPoint = lastPointGroup[lastPointGroup.length - 1]; /* // plain object */ + + /* // When drawing a dot, there's only one point in a group, so without this check */ + /* // such group would end up with exactly the same 2 points. */ + if (!point.equals(lastPoint)) { + lastPointGroup.push({ + x: point.x, + y: point.y, + time: point.time, + color: this.penColor + }); + } + } + if (typeof this.onEnd === 'function') { this.onEnd(event); } @@ -302,7 +331,7 @@ SignaturePad.prototype._handleMouseEvents = function () { }; SignaturePad.prototype._handleTouchEvents = function () { - /* Pass touch events to canvas element on mobile IE11 and Edge. */ + /* // Pass touch events to canvas element on mobile IE11 and Edge. */ this._canvas.style.msTouchAction = 'none'; this._canvas.style.touchAction = 'none'; @@ -331,8 +360,8 @@ SignaturePad.prototype._addPoint = function (point) { points.push(point); if (points.length > 2) { - /* To reduce the initial lag make it work with 3 points - by copying the first point to the beginning. */ + /* // To reduce the initial lag make it work with 3 points */ + /* // by copying the first point to the beginning. */ if (points.length === 3) points.unshift(points[0]); tmp = this._calculateCurveControlPoints(points[0], points[1], points[2]); @@ -342,8 +371,8 @@ SignaturePad.prototype._addPoint = function (point) { var curve = new Bezier(points[1], c2, c3, points[2]); var widths = this._calculateCurveWidths(curve); - /* Remove the first element from the list, - so that we always have no more than 4 points in points array. */ + /* // Remove the first element from the list, */ + /* // so that we always have no more than 4 points in points array. */ points.shift(); return { curve: curve, widths: widths }; @@ -417,7 +446,7 @@ SignaturePad.prototype._drawCurve = function (curve, startWidth, endWidth) { ctx.beginPath(); for (var i = 0; i < drawSteps; i += 1) { - /* Calculate the Bezier (x, y) coordinate for this step. */ + /* // Calculate the Bezier (x, y) coordinate for this step. */ var t = i / drawSteps; var tt = t * t; var ttt = tt * t; @@ -461,22 +490,28 @@ SignaturePad.prototype._fromData = function (pointGroups, drawCurve, drawDot) { for (var j = 0; j < group.length; j += 1) { var rawPoint = group[j]; var point = new Point(rawPoint.x, rawPoint.y, rawPoint.time); + var color = rawPoint.color; if (j === 0) { - /* First point in a group. Nothing to draw yet. */ + /* // First point in a group. Nothing to draw yet. */ + + /* // All points in the group have the same color, so it's enough to set */ + /* // penColor just at the beginning. */ + this.penColor = color; this._reset(); + this._addPoint(point); } else if (j !== group.length - 1) { - /* Middle point in a group. */ + /* // Middle point in a group. */ var _addPoint2 = this._addPoint(point), curve = _addPoint2.curve, widths = _addPoint2.widths; if (curve && widths) { - drawCurve(curve, widths); + drawCurve(curve, widths, color); } } else { - /* Last point in a group. Do nothing. */ + /* // Last point in a group. Do nothing. */ } } } else { @@ -502,18 +537,18 @@ SignaturePad.prototype._toSVG = function () { svg.setAttributeNS(null, 'width', canvas.width); svg.setAttributeNS(null, 'height', canvas.height); - this._fromData(pointGroups, function (curve, widths) { + this._fromData(pointGroups, function (curve, widths, color) { var path = document.createElement('path'); - /* Need to check curve for NaN values, these pop up when drawing - lines on the canvas that are not continuous. E.g. Sharp corners - or stopping mid-stroke and than continuing without lifting mouse. */ + /* // Need to check curve for NaN values, these pop up when drawing */ + /* // lines on the canvas that are not continuous. E.g. Sharp corners */ + /* // or stopping mid-stroke and than continuing without lifting mouse. */ if (!isNaN(curve.control1.x) && !isNaN(curve.control1.y) && !isNaN(curve.control2.x) && !isNaN(curve.control2.y)) { var attr = 'M ' + curve.startPoint.x.toFixed(3) + ',' + curve.startPoint.y.toFixed(3) + ' ' + ('C ' + curve.control1.x.toFixed(3) + ',' + curve.control1.y.toFixed(3) + ' ') + (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', (widths.end * 2.25).toFixed(3)); - path.setAttribute('stroke', _this2.penColor); + path.setAttribute('stroke', color); path.setAttribute('fill', 'none'); path.setAttribute('stroke-linecap', 'round'); @@ -525,7 +560,7 @@ SignaturePad.prototype._toSVG = function () { circle.setAttribute('r', dotSize); circle.setAttribute('cx', rawPoint.x); circle.setAttribute('cy', rawPoint.y); - circle.setAttribute('fill', _this2.penColor); + circle.setAttribute('fill', rawPoint.color); svg.appendChild(circle); }); @@ -534,7 +569,7 @@ SignaturePad.prototype._toSVG = function () { var header = ''; var body = svg.innerHTML; - /* IE hack for missing innerHTML property on SVGElement */ + /* // IE hack for missing innerHTML property on SVGElement */ if (body === undefined) { var dummy = document.createElement('dummy'); var nodes = svg.childNodes; @@ -563,6 +598,8 @@ SignaturePad.prototype.fromData = function (pointGroups) { }, function (rawPoint) { return _this3._drawDot(rawPoint); }); + + this._data = pointGroups; }; SignaturePad.prototype.toData = function () { @@ -572,7 +609,4 @@ SignaturePad.prototype.toData = function () { return SignaturePad; }))); - -`; - -export default content; +`; \ No newline at end of file From b13627b74a8133730bcda100ca6488fceb4d4903 Mon Sep 17 00:00:00 2001 From: James McIntosh Date: Thu, 16 Aug 2018 01:54:55 +1200 Subject: [PATCH 2/4] Added ES6 export --- index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.js b/index.js index a1d66ab..be92fe2 100644 --- a/index.js +++ b/index.js @@ -197,4 +197,4 @@ class SignaturePad extends Component { }; } -module.exports = SignaturePad; +export { SignaturePad as default } \ No newline at end of file From 7cf10fd6f4fe10aeb7fc929639b740979559e916 Mon Sep 17 00:00:00 2001 From: James McIntosh Date: Sat, 11 Aug 2018 16:29:48 +1200 Subject: [PATCH 3/4] Add timeout to wait for document dimensions --- index.js | 8 +- injectedHtml/index.js | 2 +- injectedJavaScript/application.js | 154 +++++++++++++++++++++--------- 3 files changed, 115 insertions(+), 49 deletions(-) diff --git a/index.js b/index.js index be92fe2..0587d20 100644 --- a/index.js +++ b/index.js @@ -28,6 +28,7 @@ class SignaturePad extends Component { useFont: PropTypes.bool, name: PropTypes.string, fontStyle: PropTypes.string, + initTimeout: PropTypes.number }; static defaultProps = { @@ -56,7 +57,8 @@ class SignaturePad extends Component { props.useFont, escapedName, props.height, - props.width + props.width, + props.initTimeout ); var html = htmlContent(injectedJavaScript, props.fontStyle); this.source = {html}; @@ -84,7 +86,8 @@ class SignaturePad extends Component { this.props.useFont, escapedName, this.props.height, - this.props.width + this.props.width, + this.props.initTimeout ); var html = htmlContent(injectedJavaScript, this.props.fontStyle); this.source = {html}; @@ -183,6 +186,7 @@ class SignaturePad extends Component { render = () => { return ( { this._webview = ref }} automaticallyAdjustContentInsets={false} onNavigationStateChange={this._onNavigationChange} onMessage={this._onMessage} diff --git a/injectedHtml/index.js b/injectedHtml/index.js index b3c5aec..82b46d5 100644 --- a/injectedHtml/index.js +++ b/injectedHtml/index.js @@ -25,7 +25,7 @@ var content = (script, fontStyle) => diff --git a/injectedJavaScript/application.js b/injectedJavaScript/application.js index 8665c06..31fefd6 100644 --- a/injectedJavaScript/application.js +++ b/injectedJavaScript/application.js @@ -1,59 +1,69 @@ -var content = (penColor, backgroundColor, dataURL, penMinWidth, penMaxWidth, useFont, name, height, width) => ` - - var showSignaturePad = function (signaturePadCanvas, bodyWidth, bodyHeight) { - var width = bodyWidth; - var height = bodyHeight; - - var sizeSignaturePad = function () { - var devicePixelRatio = window.devicePixelRatio || 1; - var canvasWidth = width * devicePixelRatio; - var canvasHeight = height * devicePixelRatio; - signaturePadCanvas.width = canvasWidth; - signaturePadCanvas.height = canvasHeight; - signaturePadCanvas.getContext("2d").scale(devicePixelRatio, devicePixelRatio); - }; +var content = (penColor, backgroundColor, dataURL, penMinWidth, penMaxWidth, useFont, name, height, width, initTimeout) => ` - var enableSignaturePadFunctionality = function () { - var signaturePad = new SignaturePad(signaturePadCanvas, { - penColor: "${penColor || "black"}", - backgroundColor: "${backgroundColor || "white"}", - onEnd: function() { finishedStroke(signaturePad.toDataURL()); } - }); - signaturePad.minWidth = ${penMinWidth || 1}; - signaturePad.maxWidth = ${penMaxWidth || 4}; - if ("${dataURL}") { - signaturePad.fromDataURL("${dataURL}"); - } - }; +var showSignaturePad = function (signaturePadCanvas, bodyWidth, bodyHeight) { + var width = bodyWidth; + var height = bodyHeight; - reportSize(width, height); - sizeSignaturePad(); - enableSignaturePadFunctionality(); + var sizeSignaturePad = function () { + var devicePixelRatio = window.devicePixelRatio || 1; + var canvasWidth = width * devicePixelRatio; + var canvasHeight = height * devicePixelRatio; + signaturePadCanvas.width = canvasWidth; + signaturePadCanvas.height = canvasHeight; + signaturePadCanvas.getContext("2d").scale(devicePixelRatio, devicePixelRatio); }; - var bodyWidth = document.body.clientWidth * 2; - var bodyHeight = document.body.clientHeight * 2; - if(!bodyWidth) { - bodyWidth = window.innerWidth ? window.innerWidth : ${width}; + var enableSignaturePadFunctionality = function () { + var signaturePad = new SignaturePad(signaturePadCanvas, { + penColor: "${penColor || "black"}", + backgroundColor: "${backgroundColor || "white"}", + onEnd: function() { finishedStroke(signaturePad.toDataURL()); } + }); + signaturePad.minWidth = ${penMinWidth || 1}; + signaturePad.maxWidth = ${penMaxWidth || 4}; + if ("${dataURL}") { + signaturePad.fromDataURL("${dataURL}"); + } + }; + + reportSize(width, height); + sizeSignaturePad(); + enableSignaturePadFunctionality(); +}; + +var canvasElement = document.querySelector("canvas"); + +var reportSize = function(width, height) { + if (postMessage.length === 1) { + window.postMessage(JSON.stringify({ width: width, height: height })); + } else { + setTimeout(function() { reportSize(width, height) }, 100); } - if(!bodyHeight) { - bodyHeight = window.innerHeight ? window.innerHeight : ${height}; +} + +var finishedStroke = function(base64DataUrl) { + window.postMessage(JSON.stringify({ base64DataUrl: base64DataUrl })); +}; + +var getBodyWidth = function() { + var bodyWidth = document && document.body && document.body.clientWidth ? document.body.clientWidth * 2 : 0; + if(!bodyWidth) { + bodyWidth = window && window.innerWidth ? window.innerWidth : ${width || 0}; } - var canvasElement = document.querySelector("canvas"); + return bodyWidth; +}; - var reportSize = function(width, height) { - if (postMessage.length === 1) { - window.postMessage(JSON.stringify({ width: width, height: height })); - } else { - setTimeout(function() { reportSize(width, height) }, 100); - } +var getBodyHeight = function() { + var bodyHeight = document && document.body && document.body.clientHeight ? document.body.clientHeight * 2 : 0; + if(!bodyHeight) { + bodyHeight = window && window.innerHeight ? window.innerHeight : ${height || 0}; } - var finishedStroke = function(base64DataUrl) { - window.postMessage(JSON.stringify({ base64DataUrl: base64DataUrl })); - }; + return bodyHeight; +}; +var initSignaturePad = function(bodyWidth, bodyHeight) { if (${useFont}) { var context = canvasElement.getContext("2d"); var devicePixelRatio = 1; /* window.devicePixelRatio || 1; */ @@ -88,8 +98,60 @@ var content = (penColor, backgroundColor, dataURL, penMinWidth, penMaxWidth, use finishedStroke(canvasElement.toDataURL()); }, 75); } else { - showSignaturePad(canvasElement, bodyWidth / 2, bodyHeight / 2); + showSignaturePad(canvasElement, bodyWidth / 2, bodyHeight / 2); } +}; + +var whileDocumentSizeNotSet = function(timeout, maximumWaitTime) { + try { + if ( typeof whileDocumentSizeNotSet.counter == 'undefined' ) { + whileDocumentSizeNotSet.counter = 0; + whileDocumentSizeNotSet.bodyHeight = 0; + whileDocumentSizeNotSet.bodyWidth = 0; + } else { + whileDocumentSizeNotSet.counter++; + } + + + const maxAttemts = Math.floor(maximumWaitTime / timeout); + const attempt = whileDocumentSizeNotSet.counter; + + const previousBodyHeight = whileDocumentSizeNotSet.bodyHeight; + const previousBodyWidth = whileDocumentSizeNotSet.bodyWidth; + + const bodyHeight = getBodyHeight(); + const bodyWidth = getBodyWidth(); + + whileDocumentSizeNotSet.bodyHeight = bodyHeight; + whileDocumentSizeNotSet.bodyWidth = bodyWidth; + + if (bodyHeight === 0 || bodyWidth === 0 || previousBodyWidth !== bodyWidth || previousBodyHeight !== bodyHeight) { + if (attempt <= maxAttemts) { + setTimeout(whileDocumentSizeNotSet, timeout, timeout, maximumWaitTime); + + return false; + } else { + window.alert('Timed out trying to load SignaturePad, tried ' + attempt + ' times in ' + maximumWaitTime + 'ms.'); + // of maximumWaitTime:' + maximumWaitTime + ', timeout:' + timeout + ', maxAttemts' + maxAttemts + ' times'); + + initSignaturePad(700, 700); + } + } else { + initSignaturePad(bodyWidth, bodyHeight); + + // window.alert('Had to wait ' + attempt + ' times, width: ' + bodyWidth + ', height: ' + bodyHeight); + } + } catch (e) { + if (window) { + window.alert(e.message); + } + } + + return true; +}; + +whileDocumentSizeNotSet(250, ${initTimeout || 3000}); + `; export default content; From b4f08f3c3dcce723eb1910ededabc8372bb0cb10 Mon Sep 17 00:00:00 2001 From: James McIntosh Date: Thu, 16 Aug 2018 02:07:28 +1200 Subject: [PATCH 4/4] Add method to clear signature --- index.js | 4 ++++ injectedJavaScript/application.js | 19 +++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/index.js b/index.js index 0587d20..78d06ea 100644 --- a/index.js +++ b/index.js @@ -183,6 +183,10 @@ class SignaturePad extends Component { } } + clear = () => { + this._webview.postMessage(JSON.stringify({ action: 'clear' })); + } + render = () => { return (