diff --git a/package-lock.json b/package-lock.json index 30a2dac..63dbcdb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@shadryx/pptk", - "version": "0.1.7", + "version": "0.1.8", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@shadryx/pptk", - "version": "0.1.7", + "version": "0.1.8", "license": "MIT", "devDependencies": { "solid-js": "^1.6.2", diff --git a/package.json b/package.json index 0a1cddf..64790e4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@shadryx/pptk", - "version": "0.1.7", + "version": "0.1.8", "description": "Pixel-Perfect ToolKit, a library to help make pixel-perfect applications on high-DPI devices", "keywords": [ "pixel-perfect", @@ -44,8 +44,7 @@ "dev:solid": "vite dev test/solid", "dev:vanilla": "vite dev test/vanilla", "build": "npm run prepare-dev && vite build && tsc && cp src/index.d.ts dist/types/index.d.ts", - "clean": "rm -rf dist/ src/index.d.ts", - "publish": "npm i && npm run build && npm publish --access public" + "clean": "rm -rf dist/ src/index.d.ts" }, "repository": { "type": "git", diff --git a/src/index.js b/src/index.js index b679394..837b000 100644 --- a/src/index.js +++ b/src/index.js @@ -358,7 +358,7 @@ export class Pannable { } /** @private **/ - _getValues(touches) { + _getValues(touches, cx = 0, cy = 0) { // First-order moments of touches.x and touches.y let mx = 0; let my = 0; @@ -375,14 +375,14 @@ export class Pannable { const n = touches.size; if (n <= 1) { - return [mx, my, 1]; + return [mx - cx, my - cy, 1]; } // Unbiased sample variance, from https://en.wikipedia.org/wiki/Bessel%27s_correction#Formula const sx = mx2 / (n - 1) - (mx * mx) / (n * (n - 1)); const sy = my2 / (n - 1) - (my * my) / (n * (n - 1)); - return [mx / n, my / n, Math.sqrt(sx + sy)]; + return [mx / n - cx, my / n - cy, Math.sqrt(sx + sy)]; } /** @returns {number} **/ @@ -409,8 +409,8 @@ export class Pannable { /** * @param {Map} touches **/ - move(touches) { - const currentValues = this._getValues(touches); + move(touches, cx = 0, cy = 0) { + const currentValues = this._getValues(touches, cx, cy); let deltaLogScale = Math.log2(currentValues[2] / this.lastValues[2]); if (Number.isNaN(deltaLogScale) || !Number.isFinite(deltaLogScale)) { deltaLogScale = 0; @@ -443,8 +443,8 @@ export class Pannable { /** * @param {Map} touches **/ - update(touches) { - this.lastValues = this._getValues(touches); + update(touches, cx = 0, cy = 0) { + this.lastValues = this._getValues(touches, cx, cy); } } @@ -794,10 +794,10 @@ export class PixelPerfectContext2D { if (y2 < y1) { this.context.moveTo(this._unscaleX(x1 + dx), this._unscaleY(y2 + dy)); - this.context.lineTo(this._unscaleX(x1 + dx), this._unscaleY(y1 - dy)); + this.context.lineTo(this._unscaleX(x1 + dx), this._unscaleY(y1 - dy) - 1); } else { this.context.moveTo(this._unscaleX(x1 + dx), this._unscaleY(y1 + dy)); - this.context.lineTo(this._unscaleX(x1 + dx), this._unscaleY(y2 - dy)); + this.context.lineTo(this._unscaleX(x1 + dx), this._unscaleY(y2 - dy) + 1); } this.context.stroke(); @@ -830,10 +830,10 @@ export class PixelPerfectContext2D { if (x2 < x1) { this.context.moveTo(this._unscaleX(x2 + dx), this._unscaleY(y1 + dy)); - this.context.lineTo(this._unscaleX(x1 - dx), this._unscaleY(y1 + dy)); + this.context.lineTo(this._unscaleX(x1 - dx) - 1, this._unscaleY(y1 + dy)); } else { this.context.moveTo(this._unscaleX(x1 + dx), this._unscaleY(y1 + dy)); - this.context.lineTo(this._unscaleX(x2 - dx), this._unscaleY(y1 + dy)); + this.context.lineTo(this._unscaleX(x2 - dx) + 1, this._unscaleY(y1 + dy)); } this.context.stroke(); diff --git a/src/solid/usePannable.ts b/src/solid/usePannable.ts index 3e56daf..a675d8f 100644 --- a/src/solid/usePannable.ts +++ b/src/solid/usePannable.ts @@ -1,26 +1,40 @@ import { createSignal } from "solid-js"; import { Pannable, PannableOptions, PannableState, Touch } from "../index.js"; -export function usePannable(config: PannableOptions) { +export type UsePannableOptions = { + center?: () => [x: number, y: number], +}; + +export function usePannable(config: PannableOptions & UsePannableOptions) { const pannable = new Pannable(config); const [state, setState] = createSignal(pannable.getState()); + const center = config.center ?? (() => [0, 0]); return { update(touches: Map) { - pannable.update(touches); + pannable.update(touches, ...center()); }, move(touches: Map) { - pannable.move(touches); + pannable.move(touches, ...center()); const newState = pannable.getState(); setState(newState); return newState; }, zoom(amount: number, clientX?: number, clientY?: number) { - pannable.zoom(amount, clientX, clientY); + const [cx, cy] = center(); + pannable.zoom(amount, (clientX ?? 0) - cx, (clientY ?? 0) - cy); const newState = pannable.getState(); setState(newState); return newState; }, - getState: state, + getState: () => { + const result = state(); + const [cx, cy] = center(); + return { + ...result, + dx: result.dx + cx, + dy: result.dy + cy, + }; + } }; }