Skip to content

Commit

Permalink
fix: make beginStroke event cancelable (#744)
Browse files Browse the repository at this point in the history
* fix: make beginStroke event cancelable

* update readme
  • Loading branch information
UziTech committed Nov 16, 2023
1 parent 90b15de commit 315462e
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 24 deletions.
2 changes: 1 addition & 1 deletion README.md
Expand Up @@ -130,7 +130,7 @@ signaturePad.penColor = "rgb(66, 133, 244)";

<dl>
<dt>beginStroke</dt>
<dd>Triggered before stroke begins.</dd>
<dd>Triggered before stroke begins.<br>Can be canceled with <code>event.preventDefault()</code></dd>
<dt>endStroke</dt>
<dd>Triggered after stroke ends.</dd>
<dt>beforeUpdateStroke</dt>
Expand Down
44 changes: 25 additions & 19 deletions src/signature_pad.ts
Expand Up @@ -69,7 +69,7 @@ export default class SignaturePad extends SignatureEventTarget {
// Private stuff
/* tslint:disable: variable-name */
private _ctx: CanvasRenderingContext2D;
private _drawningStroke = false;
private _drawingStroke = false;
private _isEmpty = true;
private _lastPoints: Point[] = []; // Stores up to 4 most recent points; used to generate a new curve
private _data: PointGroup[] = []; // Stores all points in groups (one group per line or dot)
Expand Down Expand Up @@ -250,20 +250,16 @@ export default class SignaturePad extends SignatureEventTarget {
// Event handlers
private _handleMouseDown = (event: MouseEvent): void => {
if (event.buttons === 1) {
this._drawningStroke = true;
this._strokeBegin(event);
}
};

private _handleMouseMove = (event: MouseEvent): void => {
if (this._drawningStroke) {
this._strokeMoveUpdate(event);
}
this._strokeMoveUpdate(event);
};

private _handleMouseUp = (event: MouseEvent): void => {
if (event.buttons === 1 && this._drawningStroke) {
this._drawningStroke = false;
if (event.buttons === 1) {
this._strokeEnd(event);
}
};
Expand Down Expand Up @@ -302,22 +298,17 @@ export default class SignaturePad extends SignatureEventTarget {
};

private _handlePointerStart = (event: PointerEvent): void => {
this._drawningStroke = true;
event.preventDefault();
this._strokeBegin(event);
};

private _handlePointerMove = (event: PointerEvent): void => {
if (this._drawningStroke) {
event.preventDefault();
this._strokeMoveUpdate(event);
}
this._strokeMoveUpdate(event);
};

private _handlePointerEnd = (event: PointerEvent): void => {
if (this._drawningStroke) {
if (this._drawingStroke) {
event.preventDefault();
this._drawningStroke = false;
this._strokeEnd(event);
}
};
Expand All @@ -341,7 +332,13 @@ export default class SignaturePad extends SignatureEventTarget {

// Private methods
private _strokeBegin(event: SignatureEvent): void {
this.dispatchEvent(new CustomEvent('beginStroke', { detail: event }));
const cancelled = !this.dispatchEvent(
new CustomEvent('beginStroke', { detail: event, cancelable: true }),
);
if (cancelled) {
return;
}
this._drawingStroke = true;

const pointGroupOptions = this._getPointGroupOptions();

Expand All @@ -356,6 +353,10 @@ export default class SignaturePad extends SignatureEventTarget {
}

private _strokeUpdate(event: SignatureEvent): void {
if (!this._drawingStroke) {
return;
}

if (this._data.length === 0) {
// This can happen if clear() was called while a signature is still in progress,
// or if there is a race condition between start/update events.
Expand All @@ -373,8 +374,8 @@ export default class SignaturePad extends SignatureEventTarget {
(event as PointerEvent).pressure !== undefined
? (event as PointerEvent).pressure
: (event as Touch).force !== undefined
? (event as Touch).force
: 0;
? (event as Touch).force
: 0;

const point = this._createPoint(x, y, pressure);
const lastPointGroup = this._data[this._data.length - 1];
Expand Down Expand Up @@ -408,13 +409,18 @@ export default class SignaturePad extends SignatureEventTarget {
}

private _strokeEnd(event: SignatureEvent): void {
if (!this._drawingStroke) {
return;
}

this._strokeUpdate(event);

this._drawingStroke = false;
this.dispatchEvent(new CustomEvent('endStroke', { detail: event }));
}

private _handlePointerEvents(): void {
this._drawningStroke = false;
this._drawingStroke = false;

this.canvas.addEventListener('pointerdown', this._handlePointerStart);
this.canvas.addEventListener('pointermove', this._handlePointerMove);
Expand All @@ -425,7 +431,7 @@ export default class SignaturePad extends SignatureEventTarget {
}

private _handleMouseEvents(): void {
this._drawningStroke = false;
this._drawingStroke = false;

this.canvas.addEventListener('mousedown', this._handleMouseDown);
this.canvas.addEventListener('mousemove', this._handleMouseMove);
Expand Down
45 changes: 41 additions & 4 deletions tests/signature_pad.test.ts
Expand Up @@ -67,7 +67,7 @@ describe('#clear', () => {

const context = canvas.getContext('2d') as CanvasRenderingContext2D;
expect(context.globalCompositeOperation).toBe('destination-out');
})
});
});

describe('#isEmpty', () => {
Expand Down Expand Up @@ -141,9 +141,7 @@ describe('#toDataURL', () => {
const pad = new SignaturePad(canvas);
pad.fromData(face);

expect(pad.toDataURL()).toEqual(
expect.stringMatching('data:image/png'),
);
expect(pad.toDataURL()).toEqual(expect.stringMatching('data:image/png'));
});

it('returns PNG image in data URL format', () => {
Expand Down Expand Up @@ -491,4 +489,43 @@ describe('Signature events.', () => {
expect(event.detail).toBe(pointerEvent);
});
});

it(`cancel beginStroke.`, () => {
const endStroke: EventListener = jest.fn();
const cancelEvent: EventListener = jest.fn((evt: Event): void => {
evt.preventDefault();
});

signpad.addEventListener('beginStroke', cancelEvent);
signpad.addEventListener('endStroke', endStroke);

canvas.dispatchEvent(
new PointerEvent('pointerdown', {
clientX: 50,
clientY: 30,
pressure: 1,
}),
);
canvas.dispatchEvent(
new PointerEvent('pointermove', {
clientX: 50,
clientY: 40,
pressure: 1,
}),
);
document.dispatchEvent(
new PointerEvent('pointerup', {
clientX: 50,
clientY: 40,
pressure: 1,
}),
);

expect(cancelEvent).toHaveBeenCalled();
expect(endStroke).not.toHaveBeenCalled();
expect(signpad.isEmpty()).toBe(true);

signpad.removeEventListener('beginStroke', cancelEvent);
signpad.removeEventListener('endStroke', endStroke);
});
});

0 comments on commit 315462e

Please sign in to comment.