Socket
Socket
Sign inDemoInstall

signature_pad

Package Overview
Dependencies
Maintainers
1
Versions
57
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

signature_pad - npm Package Compare versions

Comparing version 1.5.3 to 1.6.0-beta.1

.babelrc

2

bower.json
{
"name": "signature_pad",
"main": "signature_pad.js",
"main": "dist/signature_pad.js",
"homepage": "https://github.com/szimek/signature_pad",

@@ -5,0 +5,0 @@ "authors": [

## Changelog
### 1.6.0-beta.1
* Added support for returning signature as SVG using `#fromDataURL('image/svg+xml')`. [jackspirou](https://github.com/jackspirou) [mymattcarroll](https://github.com/mymattcarroll) [szimek](https://github.com/szimek)
* Added `#toData` method that returns data points.
* Added `#fromData` method that draws signature from data points.
* Moved `signature_pad.js` and `signature_pad.min.js` files to `dist` folder.
### 1.5.3

@@ -3,0 +9,0 @@ * Fix `touchend` event on touch devices. (#150) [mtomic](https://github.com/mtomic)

var wrapper = document.getElementById("signature-pad"),
clearButton = wrapper.querySelector("[data-action=clear]"),
saveButton = wrapper.querySelector("[data-action=save]"),
savePNGButton = wrapper.querySelector("[data-action=save-png]"),
saveSVGButton = wrapper.querySelector("[data-action=save-svg]"),
canvas = wrapper.querySelector("canvas"),

@@ -29,3 +30,3 @@ signaturePad;

saveButton.addEventListener("click", function (event) {
savePNGButton.addEventListener("click", function (event) {
if (signaturePad.isEmpty()) {

@@ -37,1 +38,9 @@ alert("Please provide signature first.");

});
saveSVGButton.addEventListener("click", function (event) {
if (signaturePad.isEmpty()) {
alert("Please provide signature first.");
} else {
window.open(signaturePad.toDataURL('image/svg+xml'));
}
});

@@ -1,22 +0,6 @@

(function (root, factory) {
if (typeof define === 'function' && define.amd) {
// AMD. Register as an anonymous module unless amdModuleId is set
define([], function () {
return (root['SignaturePad'] = factory());
});
} else if (typeof exports === 'object') {
// Node. Does not work with strict CommonJS, but
// only CommonJS-like environments that support module.exports,
// like Node.
module.exports = factory();
} else {
root['SignaturePad'] = factory();
}
}(this, function () {
/*!
* Signature Pad v1.5.3
* Signature Pad v1.6.0-beta.1
* https://github.com/szimek/signature_pad
*
* Copyright 2016 Szymon Nowak
* Copyright 2017 Szymon Nowak
* Released under the MIT license

@@ -34,357 +18,507 @@ *

*/
var SignaturePad = (function (document) {
"use strict";
(function (global, factory) {
if (typeof define === "function" && define.amd) {
define(['module'], factory);
} else if (typeof exports !== "undefined") {
factory(module);
} else {
var mod = {
exports: {}
};
factory(mod);
global.SignaturePad = mod.exports;
}
})(this, function (module) {
'use strict';
var SignaturePad = function (canvas, options) {
var self = this,
opts = options || {};
/* eslint-env browser */
/* eslint no-underscore-dangle: "off", func-names: "off", import/prefer-default-export: "off" */
function Point(x, y, time) {
this.x = x;
this.y = y;
this.time = time || new Date().getTime();
}
this.velocityFilterWeight = opts.velocityFilterWeight || 0.7;
this.minWidth = opts.minWidth || 0.5;
this.maxWidth = opts.maxWidth || 2.5;
this.dotSize = opts.dotSize || function () {
return (this.minWidth + this.maxWidth) / 2;
};
this.penColor = opts.penColor || "black";
this.backgroundColor = opts.backgroundColor || "rgba(0,0,0,0)";
this.onEnd = opts.onEnd;
this.onBegin = opts.onBegin;
Point.prototype.velocityFrom = function (start) {
return this.time !== start.time ? this.distanceTo(start) / (this.time - start.time) : 1;
};
this._canvas = canvas;
this._ctx = canvas.getContext("2d");
this.clear();
Point.prototype.distanceTo = function (start) {
return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
};
// 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;
self._strokeBegin(event);
}
};
function Bezier(startPoint, control1, control2, endPoint) {
this.startPoint = startPoint;
this.control1 = control1;
this.control2 = control2;
this.endPoint = endPoint;
}
this._handleMouseMove = function (event) {
if (self._mouseButtonDown) {
self._strokeUpdate(event);
}
};
// Returns approximated length.
Bezier.prototype.length = function () {
var steps = 10;
var length = 0;
var px = void 0;
var py = void 0;
this._handleMouseUp = function (event) {
if (event.which === 1 && self._mouseButtonDown) {
self._mouseButtonDown = false;
self._strokeEnd(event);
}
};
for (var i = 0; i <= steps; i += 1) {
var t = i / steps;
var cx = this._point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
var cy = this._point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
if (i > 0) {
var xdiff = cx - px;
var ydiff = cy - py;
length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
}
px = cx;
py = cy;
}
this._handleTouchStart = function (event) {
if (event.targetTouches.length == 1) {
var touch = event.changedTouches[0];
self._strokeBegin(touch);
}
};
return length;
};
this._handleTouchMove = function (event) {
// Prevent scrolling.
event.preventDefault();
/* eslint-disable no-multi-spaces, space-in-parens */
Bezier.prototype._point = function (t, start, c1, c2, end) {
return start * (1.0 - t) * (1.0 - t) * (1.0 - t) + 3.0 * c1 * (1.0 - t) * (1.0 - t) * t + 3.0 * c2 * (1.0 - t) * t * t + end * t * t * t;
};
/* eslint-enable no-multi-spaces, space-in-parens */
var touch = event.targetTouches[0];
self._strokeUpdate(touch);
};
function SignaturePad(canvas, options) {
var self = this;
var opts = options || {};
this._handleTouchEnd = function (event) {
var wasCanvasTouched = event.target === self._canvas;
if (wasCanvasTouched) {
event.preventDefault();
self._strokeEnd(event);
}
};
this._handleMouseEvents();
this._handleTouchEvents();
this.velocityFilterWeight = opts.velocityFilterWeight || 0.7;
this.minWidth = opts.minWidth || 0.5;
this.maxWidth = opts.maxWidth || 2.5;
this.dotSize = opts.dotSize || function () {
return (this.minWidth + this.maxWidth) / 2;
};
this.penColor = opts.penColor || 'black';
this.backgroundColor = opts.backgroundColor || 'rgba(0,0,0,0)';
this.onBegin = opts.onBegin;
this.onEnd = opts.onEnd;
SignaturePad.prototype.clear = function () {
var ctx = this._ctx,
canvas = this._canvas;
this._canvas = canvas;
this._ctx = canvas.getContext('2d');
this.clear();
ctx.fillStyle = this.backgroundColor;
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillRect(0, 0, canvas.width, canvas.height);
this._reset();
// 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;
self._strokeBegin(event);
}
};
SignaturePad.prototype.toDataURL = function (imageType, quality) {
var canvas = this._canvas;
return canvas.toDataURL.apply(canvas, arguments);
this._handleMouseMove = function (event) {
if (self._mouseButtonDown) {
self._strokeUpdate(event);
}
};
SignaturePad.prototype.fromDataURL = function (dataUrl) {
var self = this,
image = new Image(),
ratio = window.devicePixelRatio || 1,
width = this._canvas.width / ratio,
height = this._canvas.height / ratio;
this._handleMouseUp = function (event) {
if (event.which === 1 && self._mouseButtonDown) {
self._mouseButtonDown = false;
self._strokeEnd(event);
}
};
this._reset();
image.src = dataUrl;
image.onload = function () {
self._ctx.drawImage(image, 0, 0, width, height);
};
this._isEmpty = false;
this._handleTouchStart = function (event) {
if (event.targetTouches.length === 1) {
var touch = event.changedTouches[0];
self._strokeBegin(touch);
}
};
SignaturePad.prototype._strokeUpdate = function (event) {
var point = this._createPoint(event);
this._addPoint(point);
this._handleTouchMove = function (event) {
// Prevent scrolling.
event.preventDefault();
var touch = event.targetTouches[0];
self._strokeUpdate(touch);
};
SignaturePad.prototype._strokeBegin = function (event) {
this._reset();
this._strokeUpdate(event);
if (typeof this.onBegin === 'function') {
this.onBegin(event);
}
this._handleTouchEnd = function (event) {
var wasCanvasTouched = event.target === self._canvas;
if (wasCanvasTouched) {
event.preventDefault();
self._strokeEnd(event);
}
};
SignaturePad.prototype._strokeDraw = function (point) {
var ctx = this._ctx,
dotSize = typeof(this.dotSize) === 'function' ? this.dotSize() : this.dotSize;
// Enable mouse and touch event handlers
this.on();
}
ctx.beginPath();
this._drawPoint(point.x, point.y, dotSize);
ctx.closePath();
ctx.fill();
// Public methods
SignaturePad.prototype.clear = function () {
var ctx = this._ctx;
var canvas = this._canvas;
ctx.fillStyle = this.backgroundColor;
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillRect(0, 0, canvas.width, canvas.height);
this._data = [];
this._reset();
this._isEmpty = true;
};
SignaturePad.prototype.fromDataURL = function (dataUrl) {
var _this = this;
var image = new Image();
var ratio = window.devicePixelRatio || 1;
var width = this._canvas.width / ratio;
var height = this._canvas.height / ratio;
this._reset();
image.src = dataUrl;
image.onload = function () {
_this._ctx.drawImage(image, 0, 0, width, height);
};
this._isEmpty = false;
};
SignaturePad.prototype._strokeEnd = function (event) {
var canDrawCurve = this.points.length > 2,
point = this.points[0];
SignaturePad.prototype.toDataURL = function (type) {
var _canvas;
if (!canDrawCurve && point) {
this._strokeDraw(point);
switch (type) {
case 'image/svg+xml':
return this._toSVG();
default:
for (var _len = arguments.length, options = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
options[_key - 1] = arguments[_key];
}
if (typeof this.onEnd === 'function') {
this.onEnd(event);
}
};
SignaturePad.prototype._handleMouseEvents = function () {
this._mouseButtonDown = false;
return (_canvas = this._canvas).toDataURL.apply(_canvas, [type].concat(options));
}
};
this._canvas.addEventListener("mousedown", this._handleMouseDown);
this._canvas.addEventListener("mousemove", this._handleMouseMove);
document.addEventListener("mouseup", this._handleMouseUp);
};
SignaturePad.prototype.on = function () {
this._handleMouseEvents();
this._handleTouchEvents();
};
SignaturePad.prototype._handleTouchEvents = function () {
// Pass touch events to canvas element on mobile IE11 and Edge.
this._canvas.style.msTouchAction = 'none';
this._canvas.style.touchAction = 'none';
SignaturePad.prototype.off = function () {
this._canvas.removeEventListener('mousedown', this._handleMouseDown);
this._canvas.removeEventListener('mousemove', this._handleMouseMove);
document.removeEventListener('mouseup', this._handleMouseUp);
this._canvas.addEventListener("touchstart", this._handleTouchStart);
this._canvas.addEventListener("touchmove", this._handleTouchMove);
this._canvas.addEventListener("touchend", this._handleTouchEnd);
};
this._canvas.removeEventListener('touchstart', this._handleTouchStart);
this._canvas.removeEventListener('touchmove', this._handleTouchMove);
this._canvas.removeEventListener('touchend', this._handleTouchEnd);
};
SignaturePad.prototype.on = function () {
this._handleMouseEvents();
this._handleTouchEvents();
};
SignaturePad.prototype.isEmpty = function () {
return this._isEmpty;
};
SignaturePad.prototype.off = function () {
this._canvas.removeEventListener("mousedown", this._handleMouseDown);
this._canvas.removeEventListener("mousemove", this._handleMouseMove);
document.removeEventListener("mouseup", this._handleMouseUp);
// Private methods
SignaturePad.prototype._strokeBegin = function (event) {
this._data.push([]);
this._reset();
this._strokeUpdate(event);
this._canvas.removeEventListener("touchstart", this._handleTouchStart);
this._canvas.removeEventListener("touchmove", this._handleTouchMove);
this._canvas.removeEventListener("touchend", this._handleTouchEnd);
};
if (typeof this.onBegin === 'function') {
this.onBegin(event);
}
};
SignaturePad.prototype.isEmpty = function () {
return this._isEmpty;
};
SignaturePad.prototype._strokeUpdate = function (event) {
var x = event.clientX;
var y = event.clientY;
SignaturePad.prototype._reset = function () {
this.points = [];
this._lastVelocity = 0;
this._lastWidth = (this.minWidth + this.maxWidth) / 2;
this._isEmpty = true;
this._ctx.fillStyle = this.penColor;
};
var point = this._createPoint(x, y);
SignaturePad.prototype._createPoint = function (event) {
var rect = this._canvas.getBoundingClientRect();
return new Point(
event.clientX - rect.left,
event.clientY - rect.top
);
};
var _addPoint = this._addPoint(point),
curve = _addPoint.curve,
widths = _addPoint.widths;
SignaturePad.prototype._addPoint = function (point) {
var points = this.points,
c2, c3,
curve, tmp;
if (curve && widths) {
this._drawCurve(curve, widths.start, widths.end);
}
points.push(point);
this._data[this._data.length - 1].push({
x: point.x,
y: point.y,
time: point.time
});
};
if (points.length > 2) {
// 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]);
SignaturePad.prototype._strokeEnd = function (event) {
var canDrawCurve = this.points.length > 2;
var point = this.points[0];
tmp = this._calculateCurveControlPoints(points[0], points[1], points[2]);
c2 = tmp.c2;
tmp = this._calculateCurveControlPoints(points[1], points[2], points[3]);
c3 = tmp.c1;
curve = new Bezier(points[1], c2, c3, points[2]);
this._addCurve(curve);
if (!canDrawCurve && point) {
this._drawDot(point);
}
// Remove the first element from the list,
// so that we always have no more than 4 points in points array.
points.shift();
}
};
if (typeof this.onEnd === 'function') {
this.onEnd(event);
}
};
SignaturePad.prototype._calculateCurveControlPoints = function (s1, s2, s3) {
var dx1 = s1.x - s2.x, dy1 = s1.y - s2.y,
dx2 = s2.x - s3.x, dy2 = s2.y - s3.y,
SignaturePad.prototype._handleMouseEvents = function () {
this._mouseButtonDown = false;
m1 = {x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0},
m2 = {x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0},
this._canvas.addEventListener('mousedown', this._handleMouseDown);
this._canvas.addEventListener('mousemove', this._handleMouseMove);
document.addEventListener('mouseup', this._handleMouseUp);
};
l1 = Math.sqrt(dx1*dx1 + dy1*dy1),
l2 = Math.sqrt(dx2*dx2 + dy2*dy2),
SignaturePad.prototype._handleTouchEvents = function () {
// Pass touch events to canvas element on mobile IE11 and Edge.
this._canvas.style.msTouchAction = 'none';
this._canvas.style.touchAction = 'none';
dxm = (m1.x - m2.x),
dym = (m1.y - m2.y),
this._canvas.addEventListener('touchstart', this._handleTouchStart);
this._canvas.addEventListener('touchmove', this._handleTouchMove);
this._canvas.addEventListener('touchend', this._handleTouchEnd);
};
k = l2 / (l1 + l2),
cm = {x: m2.x + dxm*k, y: m2.y + dym*k},
SignaturePad.prototype._reset = function () {
this.points = [];
this._lastVelocity = 0;
this._lastWidth = (this.minWidth + this.maxWidth) / 2;
this._ctx.fillStyle = this.penColor;
};
tx = s2.x - cm.x,
ty = s2.y - cm.y;
SignaturePad.prototype._createPoint = function (x, y, time) {
var rect = this._canvas.getBoundingClientRect();
return {
c1: new Point(m1.x + tx, m1.y + ty),
c2: new Point(m2.x + tx, m2.y + ty)
};
};
return new Point(x - rect.left, y - rect.top, time || new Date().getTime());
};
SignaturePad.prototype._addCurve = function (curve) {
var startPoint = curve.startPoint,
endPoint = curve.endPoint,
velocity, newWidth;
SignaturePad.prototype._addPoint = function (point) {
var points = this.points;
var tmp = void 0;
velocity = endPoint.velocityFrom(startPoint);
velocity = this.velocityFilterWeight * velocity
+ (1 - this.velocityFilterWeight) * this._lastVelocity;
points.push(point);
newWidth = this._strokeWidth(velocity);
this._drawCurve(curve, this._lastWidth, newWidth);
if (points.length > 2) {
// 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]);
this._lastVelocity = velocity;
this._lastWidth = newWidth;
};
tmp = this._calculateCurveControlPoints(points[0], points[1], points[2]);
var c2 = tmp.c2;
tmp = this._calculateCurveControlPoints(points[1], points[2], points[3]);
var c3 = tmp.c1;
var curve = new Bezier(points[1], c2, c3, points[2]);
var widths = this._calculateCurveWidths(curve);
SignaturePad.prototype._drawPoint = function (x, y, size) {
var ctx = this._ctx;
// Remove the first element from the list,
// so that we always have no more than 4 points in points array.
points.shift();
ctx.moveTo(x, y);
ctx.arc(x, y, size, 0, 2 * Math.PI, false);
this._isEmpty = false;
};
return { curve: curve, widths: widths };
}
SignaturePad.prototype._drawCurve = function (curve, startWidth, endWidth) {
var ctx = this._ctx,
widthDelta = endWidth - startWidth,
drawSteps, width, i, t, tt, ttt, u, uu, uuu, x, y;
return {};
};
drawSteps = Math.floor(curve.length());
ctx.beginPath();
for (i = 0; i < drawSteps; i++) {
// Calculate the Bezier (x, y) coordinate for this step.
t = i / drawSteps;
tt = t * t;
ttt = tt * t;
u = 1 - t;
uu = u * u;
uuu = uu * u;
SignaturePad.prototype._calculateCurveControlPoints = function (s1, s2, s3) {
var dx1 = s1.x - s2.x;
var dy1 = s1.y - s2.y;
var dx2 = s2.x - s3.x;
var dy2 = s2.y - s3.y;
x = uuu * curve.startPoint.x;
x += 3 * uu * t * curve.control1.x;
x += 3 * u * tt * curve.control2.x;
x += ttt * curve.endPoint.x;
var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
y = uuu * curve.startPoint.y;
y += 3 * uu * t * curve.control1.y;
y += 3 * u * tt * curve.control2.y;
y += ttt * curve.endPoint.y;
var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
width = startWidth + ttt * widthDelta;
this._drawPoint(x, y, width);
}
ctx.closePath();
ctx.fill();
};
var dxm = m1.x - m2.x;
var dym = m1.y - m2.y;
SignaturePad.prototype._strokeWidth = function (velocity) {
return Math.max(this.maxWidth / (velocity + 1), this.minWidth);
};
var k = l2 / (l1 + l2);
var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
var tx = s2.x - cm.x;
var ty = s2.y - cm.y;
var Point = function (x, y, time) {
this.x = x;
this.y = y;
this.time = time || new Date().getTime();
return {
c1: new Point(m1.x + tx, m1.y + ty),
c2: new Point(m2.x + tx, m2.y + ty)
};
};
Point.prototype.velocityFrom = function (start) {
return (this.time !== start.time) ? this.distanceTo(start) / (this.time - start.time) : 1;
};
SignaturePad.prototype._calculateCurveWidths = function (curve) {
var startPoint = curve.startPoint;
var endPoint = curve.endPoint;
var widths = { start: null, end: null };
Point.prototype.distanceTo = function (start) {
return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
};
var velocity = this.velocityFilterWeight * endPoint.velocityFrom(startPoint) + (1 - this.velocityFilterWeight) * this._lastVelocity;
var Bezier = function (startPoint, control1, control2, endPoint) {
this.startPoint = startPoint;
this.control1 = control1;
this.control2 = control2;
this.endPoint = endPoint;
};
var newWidth = this._strokeWidth(velocity);
// Returns approximated length.
Bezier.prototype.length = function () {
var steps = 10,
length = 0,
i, t, cx, cy, px, py, xdiff, ydiff;
widths.start = this._lastWidth;
widths.end = newWidth;
for (i = 0; i <= steps; i++) {
t = i / steps;
cx = this._point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
cy = this._point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
if (i > 0) {
xdiff = cx - px;
ydiff = cy - py;
length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
this._lastVelocity = velocity;
this._lastWidth = newWidth;
return widths;
};
SignaturePad.prototype._strokeWidth = function (velocity) {
return Math.max(this.maxWidth / (velocity + 1), this.minWidth);
};
SignaturePad.prototype._drawPoint = function (x, y, size) {
var ctx = this._ctx;
ctx.moveTo(x, y);
ctx.arc(x, y, size, 0, 2 * Math.PI, false);
this._isEmpty = false;
};
SignaturePad.prototype._drawCurve = function (curve, startWidth, endWidth) {
var ctx = this._ctx;
var widthDelta = endWidth - startWidth;
var drawSteps = Math.floor(curve.length());
ctx.beginPath();
for (var i = 0; i < drawSteps; i += 1) {
// Calculate the Bezier (x, y) coordinate for this step.
var t = i / drawSteps;
var tt = t * t;
var ttt = tt * t;
var u = 1 - t;
var uu = u * u;
var uuu = uu * u;
var x = uuu * curve.startPoint.x;
x += 3 * uu * t * curve.control1.x;
x += 3 * u * tt * curve.control2.x;
x += ttt * curve.endPoint.x;
var y = uuu * curve.startPoint.y;
y += 3 * uu * t * curve.control1.y;
y += 3 * u * tt * curve.control2.y;
y += ttt * curve.endPoint.y;
var width = startWidth + ttt * widthDelta;
this._drawPoint(x, y, width);
}
ctx.closePath();
ctx.fill();
};
SignaturePad.prototype._drawDot = function (point) {
var ctx = this._ctx;
var width = typeof this.dotSize === 'function' ? this.dotSize() : this.dotSize;
ctx.beginPath();
this._drawPoint(point.x, point.y, width);
ctx.closePath();
ctx.fill();
};
SignaturePad.prototype._fromData = function (pointGroups, drawCurve, drawDot) {
for (var i = 0; i < pointGroups.length; i += 1) {
var group = pointGroups[i];
if (group.length > 1) {
for (var j = 0; j < group.length; j += 1) {
var rawPoint = group[j];
var point = new Point(rawPoint.x, rawPoint.y, rawPoint.time);
if (j === 0) {
// First point in a group. Nothing to draw yet.
this._reset();
this._addPoint(point);
} else if (j !== group.length - 1) {
var _addPoint2 = this._addPoint(point),
curve = _addPoint2.curve,
widths = _addPoint2.widths;
if (curve && widths) {
drawCurve(curve, widths);
}
px = cx;
py = cy;
} else {
// Last point in a group. Do nothing.
}
}
return length;
};
} else {
this._reset();
var _rawPoint = group[0];
drawDot(_rawPoint);
}
}
};
Bezier.prototype._point = function (t, start, c1, c2, end) {
return start * (1.0 - t) * (1.0 - t) * (1.0 - t)
+ 3.0 * c1 * (1.0 - t) * (1.0 - t) * t
+ 3.0 * c2 * (1.0 - t) * t * t
+ end * t * t * t;
};
SignaturePad.prototype._toSVG = function () {
var _this2 = this;
return SignaturePad;
})(document);
var pointGroups = this._data;
var canvas = this._canvas;
var minX = 0;
var minY = 0;
var maxX = canvas.width;
var maxY = canvas.height;
var svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
return SignaturePad;
svg.setAttributeNS(null, 'width', canvas.width);
svg.setAttributeNS(null, 'height', canvas.height);
}));
this._fromData(pointGroups, function (curve, widths) {
var path = document.createElementNS('http;//www.w3.org/2000/svg', '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.
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.setAttributeNS(null, 'stroke-width', (widths.end * 2.25).toFixed(3));
path.setAttributeNS(null, 'stroke', _this2.penColor);
path.setAttributeNS(null, 'fill', 'none');
path.setAttributeNS(null, 'stroke-linecap', 'round');
svg.appendChild(path);
}
}, function (rawPoint) {
var circle = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
var dotSize = typeof _this2.dotSize === 'function' ? _this2.dotSize() : _this2.dotSize;
circle.setAttributeNS(null, 'r', dotSize);
circle.setAttributeNS(null, 'cx', rawPoint.x);
circle.setAttributeNS(null, 'cy', rawPoint.y);
circle.setAttributeNS(null, 'fill', _this2.penColor);
svg.appendChild(circle);
});
var prefix = 'data:image/svg+xml;base64,';
var header = '<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="' + minX + ' ' + minY + ' ' + maxX + ' ' + maxY + '">';
var body = svg.innerHTML;
var footer = '</svg>';
var data = header + body + footer;
return prefix + btoa(data);
};
SignaturePad.prototype.fromData = function (pointGroups) {
var _this3 = this;
this.clear();
this._fromData(pointGroups, function (curve, widths) {
return _this3._drawCurve(curve, widths.start, widths.end);
}, function (rawPoint) {
return _this3._drawDot(rawPoint);
});
};
SignaturePad.prototype.toData = function () {
return this._data;
};
module.exports = SignaturePad;
});

@@ -1,113 +0,96 @@

/*jslint node: true */
/* eslint func-names: "off" */
module.exports = function (grunt) {
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
'use strict';
eslint: {
target: [
'Gruntfile.js',
'src/signature_pad.js',
],
},
module.exports = function(grunt) {
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
jshint: {
options: {
jshintrc: '.jshintrc'
},
all: [
'Gruntfile.js',
'src/signature_pad.js'
]
babel: {
dist: {
files: {
'dist/signature_pad.js': ['src/signature_pad.js'],
},
},
},
banner: {
dev: {
options: {
banner: "/*!\n" +
" * Signature Pad v<%= pkg.version %>\n" +
" * <%= pkg.homepage %>\n" +
" *\n" +
" * Copyright <%= grunt.template.today('yyyy') %> <%= pkg.author.name %>\n" +
" * Released under the MIT license\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" +
" * http://benknowscode.wordpress.com/2012/09/14/path-interpolation-using-cubic-bezier-and-control-point-estimation-in-javascript\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" +
" */\n"
},
files: {
'signature_pad.js': ['src/signature_pad.js']
}
},
dist: {
options: {
banner: "/*!\n" +
" * Signature Pad v<%= pkg.version %> | <%= pkg.homepage %>\n" +
" * (c) <%= grunt.template.today('yyyy') %> <%= pkg.author.name %> | Released under the MIT license\n" +
" */\n"
},
files: {
'signature_pad.min.js': ['src/signature_pad.js']
}
}
uglify: {
dist: {
options: {
preserveComments: false,
},
files: {
'dist/signature_pad.min.js': ['dist/signature_pad.js'],
},
},
},
umd: {
options: {
objectToExport: 'SignaturePad',
globalAlias: 'SignaturePad',
indent: 4
},
dev: {
options: {
src: 'signature_pad.js',
dest: 'signature_pad.js'
}
},
dist: {
options: {
src: 'signature_pad.min.js',
dest: 'signature_pad.min.js'
}
}
banner: {
dev: {
options: {
banner: '/*!\n' +
' * Signature Pad v<%= pkg.version %>\n' +
' * <%= pkg.homepage %>\n' +
' *\n' +
" * Copyright <%= grunt.template.today('yyyy') %> <%= pkg.author.name %>\n" +
' * Released under the MIT license\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' +
' * http://benknowscode.wordpress.com/2012/09/14/path-interpolation-using-cubic-bezier-and-control-point-estimation-in-javascript\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' +
' */\n',
},
files: {
'dist/signature_pad.js': ['dist/signature_pad.js'],
},
},
dist: {
options: {
banner: '/*!\n' +
' * Signature Pad v<%= pkg.version %> | <%= pkg.homepage %>\n' +
" * (c) <%= grunt.template.today('yyyy') %> <%= pkg.author.name %> | Released under the MIT license\n" +
' */\n',
},
files: {
'dist/signature_pad.min.js': ['dist/signature_pad.min.js'],
},
},
},
uglify: {
dist: {
options: {
preserveComments: 'some'
},
files: {
'signature_pad.min.js': ['signature_pad.min.js']
}
}
}
});
grunt.registerMultiTask('banner', 'Add banner to files.', function () {
const options = this.options({
banner: '',
});
grunt.registerMultiTask('banner', 'Add banner to files.', function () {
var options = this.options({
banner: ''
});
const banner = grunt.template.process(options.banner);
var banner = grunt.template.process(options.banner);
this.files.forEach(function (f) {
var output = grunt.file.read(f.src);
output = banner + output;
grunt.file.write(f.dest, output);
});
this.files.forEach((f) => {
let output = grunt.file.read(f.src);
output = banner + output;
grunt.file.write(f.dest, output);
});
});
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.loadNpmTasks('grunt-umd');
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-eslint');
grunt.loadNpmTasks('grunt-babel');
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.registerTask('default', [
'jshint',
'banner',
'umd',
'uglify'
]);
grunt.registerTask('default', [
'eslint',
'babel',
'uglify',
'banner',
]);
};
{
"name": "signature_pad",
"description": "Library for drawing smooth signatures.",
"version": "1.5.3",
"version": "1.6.0-beta.1",
"homepage": "https://github.com/szimek/signature_pad",

@@ -11,19 +11,28 @@ "author": {

},
"main": "./signature_pad.js",
"main": "dist/signature_pad.js",
"scripts": {
"build": "grunt"
"build": "grunt",
"prepublish": "npm run build && cp dist/signature_pad.js example/js/signature_pad.js"
},
"repository" : {
"type" : "git",
"url" : "https://github.com/szimek/signature_pad.git"
"repository": {
"type": "git",
"url": "https://github.com/szimek/signature_pad.git"
},
"licenses": [{
"licenses": [
{
"type": "MIT"
}],
}
],
"devDependencies": {
"grunt": "~0.4.5",
"grunt-contrib-jshint": "~0.11.0",
"grunt-contrib-uglify": "~0.8.0",
"grunt-umd": "~2.3.2"
"babel-plugin-transform-es2015-modules-umd": "^6.18.0",
"babel-plugin-transform-exponentiation-operator": "^6.8.0",
"babel-preset-es2015": "^6.18.0",
"eslint": "^3.13.1",
"eslint-config-airbnb-base": "^11.0.1",
"eslint-plugin-import": "^2.2.0",
"grunt": "^1.0.1",
"grunt-babel": "^6.0.0",
"grunt-contrib-uglify": "^2.0.0",
"grunt-eslint": "^19.0.0"
}
}

@@ -1,2 +0,2 @@

Signature Pad [![Code Climate](https://codeclimate.com/github/szimek/signature_pad.png)](https://codeclimate.com/github/szimek/signature_pad)
Signature Pad [![Code Climate](https://d25lcipzij17d.cloudfront.net/badge.svg?id=js&type=6&v=1.5.3&x2=0)](https://www.npmjs.com/package/signature_pad) [![Code Climate](https://codeclimate.com/github/szimek/signature_pad.png)](https://codeclimate.com/github/szimek/signature_pad)
=============

@@ -26,5 +26,6 @@

// Returns signature image as data URL (see https://mdn.io/todataurl for the list of possible paramters)
// Returns signature image as data URL (see https://mdn.io/todataurl for the list of possible parameters)
signaturePad.toDataURL(); // save image as PNG
signaturePad.toDataURL("image/jpeg"); // save image as JPEG
signaturePad.toDataURL("image/svg+xml"); // save image as SVG

@@ -34,2 +35,8 @@ // Draws signature image from data URL

// Returns signature image as an array of point groups
const data = signaturePad.toData();
// Draws signature image from an array of point groups
signaturePad.fromData(data);
// Clears the canvas

@@ -134,12 +141,14 @@ signaturePad.clear();

$data_uri = "data:image/png;base64,iVBORw0K...";
$data_pieces = explode(",", $data_uri);
$encoded_image = $data_pieces[1];
$encoded_image = explode(",", $data_uri)[1];
$decoded_image = base64_decode($encoded_image);
file_put_contents( "signature.png",$decoded_image);
file_put_contents("signature.png", $decoded_image);
```
### Removing empty space around a signature
If you'd like to remove (trim) empty space around a signature, you can do it on the server side or the client side. On the server side you can use e.g. ImageMagic and its `trim` option: `convert -trim input.jpg output.jpg`. If you don't have access to the server, or just want to trim the image before submitting it to the server, you can do it on the client side as well. Here's an example: https://github.com/szimek/signature_pad/issues/49#issue-29108215.
#### Removing empty space around a signature
If you'd like to remove (trim) empty space around a signature, you can do it on the server side or the client side. On the server side you can use e.g. ImageMagic and its `trim` option: `convert -trim input.jpg output.jpg`. If you don't have access to the server, or just want to trim the image before submitting it to the server, you can do it on the client side as well. There are a few examples how to do it, e.g. [here](https://github.com/szimek/signature_pad/issues/49#issue-29108215) or [here](https://github.com/szimek/signature_pad/issues/49#issuecomment-260976909) and there's also a tiny library https://github.com/agilgur5/trim-canvas that provides this functionality.
#### Drawing over an image
Demo: https://jsfiddle.net/szimek/d6a78gwq/
## License
Released under the [MIT License](http://www.opensource.org/licenses/MIT).

@@ -1,352 +0,503 @@

var SignaturePad = (function (document) {
"use strict";
/* eslint-env browser */
/* eslint no-underscore-dangle: "off", func-names: "off", import/prefer-default-export: "off" */
function Point(x, y, time) {
this.x = x;
this.y = y;
this.time = time || new Date().getTime();
}
var SignaturePad = function (canvas, options) {
var self = this,
opts = options || {};
Point.prototype.velocityFrom = function (start) {
return (this.time !== start.time) ? this.distanceTo(start) / (this.time - start.time) : 1;
};
this.velocityFilterWeight = opts.velocityFilterWeight || 0.7;
this.minWidth = opts.minWidth || 0.5;
this.maxWidth = opts.maxWidth || 2.5;
this.dotSize = opts.dotSize || function () {
return (this.minWidth + this.maxWidth) / 2;
};
this.penColor = opts.penColor || "black";
this.backgroundColor = opts.backgroundColor || "rgba(0,0,0,0)";
this.onEnd = opts.onEnd;
this.onBegin = opts.onBegin;
Point.prototype.distanceTo = function (start) {
return Math.sqrt(((this.x - start.x) ** 2) + ((this.y - start.y) ** 2));
};
this._canvas = canvas;
this._ctx = canvas.getContext("2d");
this.clear();
function Bezier(startPoint, control1, control2, endPoint) {
this.startPoint = startPoint;
this.control1 = control1;
this.control2 = control2;
this.endPoint = endPoint;
}
// 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;
self._strokeBegin(event);
}
};
// Returns approximated length.
Bezier.prototype.length = function () {
const steps = 10;
let length = 0;
let px;
let py;
this._handleMouseMove = function (event) {
if (self._mouseButtonDown) {
self._strokeUpdate(event);
}
};
for (let i = 0; i <= steps; i += 1) {
const t = i / steps;
const cx = this._point(
t,
this.startPoint.x,
this.control1.x,
this.control2.x,
this.endPoint.x,
);
const cy = this._point(
t,
this.startPoint.y,
this.control1.y,
this.control2.y,
this.endPoint.y,
);
if (i > 0) {
const xdiff = cx - px;
const ydiff = cy - py;
length += Math.sqrt((xdiff * xdiff) + (ydiff * ydiff));
}
px = cx;
py = cy;
}
this._handleMouseUp = function (event) {
if (event.which === 1 && self._mouseButtonDown) {
self._mouseButtonDown = false;
self._strokeEnd(event);
}
};
return length;
};
this._handleTouchStart = function (event) {
if (event.targetTouches.length == 1) {
var touch = event.changedTouches[0];
self._strokeBegin(touch);
}
};
/* eslint-disable no-multi-spaces, space-in-parens */
Bezier.prototype._point = function (t, start, c1, c2, end) {
return ( start * (1.0 - t) * (1.0 - t) * (1.0 - t))
+ (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
+ (3.0 * c2 * (1.0 - t) * t * t)
+ ( end * t * t * t);
};
/* eslint-enable no-multi-spaces, space-in-parens */
this._handleTouchMove = function (event) {
// Prevent scrolling.
event.preventDefault();
function SignaturePad(canvas, options) {
const self = this;
const opts = options || {};
var touch = event.targetTouches[0];
self._strokeUpdate(touch);
};
this.velocityFilterWeight = opts.velocityFilterWeight || 0.7;
this.minWidth = opts.minWidth || 0.5;
this.maxWidth = opts.maxWidth || 2.5;
this.dotSize = opts.dotSize || function () {
return (this.minWidth + this.maxWidth) / 2;
};
this.penColor = opts.penColor || 'black';
this.backgroundColor = opts.backgroundColor || 'rgba(0,0,0,0)';
this.onBegin = opts.onBegin;
this.onEnd = opts.onEnd;
this._handleTouchEnd = function (event) {
var wasCanvasTouched = event.target === self._canvas;
if (wasCanvasTouched) {
event.preventDefault();
self._strokeEnd(event);
}
};
this._canvas = canvas;
this._ctx = canvas.getContext('2d');
this.clear();
this._handleMouseEvents();
this._handleTouchEvents();
};
// 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;
self._strokeBegin(event);
}
};
SignaturePad.prototype.clear = function () {
var ctx = this._ctx,
canvas = this._canvas;
this._handleMouseMove = function (event) {
if (self._mouseButtonDown) {
self._strokeUpdate(event);
}
};
ctx.fillStyle = this.backgroundColor;
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillRect(0, 0, canvas.width, canvas.height);
this._reset();
};
this._handleMouseUp = function (event) {
if (event.which === 1 && self._mouseButtonDown) {
self._mouseButtonDown = false;
self._strokeEnd(event);
}
};
SignaturePad.prototype.toDataURL = function (imageType, quality) {
var canvas = this._canvas;
return canvas.toDataURL.apply(canvas, arguments);
};
this._handleTouchStart = function (event) {
if (event.targetTouches.length === 1) {
const touch = event.changedTouches[0];
self._strokeBegin(touch);
}
};
SignaturePad.prototype.fromDataURL = function (dataUrl) {
var self = this,
image = new Image(),
ratio = window.devicePixelRatio || 1,
width = this._canvas.width / ratio,
height = this._canvas.height / ratio;
this._handleTouchMove = function (event) {
// Prevent scrolling.
event.preventDefault();
this._reset();
image.src = dataUrl;
image.onload = function () {
self._ctx.drawImage(image, 0, 0, width, height);
};
this._isEmpty = false;
};
const touch = event.targetTouches[0];
self._strokeUpdate(touch);
};
SignaturePad.prototype._strokeUpdate = function (event) {
var point = this._createPoint(event);
this._addPoint(point);
};
this._handleTouchEnd = function (event) {
const wasCanvasTouched = event.target === self._canvas;
if (wasCanvasTouched) {
event.preventDefault();
self._strokeEnd(event);
}
};
SignaturePad.prototype._strokeBegin = function (event) {
this._reset();
this._strokeUpdate(event);
if (typeof this.onBegin === 'function') {
this.onBegin(event);
}
};
// Enable mouse and touch event handlers
this.on();
}
SignaturePad.prototype._strokeDraw = function (point) {
var ctx = this._ctx,
dotSize = typeof(this.dotSize) === 'function' ? this.dotSize() : this.dotSize;
// Public methods
SignaturePad.prototype.clear = function () {
const ctx = this._ctx;
const canvas = this._canvas;
ctx.beginPath();
this._drawPoint(point.x, point.y, dotSize);
ctx.closePath();
ctx.fill();
};
ctx.fillStyle = this.backgroundColor;
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillRect(0, 0, canvas.width, canvas.height);
SignaturePad.prototype._strokeEnd = function (event) {
var canDrawCurve = this.points.length > 2,
point = this.points[0];
this._data = [];
this._reset();
this._isEmpty = true;
};
if (!canDrawCurve && point) {
this._strokeDraw(point);
}
if (typeof this.onEnd === 'function') {
this.onEnd(event);
}
};
SignaturePad.prototype.fromDataURL = function (dataUrl) {
const image = new Image();
const ratio = window.devicePixelRatio || 1;
const width = this._canvas.width / ratio;
const height = this._canvas.height / ratio;
SignaturePad.prototype._handleMouseEvents = function () {
this._mouseButtonDown = false;
this._reset();
image.src = dataUrl;
image.onload = () => {
this._ctx.drawImage(image, 0, 0, width, height);
};
this._isEmpty = false;
};
this._canvas.addEventListener("mousedown", this._handleMouseDown);
this._canvas.addEventListener("mousemove", this._handleMouseMove);
document.addEventListener("mouseup", this._handleMouseUp);
};
SignaturePad.prototype.toDataURL = function (type, ...options) {
switch (type) {
case 'image/svg+xml':
return this._toSVG();
default:
return this._canvas.toDataURL(type, ...options);
}
};
SignaturePad.prototype._handleTouchEvents = function () {
// Pass touch events to canvas element on mobile IE11 and Edge.
this._canvas.style.msTouchAction = 'none';
this._canvas.style.touchAction = 'none';
SignaturePad.prototype.on = function () {
this._handleMouseEvents();
this._handleTouchEvents();
};
this._canvas.addEventListener("touchstart", this._handleTouchStart);
this._canvas.addEventListener("touchmove", this._handleTouchMove);
this._canvas.addEventListener("touchend", this._handleTouchEnd);
};
SignaturePad.prototype.off = function () {
this._canvas.removeEventListener('mousedown', this._handleMouseDown);
this._canvas.removeEventListener('mousemove', this._handleMouseMove);
document.removeEventListener('mouseup', this._handleMouseUp);
SignaturePad.prototype.on = function () {
this._handleMouseEvents();
this._handleTouchEvents();
};
this._canvas.removeEventListener('touchstart', this._handleTouchStart);
this._canvas.removeEventListener('touchmove', this._handleTouchMove);
this._canvas.removeEventListener('touchend', this._handleTouchEnd);
};
SignaturePad.prototype.off = function () {
this._canvas.removeEventListener("mousedown", this._handleMouseDown);
this._canvas.removeEventListener("mousemove", this._handleMouseMove);
document.removeEventListener("mouseup", this._handleMouseUp);
SignaturePad.prototype.isEmpty = function () {
return this._isEmpty;
};
this._canvas.removeEventListener("touchstart", this._handleTouchStart);
this._canvas.removeEventListener("touchmove", this._handleTouchMove);
this._canvas.removeEventListener("touchend", this._handleTouchEnd);
};
// Private methods
SignaturePad.prototype._strokeBegin = function (event) {
this._data.push([]);
this._reset();
this._strokeUpdate(event);
SignaturePad.prototype.isEmpty = function () {
return this._isEmpty;
};
if (typeof this.onBegin === 'function') {
this.onBegin(event);
}
};
SignaturePad.prototype._reset = function () {
this.points = [];
this._lastVelocity = 0;
this._lastWidth = (this.minWidth + this.maxWidth) / 2;
this._isEmpty = true;
this._ctx.fillStyle = this.penColor;
};
SignaturePad.prototype._strokeUpdate = function (event) {
const x = event.clientX;
const y = event.clientY;
SignaturePad.prototype._createPoint = function (event) {
var rect = this._canvas.getBoundingClientRect();
return new Point(
event.clientX - rect.left,
event.clientY - rect.top
);
};
const point = this._createPoint(x, y);
const { curve, widths } = this._addPoint(point);
SignaturePad.prototype._addPoint = function (point) {
var points = this.points,
c2, c3,
curve, tmp;
if (curve && widths) {
this._drawCurve(curve, widths.start, widths.end);
}
points.push(point);
this._data[this._data.length - 1].push({
x: point.x,
y: point.y,
time: point.time,
});
};
if (points.length > 2) {
// 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]);
SignaturePad.prototype._strokeEnd = function (event) {
const canDrawCurve = this.points.length > 2;
const point = this.points[0];
tmp = this._calculateCurveControlPoints(points[0], points[1], points[2]);
c2 = tmp.c2;
tmp = this._calculateCurveControlPoints(points[1], points[2], points[3]);
c3 = tmp.c1;
curve = new Bezier(points[1], c2, c3, points[2]);
this._addCurve(curve);
if (!canDrawCurve && point) {
this._drawDot(point);
}
// Remove the first element from the list,
// so that we always have no more than 4 points in points array.
points.shift();
}
};
if (typeof this.onEnd === 'function') {
this.onEnd(event);
}
};
SignaturePad.prototype._calculateCurveControlPoints = function (s1, s2, s3) {
var dx1 = s1.x - s2.x, dy1 = s1.y - s2.y,
dx2 = s2.x - s3.x, dy2 = s2.y - s3.y,
SignaturePad.prototype._handleMouseEvents = function () {
this._mouseButtonDown = false;
m1 = {x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0},
m2 = {x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0},
this._canvas.addEventListener('mousedown', this._handleMouseDown);
this._canvas.addEventListener('mousemove', this._handleMouseMove);
document.addEventListener('mouseup', this._handleMouseUp);
};
l1 = Math.sqrt(dx1*dx1 + dy1*dy1),
l2 = Math.sqrt(dx2*dx2 + dy2*dy2),
SignaturePad.prototype._handleTouchEvents = function () {
// Pass touch events to canvas element on mobile IE11 and Edge.
this._canvas.style.msTouchAction = 'none';
this._canvas.style.touchAction = 'none';
dxm = (m1.x - m2.x),
dym = (m1.y - m2.y),
this._canvas.addEventListener('touchstart', this._handleTouchStart);
this._canvas.addEventListener('touchmove', this._handleTouchMove);
this._canvas.addEventListener('touchend', this._handleTouchEnd);
};
k = l2 / (l1 + l2),
cm = {x: m2.x + dxm*k, y: m2.y + dym*k},
SignaturePad.prototype._reset = function () {
this.points = [];
this._lastVelocity = 0;
this._lastWidth = (this.minWidth + this.maxWidth) / 2;
this._ctx.fillStyle = this.penColor;
};
tx = s2.x - cm.x,
ty = s2.y - cm.y;
SignaturePad.prototype._createPoint = function (x, y, time) {
const rect = this._canvas.getBoundingClientRect();
return {
c1: new Point(m1.x + tx, m1.y + ty),
c2: new Point(m2.x + tx, m2.y + ty)
};
};
return new Point(
x - rect.left,
y - rect.top,
time || new Date().getTime(),
);
};
SignaturePad.prototype._addCurve = function (curve) {
var startPoint = curve.startPoint,
endPoint = curve.endPoint,
velocity, newWidth;
SignaturePad.prototype._addPoint = function (point) {
const points = this.points;
let tmp;
velocity = endPoint.velocityFrom(startPoint);
velocity = this.velocityFilterWeight * velocity
+ (1 - this.velocityFilterWeight) * this._lastVelocity;
points.push(point);
newWidth = this._strokeWidth(velocity);
this._drawCurve(curve, this._lastWidth, newWidth);
if (points.length > 2) {
// 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]);
this._lastVelocity = velocity;
this._lastWidth = newWidth;
};
tmp = this._calculateCurveControlPoints(points[0], points[1], points[2]);
const c2 = tmp.c2;
tmp = this._calculateCurveControlPoints(points[1], points[2], points[3]);
const c3 = tmp.c1;
const curve = new Bezier(points[1], c2, c3, points[2]);
const widths = this._calculateCurveWidths(curve);
SignaturePad.prototype._drawPoint = function (x, y, size) {
var ctx = this._ctx;
// Remove the first element from the list,
// so that we always have no more than 4 points in points array.
points.shift();
ctx.moveTo(x, y);
ctx.arc(x, y, size, 0, 2 * Math.PI, false);
this._isEmpty = false;
};
return { curve, widths };
}
SignaturePad.prototype._drawCurve = function (curve, startWidth, endWidth) {
var ctx = this._ctx,
widthDelta = endWidth - startWidth,
drawSteps, width, i, t, tt, ttt, u, uu, uuu, x, y;
return {};
};
drawSteps = Math.floor(curve.length());
ctx.beginPath();
for (i = 0; i < drawSteps; i++) {
// Calculate the Bezier (x, y) coordinate for this step.
t = i / drawSteps;
tt = t * t;
ttt = tt * t;
u = 1 - t;
uu = u * u;
uuu = uu * u;
SignaturePad.prototype._calculateCurveControlPoints = function (s1, s2, s3) {
const dx1 = s1.x - s2.x;
const dy1 = s1.y - s2.y;
const dx2 = s2.x - s3.x;
const dy2 = s2.y - s3.y;
x = uuu * curve.startPoint.x;
x += 3 * uu * t * curve.control1.x;
x += 3 * u * tt * curve.control2.x;
x += ttt * curve.endPoint.x;
const m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
const m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
y = uuu * curve.startPoint.y;
y += 3 * uu * t * curve.control1.y;
y += 3 * u * tt * curve.control2.y;
y += ttt * curve.endPoint.y;
const l1 = Math.sqrt((dx1 * dx1) + (dy1 * dy1));
const l2 = Math.sqrt((dx2 * dx2) + (dy2 * dy2));
width = startWidth + ttt * widthDelta;
this._drawPoint(x, y, width);
}
ctx.closePath();
ctx.fill();
};
const dxm = (m1.x - m2.x);
const dym = (m1.y - m2.y);
SignaturePad.prototype._strokeWidth = function (velocity) {
return Math.max(this.maxWidth / (velocity + 1), this.minWidth);
};
const k = l2 / (l1 + l2);
const cm = { x: m2.x + (dxm * k), y: m2.y + (dym * k) };
const tx = s2.x - cm.x;
const ty = s2.y - cm.y;
var Point = function (x, y, time) {
this.x = x;
this.y = y;
this.time = time || new Date().getTime();
};
return {
c1: new Point(m1.x + tx, m1.y + ty),
c2: new Point(m2.x + tx, m2.y + ty),
};
};
Point.prototype.velocityFrom = function (start) {
return (this.time !== start.time) ? this.distanceTo(start) / (this.time - start.time) : 1;
};
SignaturePad.prototype._calculateCurveWidths = function (curve) {
const startPoint = curve.startPoint;
const endPoint = curve.endPoint;
const widths = { start: null, end: null };
Point.prototype.distanceTo = function (start) {
return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
};
const velocity = (this.velocityFilterWeight * endPoint.velocityFrom(startPoint))
+ ((1 - this.velocityFilterWeight) * this._lastVelocity);
var Bezier = function (startPoint, control1, control2, endPoint) {
this.startPoint = startPoint;
this.control1 = control1;
this.control2 = control2;
this.endPoint = endPoint;
};
const newWidth = this._strokeWidth(velocity);
// Returns approximated length.
Bezier.prototype.length = function () {
var steps = 10,
length = 0,
i, t, cx, cy, px, py, xdiff, ydiff;
widths.start = this._lastWidth;
widths.end = newWidth;
for (i = 0; i <= steps; i++) {
t = i / steps;
cx = this._point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
cy = this._point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
if (i > 0) {
xdiff = cx - px;
ydiff = cy - py;
length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
}
px = cx;
py = cy;
this._lastVelocity = velocity;
this._lastWidth = newWidth;
return widths;
};
SignaturePad.prototype._strokeWidth = function (velocity) {
return Math.max(this.maxWidth / (velocity + 1), this.minWidth);
};
SignaturePad.prototype._drawPoint = function (x, y, size) {
const ctx = this._ctx;
ctx.moveTo(x, y);
ctx.arc(x, y, size, 0, 2 * Math.PI, false);
this._isEmpty = false;
};
SignaturePad.prototype._drawCurve = function (curve, startWidth, endWidth) {
const ctx = this._ctx;
const widthDelta = endWidth - startWidth;
const drawSteps = Math.floor(curve.length());
ctx.beginPath();
for (let i = 0; i < drawSteps; i += 1) {
// Calculate the Bezier (x, y) coordinate for this step.
const t = i / drawSteps;
const tt = t * t;
const ttt = tt * t;
const u = 1 - t;
const uu = u * u;
const uuu = uu * u;
let x = uuu * curve.startPoint.x;
x += 3 * uu * t * curve.control1.x;
x += 3 * u * tt * curve.control2.x;
x += ttt * curve.endPoint.x;
let y = uuu * curve.startPoint.y;
y += 3 * uu * t * curve.control1.y;
y += 3 * u * tt * curve.control2.y;
y += ttt * curve.endPoint.y;
const width = startWidth + (ttt * widthDelta);
this._drawPoint(x, y, width);
}
ctx.closePath();
ctx.fill();
};
SignaturePad.prototype._drawDot = function (point) {
const ctx = this._ctx;
const width = (typeof this.dotSize) === 'function' ? this.dotSize() : this.dotSize;
ctx.beginPath();
this._drawPoint(point.x, point.y, width);
ctx.closePath();
ctx.fill();
};
SignaturePad.prototype._fromData = function (pointGroups, drawCurve, drawDot) {
for (let i = 0; i < pointGroups.length; i += 1) {
const group = pointGroups[i];
if (group.length > 1) {
for (let j = 0; j < group.length; j += 1) {
const rawPoint = group[j];
const point = new Point(rawPoint.x, rawPoint.y, rawPoint.time);
if (j === 0) {
// First point in a group. Nothing to draw yet.
this._reset();
this._addPoint(point);
} else if (j !== group.length - 1) {
// Middle point in a group.
const { curve, widths } = this._addPoint(point);
if (curve && widths) {
drawCurve(curve, widths);
}
} else {
// Last point in a group. Do nothing.
}
return length;
};
}
} else {
this._reset();
const rawPoint = group[0];
drawDot(rawPoint);
}
}
};
Bezier.prototype._point = function (t, start, c1, c2, end) {
return start * (1.0 - t) * (1.0 - t) * (1.0 - t)
+ 3.0 * c1 * (1.0 - t) * (1.0 - t) * t
+ 3.0 * c2 * (1.0 - t) * t * t
+ end * t * t * t;
};
SignaturePad.prototype._toSVG = function () {
const pointGroups = this._data;
const canvas = this._canvas;
const minX = 0;
const minY = 0;
const maxX = canvas.width;
const maxY = canvas.height;
const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
return SignaturePad;
})(document);
svg.setAttributeNS(null, 'width', canvas.width);
svg.setAttributeNS(null, 'height', canvas.height);
this._fromData(
pointGroups,
(curve, widths) => {
const path = document.createElementNS('http;//www.w3.org/2000/svg', '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.
if (!isNaN(curve.control1.x) &&
!isNaN(curve.control1.y) &&
!isNaN(curve.control2.x) &&
!isNaN(curve.control2.y)) {
const 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.setAttributeNS(null, 'stroke-width', (widths.end * 2.25).toFixed(3));
path.setAttributeNS(null, 'stroke', this.penColor);
path.setAttributeNS(null, 'fill', 'none');
path.setAttributeNS(null, 'stroke-linecap', 'round');
svg.appendChild(path);
}
},
(rawPoint) => {
const circle = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
const dotSize = (typeof this.dotSize) === 'function' ? this.dotSize() : this.dotSize;
circle.setAttributeNS(null, 'r', dotSize);
circle.setAttributeNS(null, 'cx', rawPoint.x);
circle.setAttributeNS(null, 'cy', rawPoint.y);
circle.setAttributeNS(null, 'fill', this.penColor);
svg.appendChild(circle);
},
);
const prefix = 'data:image/svg+xml;base64,';
const header = `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="${minX} ${minY} ${maxX} ${maxY}">`;
const body = svg.innerHTML;
const footer = '</svg>';
const data = header + body + footer;
return prefix + btoa(data);
};
SignaturePad.prototype.fromData = function (pointGroups) {
this.clear();
this._fromData(
pointGroups,
(curve, widths) => this._drawCurve(curve, widths.start, widths.end),
rawPoint => this._drawDot(rawPoint),
);
};
SignaturePad.prototype.toData = function () {
return this._data;
};
module.exports = SignaturePad;

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc