feat:add undo feature for signature

pr675
Hufe921 4 years ago
parent 34529feaca
commit 571182ab3f

@ -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 {

Loading…
Cancel
Save