feat:add undo feature for signature
This commit is contained in:
@@ -0,0 +1 @@
|
|||||||
|
<svg width="24" height="24" xmlns="http://www.w3.org/2000/svg"><path d="M8 6h5.5a7 7 0 010 14v-2a5 5 0 000-10H8v3L4 7l4-4v3z" fill="#3d4757" fill-rule="evenodd"/></svg>
|
||||||
|
After Width: | Height: | Size: 168 B |
@@ -16,33 +16,36 @@ export interface ISignatureOptions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class Signature {
|
export class Signature {
|
||||||
private x: number
|
private readonly MAX_RECORD_COUNT = 1000
|
||||||
private y: number
|
private undoStack: Array<Function> = []
|
||||||
private isDrawing: boolean
|
private x = 0
|
||||||
|
private y = 0
|
||||||
|
private isDrawing = false
|
||||||
|
private isDrawn = false
|
||||||
private canvasWidth: number
|
private canvasWidth: number
|
||||||
private canvasHeight: number
|
private canvasHeight: number
|
||||||
private options: ISignatureOptions
|
private options: ISignatureOptions
|
||||||
private mask: HTMLDivElement
|
private mask: HTMLDivElement
|
||||||
private container: HTMLDivElement
|
private container: HTMLDivElement
|
||||||
private trashContainer: HTMLDivElement
|
private trashContainer: HTMLDivElement
|
||||||
|
private undoContainer: HTMLDivElement
|
||||||
private canvas: HTMLCanvasElement
|
private canvas: HTMLCanvasElement
|
||||||
private ctx: CanvasRenderingContext2D
|
private ctx: CanvasRenderingContext2D
|
||||||
|
|
||||||
constructor(options: ISignatureOptions) {
|
constructor(options: ISignatureOptions) {
|
||||||
this.x = 0
|
|
||||||
this.y = 0
|
|
||||||
this.isDrawing = false
|
|
||||||
this.options = options
|
this.options = options
|
||||||
this.canvasWidth = options.width || 390
|
this.canvasWidth = options.width || 390
|
||||||
this.canvasHeight = options.height || 180
|
this.canvasHeight = options.height || 180
|
||||||
const { mask, container, trashContainer, canvas } = this._render()
|
const { mask, container, trashContainer, undoContainer, canvas } = this._render()
|
||||||
this.mask = mask
|
this.mask = mask
|
||||||
this.container = container
|
this.container = container
|
||||||
this.trashContainer = trashContainer
|
this.trashContainer = trashContainer
|
||||||
|
this.undoContainer = undoContainer
|
||||||
this.canvas = canvas
|
this.canvas = canvas
|
||||||
this.ctx = <CanvasRenderingContext2D>canvas.getContext('2d')
|
this.ctx = <CanvasRenderingContext2D>canvas.getContext('2d')
|
||||||
this.ctx.lineCap = 'round'
|
this.ctx.lineCap = 'round'
|
||||||
this._bindEvent()
|
this._bindEvent()
|
||||||
|
this._clearUndoFn()
|
||||||
}
|
}
|
||||||
|
|
||||||
private _render() {
|
private _render() {
|
||||||
@@ -79,11 +82,21 @@ export class Signature {
|
|||||||
// 操作区
|
// 操作区
|
||||||
const operationContainer = document.createElement('div')
|
const operationContainer = document.createElement('div')
|
||||||
operationContainer.classList.add('signature-operation')
|
operationContainer.classList.add('signature-operation')
|
||||||
|
// 撤销
|
||||||
|
const undoContainer = document.createElement('div')
|
||||||
|
undoContainer.classList.add('signature-operation__undo')
|
||||||
|
const undoIcon = document.createElement('i')
|
||||||
|
const undoLabel = document.createElement('span')
|
||||||
|
undoLabel.innerText = '撤销'
|
||||||
|
undoContainer.append(undoIcon)
|
||||||
|
undoContainer.append(undoLabel)
|
||||||
|
operationContainer.append(undoContainer)
|
||||||
|
// 清空画布
|
||||||
const trashContainer = document.createElement('div')
|
const trashContainer = document.createElement('div')
|
||||||
trashContainer.classList.add('signature-operation__trash')
|
trashContainer.classList.add('signature-operation__trash')
|
||||||
const trashIcon = document.createElement('i')
|
const trashIcon = document.createElement('i')
|
||||||
const trashLabel = document.createElement('span')
|
const trashLabel = document.createElement('span')
|
||||||
trashLabel.innerText = '清空画布'
|
trashLabel.innerText = '清空'
|
||||||
trashContainer.append(trashIcon)
|
trashContainer.append(trashIcon)
|
||||||
trashContainer.append(trashLabel)
|
trashContainer.append(trashLabel)
|
||||||
operationContainer.append(trashContainer)
|
operationContainer.append(trashContainer)
|
||||||
@@ -137,18 +150,44 @@ export class Signature {
|
|||||||
mask,
|
mask,
|
||||||
canvas,
|
canvas,
|
||||||
container,
|
container,
|
||||||
trashContainer
|
trashContainer,
|
||||||
|
undoContainer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _bindEvent() {
|
private _bindEvent() {
|
||||||
this.trashContainer.onclick = this._clearCanvas.bind(this)
|
this.trashContainer.onclick = this._clearCanvas.bind(this)
|
||||||
|
this.undoContainer.onclick = this._undo.bind(this)
|
||||||
this.canvas.onmousedown = this._startDraw.bind(this)
|
this.canvas.onmousedown = this._startDraw.bind(this)
|
||||||
this.canvas.onmousemove = this._draw.bind(this)
|
this.canvas.onmousemove = this._draw.bind(this)
|
||||||
this.container.onmouseup = this._stopDraw.bind(this)
|
this.container.onmouseup = this._stopDraw.bind(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _undo() {
|
||||||
|
if (this.undoStack.length > 1) {
|
||||||
|
this.undoStack.pop()
|
||||||
|
if (this.undoStack.length) {
|
||||||
|
this.undoStack[this.undoStack.length - 1]()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _saveUndoFn(fn: Function) {
|
||||||
|
this.undoStack.push(fn)
|
||||||
|
while (this.undoStack.length > this.MAX_RECORD_COUNT) {
|
||||||
|
this.undoStack.shift()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _clearUndoFn() {
|
||||||
|
const clearFn = () => {
|
||||||
|
this.ctx.clearRect(0, 0, this.canvasWidth, this.canvasHeight)
|
||||||
|
}
|
||||||
|
this.undoStack = [clearFn]
|
||||||
|
}
|
||||||
|
|
||||||
private _clearCanvas() {
|
private _clearCanvas() {
|
||||||
|
this._clearUndoFn()
|
||||||
this.ctx.clearRect(0, 0, this.canvasWidth, this.canvasHeight)
|
this.ctx.clearRect(0, 0, this.canvasWidth, this.canvasHeight)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -168,10 +207,20 @@ export class Signature {
|
|||||||
this.ctx.stroke()
|
this.ctx.stroke()
|
||||||
this.x = offsetX
|
this.x = offsetX
|
||||||
this.y = offsetY
|
this.y = offsetY
|
||||||
|
this.isDrawn = true
|
||||||
}
|
}
|
||||||
|
|
||||||
private _stopDraw() {
|
private _stopDraw() {
|
||||||
this.isDrawing = false
|
this.isDrawing = false
|
||||||
|
if (this.isDrawn) {
|
||||||
|
const imageData = this.ctx.getImageData(0, 0, this.canvasWidth, this.canvasHeight)
|
||||||
|
const self = this
|
||||||
|
this._saveUndoFn(function () {
|
||||||
|
self.ctx.clearRect(0, 0, self.canvasWidth, self.canvasHeight)
|
||||||
|
self.ctx.putImageData(imageData, 0, 0)
|
||||||
|
})
|
||||||
|
this.isDrawn = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _toDataURL() {
|
private _toDataURL() {
|
||||||
|
|||||||
@@ -50,7 +50,7 @@
|
|||||||
background: url(../../assets/images/close.svg);
|
background: url(../../assets/images/close.svg);
|
||||||
}
|
}
|
||||||
|
|
||||||
.signature-operation__trash {
|
.signature-operation>div {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -58,30 +58,36 @@
|
|||||||
user-select: none;
|
user-select: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.signature-operation__trash:hover {
|
.signature-operation>div:hover {
|
||||||
color: #6e7175;
|
color: #6e7175;
|
||||||
}
|
}
|
||||||
|
|
||||||
.signature-operation__trash i {
|
.signature-operation>div i {
|
||||||
width: 24px;
|
width: 24px;
|
||||||
height: 24px;
|
height: 24px;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.signature-operation__undo {
|
||||||
|
background: url(../../assets/images/signature-undo.svg) no-repeat;
|
||||||
|
}
|
||||||
|
|
||||||
|
.signature-operation__trash {
|
||||||
background: url(../../assets/images/trash.svg) no-repeat;
|
background: url(../../assets/images/trash.svg) no-repeat;
|
||||||
}
|
}
|
||||||
|
|
||||||
.signature-operation__trash span {
|
.signature-operation>div span {
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
margin-left: 5px;
|
margin: 0 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.signature-canvas {
|
.signature-canvas {
|
||||||
margin: 15px 0;
|
margin: 15px 0;
|
||||||
border: 1px solid #e9e9e9;
|
|
||||||
user-select: none;
|
user-select: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.signature-canvas canvas {
|
.signature-canvas canvas {
|
||||||
background: #fbfbfb;
|
background: #f3f5f7;
|
||||||
}
|
}
|
||||||
|
|
||||||
.signature-menu {
|
.signature-menu {
|
||||||
|
|||||||
Reference in New Issue
Block a user