Skip to content

Commit

Permalink
working mouse events
Browse files Browse the repository at this point in the history
  • Loading branch information
tshaddix committed Nov 16, 2023
1 parent 25125bb commit c1b7fb0
Show file tree
Hide file tree
Showing 14 changed files with 566 additions and 325 deletions.
346 changes: 201 additions & 145 deletions dist/index.es.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/index.es.js.map

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/index.umd.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/index.umd.js.map

Large diffs are not rendered by default.

352 changes: 204 additions & 148 deletions example/build/example.es.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion example/build/example.es.js.map

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion example/build/example.umd.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion example/build/example.umd.js.map

Large diffs are not rendered by default.

21 changes: 12 additions & 9 deletions src/Manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import { StrokeManager } from "./StrokeManager";
export class Manager {
// reference to the canvas
private canvas: HTMLCanvasElement;
// reference to canvas rendering context
private ctx: CanvasRenderingContext2D | null;
// reference to stroke manager
private strokeManager: StrokeManager;
// holds the pixel ratio between canvas backing
Expand Down Expand Up @@ -32,6 +34,7 @@ export class Manager {
canvasHeight: number,
) {
this.canvas = canvas;
this.ctx = canvas.getContext("2d", { willReadFrequently: true });
this.canvasWidth = canvasWidth;
this.canvasHeight = canvasHeight;
this.currentTool = null;
Expand All @@ -42,7 +45,7 @@ export class Manager {
this.shouldCommit = false;

// find pixel ratio relative to backing store and device ratio
const bsr = (canvas.getContext("2d") as any).backingStorePixelRatio || 1;
const bsr = (this.ctx as any).backingStorePixelRatio || 1;
const dpr = window.devicePixelRatio || 1;
this.pixelRatio = dpr / bsr;

Expand Down Expand Up @@ -71,16 +74,16 @@ export class Manager {
this.canvasWidth = width;
this.canvasHeight = height;

const { canvas, canvasWidth, canvasHeight, pixelRatio } = this;
const { ctx, canvas, canvasWidth, canvasHeight, pixelRatio } = this;

const ctx = canvas.getContext("2d");
if (!ctx) return;

// appropriately scale canvas to map to device ratio
canvas.width = canvasWidth * pixelRatio;
canvas.height = canvasHeight * pixelRatio;
canvas.style.width = canvasWidth + "px";
canvas.style.height = canvasHeight + "px";
ctx.setTransform(pixelRatio, 0, 0, pixelRatio, 0, 0);
ctx.scale(pixelRatio, pixelRatio);
}

/**
Expand Down Expand Up @@ -133,9 +136,9 @@ export class Manager {
// schedule next draw
this.nextAnimationFrame = window.requestAnimationFrame(this.draw);

const ctx = this.canvas.getContext("2d");
const { ctx, canvasWidth, canvasHeight } = this;

if (!this.shouldDraw) {
if (!this.shouldDraw || !ctx) {
return;
}

Expand All @@ -151,7 +154,7 @@ export class Manager {
// pending strokes, draw them
if (this.currentTool && this.currentStroke.length) {
ctx.save();
this.currentTool.draw(ctx, this.currentStroke);
this.currentTool.draw(ctx, this.currentStroke, canvasWidth, canvasHeight);
ctx.restore();
}

Expand All @@ -161,8 +164,8 @@ export class Manager {
this.canvasState = ctx.getImageData(
0,
0,
this.canvasWidth * this.pixelRatio,
this.canvasHeight * this.pixelRatio,
canvasWidth * this.pixelRatio,
canvasHeight * this.pixelRatio,
);
this.currentStroke = [];
this.shouldCommit = false;
Expand Down
99 changes: 97 additions & 2 deletions src/StrokeManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ export class StrokeManager {
private canvas: HTMLCanvasElement;
// holds last touch point in a drag
private lastTouch: ITouch | null;
// holds last mouse point in drag
private lastMouse: IPoint | null;
// value indicates how sensitive the stroke detection is higher is better
private sensitivity: number;
// holds all of the last emitted stroke parts in a drag
Expand All @@ -25,6 +27,7 @@ export class StrokeManager {
constructor(canvas: HTMLCanvasElement) {
this.canvas = canvas;
this.lastTouch = null;
this.lastMouse = null;
this.sensitivity = 20.0;
this.lastStrokeParts = [];
this.onStrokePartHandlers = [];
Expand All @@ -35,6 +38,9 @@ export class StrokeManager {
this.onTouchMove = this.onTouchMove.bind(this);
this.destroy = this.destroy.bind(this);
this.getRelativePosition = this.getRelativePosition.bind(this);
this.onMouseDown = this.onMouseDown.bind(this);
this.onMouseUp = this.onMouseUp.bind(this);
this.onMouseMove = this.onMouseMove.bind(this);

this.canvas.addEventListener("touchstart", this.onTouchStart, {
passive: false,
Expand All @@ -48,6 +54,15 @@ export class StrokeManager {
this.canvas.addEventListener("touchmove", this.onTouchMove, {
passive: false,
});
this.canvas.addEventListener("mousedown", this.onMouseDown, {
passive: false,
});
document.addEventListener("mouseup", this.onMouseUp, {
passive: false,
});
this.canvas.addEventListener("mousemove", this.onMouseMove, {
passive: false,
});
}

/**
Expand All @@ -68,6 +83,9 @@ export class StrokeManager {
this.canvas.removeEventListener("touchend", this.onTouchEnd);
this.canvas.removeEventListener("touchcancel", this.onTouchCancel);
this.canvas.removeEventListener("touchmove", this.onTouchMove);
this.canvas.removeEventListener("mousedown", this.onMouseDown);
document.removeEventListener("mouseup", this.onMouseUp);
this.canvas.removeEventListener("mousemove", this.onMouseMove);
}

/**
Expand All @@ -80,8 +98,8 @@ export class StrokeManager {
const rect = this.canvas.getBoundingClientRect();

return {
x: clientX - rect.left,
y: clientY - rect.top,
x: (clientX - rect.left) / rect.width,
y: (clientY - rect.top) / rect.height,
};
}

Expand Down Expand Up @@ -220,4 +238,81 @@ export class StrokeManager {
this.lastTouch = null;
this.lastStrokeParts = [];
}

private onMouseDown(e: MouseEvent): void {
// if there is an ongoing drag, ignore this event
if (this.lastMouse) {
return;
}

// save the drag
this.lastMouse = this.getRelativePosition(e.clientX, e.clientY);
}

private onMouseUp(e: MouseEvent): void {
// if no last mouse... something is wrong
if (!this.lastMouse) {
return;
}

let endPoint = this.getRelativePosition(e.clientX, e.clientY);

const d = getEuclidean(this.lastMouse, endPoint);

// if line is less than 1 pixel length, generate a fake line
if (d < 1) {
endPoint = {
x: this.lastMouse.x + 0.0008,
y: this.lastMouse.y + 0.0008,
};
}

const strokePart: IStrokePart = {
startPoint: this.lastMouse,
endPoint: endPoint,
isStart: false,
isEnd: true,
};

this.onStrokePartHandlers.forEach((handler) => {
handler(strokePart);
});

this.lastMouse = null;
this.lastStrokeParts = [];
}

private onMouseMove(e: MouseEvent): void {
// if no last drag... something is wrong
if (!this.lastMouse) {
return;
}

const nextMouse = this.getRelativePosition(e.clientX, e.clientY);

// If sensitivity setting has been set,
// check if this point is far enough from last
// drag to be drawn
if (
this.sensitivity &&
getEuclidean(nextMouse, this.lastMouse) < 0.05 / this.sensitivity
) {
return;
}

const strokePart: IStrokePart = {
endPoint: nextMouse,
startPoint: this.lastMouse,
isStart: this.lastStrokeParts.length === 0,
isEnd: false,
};

this.onStrokePartHandlers.forEach((handler) => {
handler(strokePart);
});

// save this drag as last drag
this.lastMouse = nextMouse;
this.lastStrokeParts.push(strokePart);
}
}
26 changes: 18 additions & 8 deletions src/tools/EraserTool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,17 @@ export class EraserTool {
* @param ctx
* @param strokeParts
*/
public draw(ctx: CanvasRenderingContext2D, strokeParts: IStrokePart[]): void {
public draw(
ctx: CanvasRenderingContext2D,
strokeParts: IStrokePart[],
canvasWidth: number,
canvasHeight: number,
): void {
const { handleOpts } = this;
const halfWidth = this.width / 2.0;

strokeParts.forEach((strokePart) => {
const { startPoint, endPoint, isEnd } = strokePart;
const { startPoint, endPoint } = strokePart;

const length = getEuclidean(startPoint, endPoint);
const unitVect: IPoint = getUnitVector(startPoint, endPoint);
Expand All @@ -45,8 +50,8 @@ export class EraserTool {
// clear all the way along the drag
while (i < length) {
const nextPoint: IPoint = {
x: currentPoint.x + unitVect.x,
y: currentPoint.y + unitVect.y,
x: currentPoint.x * canvasWidth + unitVect.x,
y: currentPoint.y * canvasHeight + unitVect.y,
};

ctx.clearRect(
Expand All @@ -69,16 +74,21 @@ export class EraserTool {
ctx.strokeStyle = handleOpts.strokeColor;
ctx.fillStyle = handleOpts.fillColor;

const lastEndPoint = {
x: lastPart.endPoint.x * canvasWidth,
y: lastPart.endPoint.y * canvasHeight,
};

ctx.fillRect(
lastPart.endPoint.x - halfWidth,
lastPart.endPoint.y - halfWidth,
lastEndPoint.x - halfWidth,
lastEndPoint.y - halfWidth,
this.width,
this.width,
);

ctx.strokeRect(
lastPart.endPoint.x - halfWidth + 0.5,
lastPart.endPoint.y - halfWidth + 0.5,
lastEndPoint.x - halfWidth + 0.5,
lastEndPoint.y - halfWidth + 0.5,
this.width - 1,
this.width - 1,
);
Expand Down
14 changes: 11 additions & 3 deletions src/tools/HighlighterTool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,12 @@ export class HighlighterTool {
* @param ctx
* @param strokeParts
*/
public draw(ctx: CanvasRenderingContext2D, strokeParts: IStrokePart[]): void {
public draw(
ctx: CanvasRenderingContext2D,
strokeParts: IStrokePart[],
canvasWidth: number,
canvasHeight: number,
): void {
const firstPart = strokeParts[0];

ctx.beginPath();
Expand All @@ -32,11 +37,14 @@ export class HighlighterTool {
ctx.lineCap = "butt";
ctx.miterLimit = 1;

ctx.moveTo(firstPart.startPoint.x, firstPart.startPoint.y);
ctx.moveTo(
firstPart.startPoint.x * canvasWidth,
firstPart.startPoint.y * canvasHeight,
);

strokeParts.forEach((strokePart) => {
const { endPoint } = strokePart;
ctx.lineTo(endPoint.x, endPoint.y);
ctx.lineTo(endPoint.x * canvasWidth, endPoint.y * canvasHeight);
});

ctx.stroke();
Expand Down
14 changes: 11 additions & 3 deletions src/tools/PenTool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,12 @@ export class PenTool {
* @param ctx
* @param strokeParts
*/
public draw(ctx: CanvasRenderingContext2D, strokeParts: IStrokePart[]): void {
public draw(
ctx: CanvasRenderingContext2D,
strokeParts: IStrokePart[],
canvasWidth: number,
canvasHeight: number,
): void {
const firstPart = strokeParts[0];

ctx.beginPath();
Expand All @@ -24,11 +29,14 @@ export class PenTool {
ctx.lineCap = "round";
ctx.lineJoin = "round";

ctx.moveTo(firstPart.startPoint.x, firstPart.startPoint.y);
ctx.moveTo(
firstPart.startPoint.x * canvasWidth,
firstPart.startPoint.y * canvasHeight,
);

strokeParts.forEach((strokePart) => {
const { endPoint } = strokePart;
ctx.lineTo(endPoint.x, endPoint.y);
ctx.lineTo(endPoint.x * canvasWidth, endPoint.y * canvasHeight);
});

ctx.stroke();
Expand Down
7 changes: 6 additions & 1 deletion src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,10 @@ export interface ILine {
}

export interface ITool {
draw(ctx: CanvasRenderingContext2D, strokeParts: IStrokePart[]): void;
draw(
ctx: CanvasRenderingContext2D,
strokeParts: IStrokePart[],
canvasWidth: number,
canvasHeight: number,
): void;
}

0 comments on commit c1b7fb0

Please sign in to comment.