Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: make beginStroke event cancelable #744

Merged
merged 2 commits into from
Nov 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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);
});
});
Loading