Skip to content

Commit

Permalink
Fix pointer offset and movement with high dpr (#14)
Browse files Browse the repository at this point in the history
* Fix offset only when broken;
Fix movement when offset is broken

* Fixed tests

* 0.4.4
  • Loading branch information
ydaniv authored Feb 11, 2025
1 parent 06fb5d6 commit 92d025b
Show file tree
Hide file tree
Showing 8 changed files with 1,057 additions and 19 deletions.
39 changes: 32 additions & 7 deletions dist/index.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,31 @@ function getRect (element) {
};
}

/**
* @see https://issues.chromium.org/issues/40887601?pli=1
*/
function testPointerOffsetDprBug () {
const DPR = window.devicePixelRatio;
let fixRequired = false;

if (DPR === 1) {
// we can immediately return here, not required to fix
return false;
}

document.body.addEventListener('pointerdown', (e) => {
fixRequired = e.offsetX !== 10;
}, { once: true });

const event = new PointerEvent('pointerdown', {
clientX: 10
});

document.body.dispatchEvent(event);

return fixRequired;
}

let listeners = 0;
const pointers = new Set();

Expand Down Expand Up @@ -380,13 +405,16 @@ class Pointer {
this.previousProgress = { ...this.progress };
this.currentProgress = null;

const shouldFixSynthPointer = testPointerOffsetDprBug();
const DPR = shouldFixSynthPointer ? window.devicePixelRatio : 1;

const _measure = (event) => {
Object.assign(this.previousProgress, this.currentProgress || this.progress);

this.progress.x = this.config.root ? event.offsetX : event.x;
this.progress.y = this.config.root ? event.offsetY : event.y;
this.progress.vx = event.movementX;
this.progress.vy = event.movementY;
this.progress.vx = event.movementX * DPR;
this.progress.vy = event.movementY * DPR;
this._nextTick = trigger();
};

Expand All @@ -399,18 +427,15 @@ class Pointer {
this.progress.active = true;
this._nextTick = trigger();
};
const dpr = window.devicePixelRatio;

if (this.config.root) {
this._measure = (e) => {
if (e.target !== this.config.root) {
const event = new PointerEvent('pointermove', {
bubbles: true,
cancelable: true,
clientX: e.x * dpr,
clientY: e.y * dpr,
movementX: e.movementX,
movementY: e.movementY,
clientX: e.x * DPR,
clientY: e.y * DPR,
});

e.stopPropagation();
Expand Down
117 changes: 117 additions & 0 deletions docs/demo/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Kuliso | Tiny library for performant pointer-driven effects</title>
<style>
body {
margin: 0;
padding: 0;
}

main {
display: flex;
position: relative;
justify-content: center;
align-items: center;
height: 100vh;
background-color: #f0f0f0;
}

section {
width: 80%;
height: 80%;
background-color: #fff;
box-shadow: 0 0 10px rgb(0 0 0 / 10%);
}

#target {
box-sizing: border-box;
width: 40px;
margin: -20px 0 0 -20px;
aspect-ratio: 1;
border: 10px solid #007bff;
border-radius: 50%;
box-shadow:
0 0 6px rgb(0 0 0 / 50%),
inset 0 0 4px rgb(0 0 0 / 50%);
pointer-events: none;
}

#cover {
position: absolute;
top: 0;
left: 25%;
width: 50%;
height: 100%;
background-color: rgba(0, 123, 255, 0.1);
}

aside {
position: fixed;
top: 0;
right: 0;
padding: 10px;
background-color: #fff;
box-shadow: 0 0 10px rgb(0 0 0 / 10%);
}
</style>
</head>
<body>
<main>
<aside>
<pre id="x"></pre>
<pre id="y"></pre>
<pre id="vx"></pre> <pre id="vxmax"></pre>
<pre id="vy"></pre> <pre id="vymax"></pre>
</aside>
<section id="root">
<div id="target"></div>
<div id="cover"></div>
</section>
</main>
<script type="module">
import { Pointer } from './index.js';

const root = document.getElementById('root');
const target = document.getElementById('target');

const x = document.getElementById('x');
const y = document.getElementById('y');
const vx = document.getElementById('vx');
const vxmax = document.getElementById('vxmax');
const vy = document.getElementById('vy');
const vymax = document.getElementById('vymax');

let vxmaxValue = 0;
let vymaxValue = 0;

function log (p, v) {
x.innerText = `X: ${p.x}`;
y.innerText = `Y: ${p.y}`;
vx.innerText = `Vx: ${v.x}`;
vy.innerText = `Vy: ${v.y}`;
vxmaxValue = Math.max(vxmaxValue, v.x);
vymaxValue = Math.max(vymaxValue, v.y);
vxmax.innerText = `Vx Max: ${vxmaxValue}`;
vymax.innerText = `Vy Max: ${vymaxValue}`;
}

const WIDTH = root.clientWidth;
const HEIGHT = root.clientHeight;
const pointer = new Pointer({
root,
scenes: [{
target,
// centeredToTarget: true,
effect: (scene, p, v) => {
log(p, v);
target.style.transform = `translate(${p.x * WIDTH}px, ${p.y * HEIGHT}px)`;
}
}]
});

pointer.start();
</script>
</body>
</html>
Loading

0 comments on commit 92d025b

Please sign in to comment.