Skouter mortgage estimates. Web application with view written in PHP and Vue, but controller and models in Go.
25'ten fazla konu seçemezsiniz Konular bir harf veya rakamla başlamalı, kısa çizgiler ('-') içerebilir ve en fazla 35 karakter uzunluğunda olabilir.
 
 
 
 
 
 

618 satır
18 KiB

  1. /*!
  2. * Signature Pad v2.3.2
  3. * https://github.com/szimek/signature_pad
  4. *
  5. * Copyright 2017 Szymon Nowak
  6. * Released under the MIT license
  7. *
  8. * The main idea and some parts of the code (e.g. drawing variable width Bézier curve) are taken from:
  9. * http://corner.squareup.com/2012/07/smoother-signatures.html
  10. *
  11. * Implementation of interpolation using cubic Bézier curves is taken from:
  12. * http://benknowscode.wordpress.com/2012/09/14/path-interpolation-using-cubic-bezier-and-control-point-estimation-in-javascript
  13. *
  14. * Algorithm for approximated length of a Bézier curve is taken from:
  15. * http://www.lemoda.net/maths/bezier-length/index.html
  16. *
  17. */
  18. (function (global, factory) {
  19. typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
  20. typeof define === 'function' && define.amd ? define(factory) :
  21. (global.SignaturePad = factory());
  22. }(this, (function () { 'use strict';
  23. function Point(x, y, time) {
  24. this.x = x;
  25. this.y = y;
  26. this.time = time || new Date().getTime();
  27. }
  28. Point.prototype.velocityFrom = function (start) {
  29. return this.time !== start.time ? this.distanceTo(start) / (this.time - start.time) : 1;
  30. };
  31. Point.prototype.distanceTo = function (start) {
  32. return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
  33. };
  34. Point.prototype.equals = function (other) {
  35. return this.x === other.x && this.y === other.y && this.time === other.time;
  36. };
  37. function Bezier(startPoint, control1, control2, endPoint) {
  38. this.startPoint = startPoint;
  39. this.control1 = control1;
  40. this.control2 = control2;
  41. this.endPoint = endPoint;
  42. }
  43. // Returns approximated length.
  44. Bezier.prototype.length = function () {
  45. var steps = 10;
  46. var length = 0;
  47. var px = void 0;
  48. var py = void 0;
  49. for (var i = 0; i <= steps; i += 1) {
  50. var t = i / steps;
  51. var cx = this._point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
  52. var cy = this._point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
  53. if (i > 0) {
  54. var xdiff = cx - px;
  55. var ydiff = cy - py;
  56. length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
  57. }
  58. px = cx;
  59. py = cy;
  60. }
  61. return length;
  62. };
  63. /* eslint-disable no-multi-spaces, space-in-parens */
  64. Bezier.prototype._point = function (t, start, c1, c2, end) {
  65. 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;
  66. };
  67. /* eslint-disable */
  68. // http://stackoverflow.com/a/27078401/815507
  69. function throttle(func, wait, options) {
  70. var context, args, result;
  71. var timeout = null;
  72. var previous = 0;
  73. if (!options) options = {};
  74. var later = function later() {
  75. previous = options.leading === false ? 0 : Date.now();
  76. timeout = null;
  77. result = func.apply(context, args);
  78. if (!timeout) context = args = null;
  79. };
  80. return function () {
  81. var now = Date.now();
  82. if (!previous && options.leading === false) previous = now;
  83. var remaining = wait - (now - previous);
  84. context = this;
  85. args = arguments;
  86. if (remaining <= 0 || remaining > wait) {
  87. if (timeout) {
  88. clearTimeout(timeout);
  89. timeout = null;
  90. }
  91. previous = now;
  92. result = func.apply(context, args);
  93. if (!timeout) context = args = null;
  94. } else if (!timeout && options.trailing !== false) {
  95. timeout = setTimeout(later, remaining);
  96. }
  97. return result;
  98. };
  99. }
  100. function SignaturePad(canvas, options) {
  101. var self = this;
  102. var opts = options || {};
  103. this.velocityFilterWeight = opts.velocityFilterWeight || 0.7;
  104. this.minWidth = opts.minWidth || 0.5;
  105. this.maxWidth = opts.maxWidth || 2.5;
  106. this.throttle = 'throttle' in opts ? opts.throttle : 16; // in miliseconds
  107. this.minDistance = 'minDistance' in opts ? opts.minDistance : 5;
  108. if (this.throttle) {
  109. this._strokeMoveUpdate = throttle(SignaturePad.prototype._strokeUpdate, this.throttle);
  110. } else {
  111. this._strokeMoveUpdate = SignaturePad.prototype._strokeUpdate;
  112. }
  113. this.dotSize = opts.dotSize || function () {
  114. return (this.minWidth + this.maxWidth) / 2;
  115. };
  116. this.penColor = opts.penColor || 'black';
  117. this.backgroundColor = opts.backgroundColor || 'rgba(0,0,0,0)';
  118. this.onBegin = opts.onBegin;
  119. this.onEnd = opts.onEnd;
  120. this._canvas = canvas;
  121. this._ctx = canvas.getContext('2d');
  122. this.clear();
  123. // We need add these inline so they are available to unbind while still having
  124. // access to 'self' we could use _.bind but it's not worth adding a dependency.
  125. this._handleMouseDown = function (event) {
  126. if (event.which === 1) {
  127. self._mouseButtonDown = true;
  128. self._strokeBegin(event);
  129. }
  130. };
  131. this._handleMouseMove = function (event) {
  132. if (self._mouseButtonDown) {
  133. self._strokeMoveUpdate(event);
  134. }
  135. };
  136. this._handleMouseUp = function (event) {
  137. if (event.which === 1 && self._mouseButtonDown) {
  138. self._mouseButtonDown = false;
  139. self._strokeEnd(event);
  140. }
  141. };
  142. this._handleTouchStart = function (event) {
  143. // Prevent scrolling.
  144. event.preventDefault();
  145. if (event.targetTouches.length === 1) {
  146. var touch = event.changedTouches[0];
  147. self._strokeBegin(touch);
  148. }
  149. };
  150. this._handleTouchMove = function (event) {
  151. // Prevent scrolling.
  152. event.preventDefault();
  153. var touch = event.targetTouches[0];
  154. self._strokeMoveUpdate(touch);
  155. };
  156. this._handleTouchEnd = function (event) {
  157. var wasCanvasTouched = event.target === self._canvas;
  158. if (wasCanvasTouched) {
  159. event.preventDefault();
  160. self._strokeEnd(event);
  161. }
  162. };
  163. // Enable mouse and touch event handlers
  164. this.on();
  165. }
  166. // Public methods
  167. SignaturePad.prototype.clear = function () {
  168. var ctx = this._ctx;
  169. var canvas = this._canvas;
  170. ctx.fillStyle = this.backgroundColor;
  171. ctx.clearRect(0, 0, canvas.width, canvas.height);
  172. ctx.fillRect(0, 0, canvas.width, canvas.height);
  173. this._data = [];
  174. this._reset();
  175. this._isEmpty = true;
  176. };
  177. SignaturePad.prototype.fromDataURL = function (dataUrl) {
  178. var _this = this;
  179. var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  180. var image = new Image();
  181. var ratio = options.ratio || window.devicePixelRatio || 1;
  182. var width = options.width || this._canvas.width / ratio;
  183. var height = options.height || this._canvas.height / ratio;
  184. this._reset();
  185. image.src = dataUrl;
  186. image.onload = function () {
  187. _this._ctx.drawImage(image, 0, 0, width, height);
  188. };
  189. this._isEmpty = false;
  190. };
  191. SignaturePad.prototype.toDataURL = function (type) {
  192. var _canvas;
  193. switch (type) {
  194. case 'image/svg+xml':
  195. return this._toSVG();
  196. default:
  197. for (var _len = arguments.length, options = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
  198. options[_key - 1] = arguments[_key];
  199. }
  200. return (_canvas = this._canvas).toDataURL.apply(_canvas, [type].concat(options));
  201. }
  202. };
  203. SignaturePad.prototype.on = function () {
  204. this._handleMouseEvents();
  205. this._handleTouchEvents();
  206. };
  207. SignaturePad.prototype.off = function () {
  208. // Pass touch events to canvas element on mobile IE11 and Edge.
  209. this._canvas.style.msTouchAction = 'auto';
  210. this._canvas.style.touchAction = 'auto';
  211. this._canvas.removeEventListener('mousedown', this._handleMouseDown);
  212. this._canvas.removeEventListener('mousemove', this._handleMouseMove);
  213. document.removeEventListener('mouseup', this._handleMouseUp);
  214. this._canvas.removeEventListener('touchstart', this._handleTouchStart);
  215. this._canvas.removeEventListener('touchmove', this._handleTouchMove);
  216. this._canvas.removeEventListener('touchend', this._handleTouchEnd);
  217. };
  218. SignaturePad.prototype.isEmpty = function () {
  219. return this._isEmpty;
  220. };
  221. // Private methods
  222. SignaturePad.prototype._strokeBegin = function (event) {
  223. this._data.push([]);
  224. this._reset();
  225. this._strokeUpdate(event);
  226. if (typeof this.onBegin === 'function') {
  227. this.onBegin(event);
  228. }
  229. };
  230. SignaturePad.prototype._strokeUpdate = function (event) {
  231. var x = event.clientX;
  232. var y = event.clientY;
  233. var point = this._createPoint(x, y);
  234. var lastPointGroup = this._data[this._data.length - 1];
  235. var lastPoint = lastPointGroup && lastPointGroup[lastPointGroup.length - 1];
  236. var isLastPointTooClose = lastPoint && point.distanceTo(lastPoint) < this.minDistance;
  237. // Skip this point if it's too close to the previous one
  238. if (!(lastPoint && isLastPointTooClose)) {
  239. var _addPoint = this._addPoint(point),
  240. curve = _addPoint.curve,
  241. widths = _addPoint.widths;
  242. if (curve && widths) {
  243. this._drawCurve(curve, widths.start, widths.end);
  244. }
  245. this._data[this._data.length - 1].push({
  246. x: point.x,
  247. y: point.y,
  248. time: point.time,
  249. color: this.penColor
  250. });
  251. }
  252. };
  253. SignaturePad.prototype._strokeEnd = function (event) {
  254. var canDrawCurve = this.points.length > 2;
  255. var point = this.points[0]; // Point instance
  256. if (!canDrawCurve && point) {
  257. this._drawDot(point);
  258. }
  259. if (point) {
  260. var lastPointGroup = this._data[this._data.length - 1];
  261. var lastPoint = lastPointGroup[lastPointGroup.length - 1]; // plain object
  262. // When drawing a dot, there's only one point in a group, so without this check
  263. // such group would end up with exactly the same 2 points.
  264. if (!point.equals(lastPoint)) {
  265. lastPointGroup.push({
  266. x: point.x,
  267. y: point.y,
  268. time: point.time,
  269. color: this.penColor
  270. });
  271. }
  272. }
  273. if (typeof this.onEnd === 'function') {
  274. this.onEnd(event);
  275. }
  276. };
  277. SignaturePad.prototype._handleMouseEvents = function () {
  278. this._mouseButtonDown = false;
  279. this._canvas.addEventListener('mousedown', this._handleMouseDown);
  280. this._canvas.addEventListener('mousemove', this._handleMouseMove);
  281. document.addEventListener('mouseup', this._handleMouseUp);
  282. };
  283. SignaturePad.prototype._handleTouchEvents = function () {
  284. // Pass touch events to canvas element on mobile IE11 and Edge.
  285. this._canvas.style.msTouchAction = 'none';
  286. this._canvas.style.touchAction = 'none';
  287. this._canvas.addEventListener('touchstart', this._handleTouchStart);
  288. this._canvas.addEventListener('touchmove', this._handleTouchMove);
  289. this._canvas.addEventListener('touchend', this._handleTouchEnd);
  290. };
  291. SignaturePad.prototype._reset = function () {
  292. this.points = [];
  293. this._lastVelocity = 0;
  294. this._lastWidth = (this.minWidth + this.maxWidth) / 2;
  295. this._ctx.fillStyle = this.penColor;
  296. };
  297. SignaturePad.prototype._createPoint = function (x, y, time) {
  298. var rect = this._canvas.getBoundingClientRect();
  299. return new Point(x - rect.left, y - rect.top, time || new Date().getTime());
  300. };
  301. SignaturePad.prototype._addPoint = function (point) {
  302. var points = this.points;
  303. var tmp = void 0;
  304. points.push(point);
  305. if (points.length > 2) {
  306. // To reduce the initial lag make it work with 3 points
  307. // by copying the first point to the beginning.
  308. if (points.length === 3) points.unshift(points[0]);
  309. tmp = this._calculateCurveControlPoints(points[0], points[1], points[2]);
  310. var c2 = tmp.c2;
  311. tmp = this._calculateCurveControlPoints(points[1], points[2], points[3]);
  312. var c3 = tmp.c1;
  313. var curve = new Bezier(points[1], c2, c3, points[2]);
  314. var widths = this._calculateCurveWidths(curve);
  315. // Remove the first element from the list,
  316. // so that we always have no more than 4 points in points array.
  317. points.shift();
  318. return { curve: curve, widths: widths };
  319. }
  320. return {};
  321. };
  322. SignaturePad.prototype._calculateCurveControlPoints = function (s1, s2, s3) {
  323. var dx1 = s1.x - s2.x;
  324. var dy1 = s1.y - s2.y;
  325. var dx2 = s2.x - s3.x;
  326. var dy2 = s2.y - s3.y;
  327. var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
  328. var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
  329. var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
  330. var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
  331. var dxm = m1.x - m2.x;
  332. var dym = m1.y - m2.y;
  333. var k = l2 / (l1 + l2);
  334. var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
  335. var tx = s2.x - cm.x;
  336. var ty = s2.y - cm.y;
  337. return {
  338. c1: new Point(m1.x + tx, m1.y + ty),
  339. c2: new Point(m2.x + tx, m2.y + ty)
  340. };
  341. };
  342. SignaturePad.prototype._calculateCurveWidths = function (curve) {
  343. var startPoint = curve.startPoint;
  344. var endPoint = curve.endPoint;
  345. var widths = { start: null, end: null };
  346. var velocity = this.velocityFilterWeight * endPoint.velocityFrom(startPoint) + (1 - this.velocityFilterWeight) * this._lastVelocity;
  347. var newWidth = this._strokeWidth(velocity);
  348. widths.start = this._lastWidth;
  349. widths.end = newWidth;
  350. this._lastVelocity = velocity;
  351. this._lastWidth = newWidth;
  352. return widths;
  353. };
  354. SignaturePad.prototype._strokeWidth = function (velocity) {
  355. return Math.max(this.maxWidth / (velocity + 1), this.minWidth);
  356. };
  357. SignaturePad.prototype._drawPoint = function (x, y, size) {
  358. var ctx = this._ctx;
  359. ctx.moveTo(x, y);
  360. ctx.arc(x, y, size, 0, 2 * Math.PI, false);
  361. this._isEmpty = false;
  362. };
  363. SignaturePad.prototype._drawCurve = function (curve, startWidth, endWidth) {
  364. var ctx = this._ctx;
  365. var widthDelta = endWidth - startWidth;
  366. var drawSteps = Math.floor(curve.length());
  367. ctx.beginPath();
  368. for (var i = 0; i < drawSteps; i += 1) {
  369. // Calculate the Bezier (x, y) coordinate for this step.
  370. var t = i / drawSteps;
  371. var tt = t * t;
  372. var ttt = tt * t;
  373. var u = 1 - t;
  374. var uu = u * u;
  375. var uuu = uu * u;
  376. var x = uuu * curve.startPoint.x;
  377. x += 3 * uu * t * curve.control1.x;
  378. x += 3 * u * tt * curve.control2.x;
  379. x += ttt * curve.endPoint.x;
  380. var y = uuu * curve.startPoint.y;
  381. y += 3 * uu * t * curve.control1.y;
  382. y += 3 * u * tt * curve.control2.y;
  383. y += ttt * curve.endPoint.y;
  384. var width = startWidth + ttt * widthDelta;
  385. this._drawPoint(x, y, width);
  386. }
  387. ctx.closePath();
  388. ctx.fill();
  389. };
  390. SignaturePad.prototype._drawDot = function (point) {
  391. var ctx = this._ctx;
  392. var width = typeof this.dotSize === 'function' ? this.dotSize() : this.dotSize;
  393. ctx.beginPath();
  394. this._drawPoint(point.x, point.y, width);
  395. ctx.closePath();
  396. ctx.fill();
  397. };
  398. SignaturePad.prototype._fromData = function (pointGroups, drawCurve, drawDot) {
  399. for (var i = 0; i < pointGroups.length; i += 1) {
  400. var group = pointGroups[i];
  401. if (group.length > 1) {
  402. for (var j = 0; j < group.length; j += 1) {
  403. var rawPoint = group[j];
  404. var point = new Point(rawPoint.x, rawPoint.y, rawPoint.time);
  405. var color = rawPoint.color;
  406. if (j === 0) {
  407. // First point in a group. Nothing to draw yet.
  408. // All points in the group have the same color, so it's enough to set
  409. // penColor just at the beginning.
  410. this.penColor = color;
  411. this._reset();
  412. this._addPoint(point);
  413. } else if (j !== group.length - 1) {
  414. // Middle point in a group.
  415. var _addPoint2 = this._addPoint(point),
  416. curve = _addPoint2.curve,
  417. widths = _addPoint2.widths;
  418. if (curve && widths) {
  419. drawCurve(curve, widths, color);
  420. }
  421. } else {
  422. // Last point in a group. Do nothing.
  423. }
  424. }
  425. } else {
  426. this._reset();
  427. var _rawPoint = group[0];
  428. drawDot(_rawPoint);
  429. }
  430. }
  431. };
  432. SignaturePad.prototype._toSVG = function () {
  433. var _this2 = this;
  434. var pointGroups = this._data;
  435. var canvas = this._canvas;
  436. var ratio = Math.max(window.devicePixelRatio || 1, 1);
  437. var minX = 0;
  438. var minY = 0;
  439. var maxX = canvas.width / ratio;
  440. var maxY = canvas.height / ratio;
  441. var svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
  442. svg.setAttributeNS(null, 'width', canvas.width);
  443. svg.setAttributeNS(null, 'height', canvas.height);
  444. this._fromData(pointGroups, function (curve, widths, color) {
  445. var path = document.createElement('path');
  446. // Need to check curve for NaN values, these pop up when drawing
  447. // lines on the canvas that are not continuous. E.g. Sharp corners
  448. // or stopping mid-stroke and than continuing without lifting mouse.
  449. if (!isNaN(curve.control1.x) && !isNaN(curve.control1.y) && !isNaN(curve.control2.x) && !isNaN(curve.control2.y)) {
  450. 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));
  451. path.setAttribute('d', attr);
  452. path.setAttribute('stroke-width', (widths.end * 2.25).toFixed(3));
  453. path.setAttribute('stroke', color);
  454. path.setAttribute('fill', 'none');
  455. path.setAttribute('stroke-linecap', 'round');
  456. svg.appendChild(path);
  457. }
  458. }, function (rawPoint) {
  459. var circle = document.createElement('circle');
  460. var dotSize = typeof _this2.dotSize === 'function' ? _this2.dotSize() : _this2.dotSize;
  461. circle.setAttribute('r', dotSize);
  462. circle.setAttribute('cx', rawPoint.x);
  463. circle.setAttribute('cy', rawPoint.y);
  464. circle.setAttribute('fill', rawPoint.color);
  465. svg.appendChild(circle);
  466. });
  467. var prefix = 'data:image/svg+xml;base64,';
  468. var header = '<svg' + ' xmlns="http://www.w3.org/2000/svg"' + ' xmlns:xlink="http://www.w3.org/1999/xlink"' + (' viewBox="' + minX + ' ' + minY + ' ' + maxX + ' ' + maxY + '"') + (' width="' + maxX + '"') + (' height="' + maxY + '"') + '>';
  469. var body = svg.innerHTML;
  470. // IE hack for missing innerHTML property on SVGElement
  471. if (body === undefined) {
  472. var dummy = document.createElement('dummy');
  473. var nodes = svg.childNodes;
  474. dummy.innerHTML = '';
  475. for (var i = 0; i < nodes.length; i += 1) {
  476. dummy.appendChild(nodes[i].cloneNode(true));
  477. }
  478. body = dummy.innerHTML;
  479. }
  480. var footer = '</svg>';
  481. var data = header + body + footer;
  482. return prefix + btoa(data);
  483. };
  484. SignaturePad.prototype.fromData = function (pointGroups) {
  485. var _this3 = this;
  486. this.clear();
  487. this._fromData(pointGroups, function (curve, widths) {
  488. return _this3._drawCurve(curve, widths.start, widths.end);
  489. }, function (rawPoint) {
  490. return _this3._drawDot(rawPoint);
  491. });
  492. this._data = pointGroups;
  493. };
  494. SignaturePad.prototype.toData = function () {
  495. return this._data;
  496. };
  497. return SignaturePad;
  498. })));