271 lines
9.4 KiB
JavaScript
271 lines
9.4 KiB
JavaScript
"use strict";
|
|
(function (root, factory) {
|
|
if (typeof exports === "object" && typeof module !== "undefined") {
|
|
module.exports = factory()
|
|
} else if (typeof define === "function" && define.amd) {
|
|
define(factory)
|
|
} else {
|
|
root = root || self
|
|
root.Signature = factory()
|
|
}
|
|
})(typeof self !== "undefined" ? self : this, function () {
|
|
function isHexColor(color) {
|
|
return typeof color === "string" && /^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/.test(color)
|
|
}
|
|
|
|
function Signature(options) {
|
|
var self = this
|
|
var history = []
|
|
var drawing = false
|
|
var currentPoint = null
|
|
var activePointerId = null
|
|
var canvas
|
|
var context
|
|
|
|
self.obj = options && !Array.isArray(options) ? options : {}
|
|
self.option = {
|
|
el: self.obj.el ? self.obj.el : "body",
|
|
lineWidth: !isNaN(parseInt(self.obj.lineWidth, 10)) ? parseInt(self.obj.lineWidth, 10) : 5,
|
|
lineColor: isHexColor(self.obj.lineColor) ? self.obj.lineColor : "#000000",
|
|
overflow: typeof self.obj.overflow === "boolean" ? self.obj.overflow : true,
|
|
background: isHexColor(self.obj.background) ? self.obj.background : "#ffffff"
|
|
}
|
|
self.status = history
|
|
self.step = -1
|
|
self.firstStatus = ""
|
|
|
|
canvas = document.querySelector(self.option.el)
|
|
if (!canvas || canvas.tagName !== "CANVAS") {
|
|
throw new Error("Target element must be canvas")
|
|
}
|
|
|
|
context = canvas.getContext("2d")
|
|
canvas.style.backgroundColor = self.option.background
|
|
canvas.style.touchAction = "none"
|
|
|
|
function syncHistoryIndex() {
|
|
self.step = history.length - 1
|
|
}
|
|
|
|
function clearCanvas() {
|
|
context.clearRect(0, 0, canvas.width, canvas.height)
|
|
}
|
|
|
|
function drawImage(dataUrl) {
|
|
clearCanvas()
|
|
if (!dataUrl) {
|
|
return
|
|
}
|
|
var image = new Image()
|
|
image.onload = function () {
|
|
context.drawImage(image, 0, 0)
|
|
}
|
|
image.src = dataUrl
|
|
}
|
|
|
|
function saveSnapshot() {
|
|
var dataUrl = canvas.toDataURL("image/png")
|
|
if (history.length === 0 || history[history.length - 1] !== dataUrl) {
|
|
history.push(dataUrl)
|
|
syncHistoryIndex()
|
|
}
|
|
}
|
|
|
|
function getPoint(event) {
|
|
var source = event
|
|
var rect = canvas.getBoundingClientRect()
|
|
if (event.touches && event.touches.length > 0) {
|
|
source = event.touches[0]
|
|
} else if (event.changedTouches && event.changedTouches.length > 0) {
|
|
source = event.changedTouches[0]
|
|
}
|
|
return {
|
|
x: source.clientX - rect.left,
|
|
y: source.clientY - rect.top
|
|
}
|
|
}
|
|
|
|
function preventDefault(event) {
|
|
if (event && event.cancelable) {
|
|
event.preventDefault()
|
|
}
|
|
}
|
|
|
|
function startDraw(event) {
|
|
preventDefault(event)
|
|
drawing = true
|
|
currentPoint = getPoint(event)
|
|
}
|
|
|
|
function moveDraw(event) {
|
|
if (!drawing || !currentPoint) {
|
|
return
|
|
}
|
|
preventDefault(event)
|
|
var nextPoint = getPoint(event)
|
|
context.beginPath()
|
|
context.moveTo(currentPoint.x, currentPoint.y)
|
|
context.lineTo(nextPoint.x, nextPoint.y)
|
|
context.closePath()
|
|
context.stroke()
|
|
currentPoint = nextPoint
|
|
}
|
|
|
|
function endDraw(event) {
|
|
if (!drawing) {
|
|
return
|
|
}
|
|
preventDefault(event)
|
|
drawing = false
|
|
currentPoint = null
|
|
activePointerId = null
|
|
saveSnapshot()
|
|
}
|
|
|
|
// Prefer capability detection so Harmony / modern WebView devices do not rely on UA strings.
|
|
function bindEvents() {
|
|
if (window.PointerEvent) {
|
|
canvas.addEventListener("pointerdown", function (event) {
|
|
activePointerId = event.pointerId
|
|
if (canvas.setPointerCapture) {
|
|
canvas.setPointerCapture(event.pointerId)
|
|
}
|
|
startDraw(event)
|
|
}, { passive: false })
|
|
canvas.addEventListener("pointermove", function (event) {
|
|
if (activePointerId !== null && event.pointerId !== activePointerId) {
|
|
return
|
|
}
|
|
moveDraw(event)
|
|
}, { passive: false })
|
|
canvas.addEventListener("pointerup", endDraw, { passive: false })
|
|
canvas.addEventListener("pointercancel", endDraw, { passive: false })
|
|
return
|
|
}
|
|
|
|
if ("ontouchstart" in window || navigator.maxTouchPoints > 0 || navigator.msMaxTouchPoints > 0) {
|
|
canvas.addEventListener("touchstart", startDraw, { passive: false })
|
|
canvas.addEventListener("touchmove", moveDraw, { passive: false })
|
|
canvas.addEventListener("touchend", endDraw, { passive: false })
|
|
canvas.addEventListener("touchcancel", endDraw, { passive: false })
|
|
return
|
|
}
|
|
|
|
canvas.addEventListener("mousedown", startDraw, false)
|
|
canvas.addEventListener("mousemove", moveDraw, false)
|
|
canvas.addEventListener("mouseup", endDraw, false)
|
|
canvas.addEventListener("mouseleave", endDraw, false)
|
|
}
|
|
|
|
self.create = function (option) {
|
|
var currentOption = option || self.option
|
|
context.lineWidth = currentOption.lineWidth
|
|
context.lineCap = "round"
|
|
context.lineJoin = "round"
|
|
context.strokeStyle = currentOption.lineColor
|
|
if (currentOption.overflow === true && document.body) {
|
|
document.body.style.overflow = "hidden"
|
|
}
|
|
self.firstStatus = canvas.toDataURL("image/png")
|
|
history.length = 0
|
|
history.push(self.firstStatus)
|
|
syncHistoryIndex()
|
|
bindEvents()
|
|
}
|
|
|
|
self.getContentBase64 = function (rotate, callback) {
|
|
if (!rotate) {
|
|
var dataUrl = canvas.toDataURL("image/png")
|
|
if (callback) {
|
|
callback(dataUrl)
|
|
}
|
|
return dataUrl
|
|
}
|
|
|
|
var tempCanvas = document.createElement("canvas")
|
|
tempCanvas.width = canvas.offsetHeight || canvas.height
|
|
tempCanvas.height = canvas.offsetWidth || canvas.width
|
|
var tempContext = tempCanvas.getContext("2d")
|
|
var image = new Image()
|
|
image.onload = function () {
|
|
var centerX = Math.round(tempCanvas.width / 2)
|
|
var centerY = Math.round(tempCanvas.height / 2)
|
|
tempContext.save()
|
|
tempContext.translate(centerX, centerY)
|
|
tempContext.rotate(-90 * Math.PI / 180)
|
|
tempContext.drawImage(image, -image.width / 2, -image.height / 2)
|
|
tempContext.restore()
|
|
if (callback) {
|
|
callback(tempCanvas.toDataURL("image/png"))
|
|
}
|
|
}
|
|
image.src = self.getContentBase64(false)
|
|
}
|
|
|
|
self.getBlob = function (dataUrl) {
|
|
var content = dataUrl || self.getContentBase64()
|
|
var parts = content.split(",")
|
|
var mime = parts[0].match(/:(.*?);/)[1]
|
|
var binary = atob(parts[1])
|
|
var length = binary.length
|
|
var bytes = new Uint8Array(length)
|
|
while (length--) {
|
|
bytes[length] = binary.charCodeAt(length)
|
|
}
|
|
return new Blob([bytes], { type: mime })
|
|
}
|
|
|
|
self.getFile = function (fileName, dataUrl) {
|
|
var content = dataUrl || self.getContentBase64()
|
|
var name = fileName || "file"
|
|
var parts = content.split(",")
|
|
var mime = parts[0].match(/:(.*?);/)[1]
|
|
var suffix = mime.split("/")[1]
|
|
var binary = atob(parts[1])
|
|
var length = binary.length
|
|
var bytes = new Uint8Array(length)
|
|
while (length--) {
|
|
bytes[length] = binary.charCodeAt(length)
|
|
}
|
|
return new File([bytes], name + "." + suffix, { type: mime })
|
|
}
|
|
|
|
self.reset = function () {
|
|
if (!self.firstStatus) {
|
|
return false
|
|
}
|
|
drawImage(self.firstStatus)
|
|
history.length = 0
|
|
history.push(self.firstStatus)
|
|
syncHistoryIndex()
|
|
return true
|
|
}
|
|
|
|
self.undo = function (callback) {
|
|
if (history.length <= 1 || self.step <= 0) {
|
|
console.warn("no more undo")
|
|
if (callback) {
|
|
callback()
|
|
}
|
|
return
|
|
}
|
|
self.step -= 1
|
|
drawImage(history[self.step])
|
|
}
|
|
|
|
self.redo = function (callback) {
|
|
if (self.step >= history.length - 1) {
|
|
console.warn("no more redo")
|
|
if (callback) {
|
|
callback()
|
|
}
|
|
return
|
|
}
|
|
self.step += 1
|
|
drawImage(history[self.step])
|
|
}
|
|
}
|
|
|
|
return Signature
|
|
});
|