Browse Source

fix mouse

the mouse code only handled a full page canvas.
That's now fixed.
Gregg Tavares 6 years ago
parent
commit
2d3cfcf246

+ 34 - 8
threejs/lessons/threejs-indexed-textures.md

@@ -159,13 +159,21 @@ Now we can use that to pick countries.
 ```js
 ```js
 const pickHelper = new GPUPickHelper();
 const pickHelper = new GPUPickHelper();
 
 
+function getCanvasRelativePosition(event) {
+  const rect = canvas.getBoundingClientRect();
+  return {
+    x: event.clientX - rect.left,
+    y: event.clientY - rect.top,
+  };
+}
+
 function pickCountry(event) {
 function pickCountry(event) {
   // exit if we have not loaded the data yet
   // exit if we have not loaded the data yet
   if (!countryInfos) {
   if (!countryInfos) {
     return;
     return;
   }
   }
 
 
-  const position = {x: event.clientX, y: event.clientY};
+  const position = getCanvasRelativePosition(event);
   const id = pickHelper.pick(position, pickingScene, camera);
   const id = pickHelper.pick(position, pickingScene, camera);
   if (id > 0) {
   if (id > 0) {
     // we clicked a country. Toggle its 'selected' property
     // we clicked a country. Toggle its 'selected' property
@@ -527,13 +535,21 @@ Now let's use those functions to update the palette when a country
 is selected
 is selected
 
 
 ```js
 ```js
+function getCanvasRelativePosition(event) {
+  const rect = canvas.getBoundingClientRect();
+  return {
+    x: event.clientX - rect.left,
+    y: event.clientY - rect.top,
+  };
+}
+
 function pickCountry(event) {
 function pickCountry(event) {
   // exit if we have not loaded the data yet
   // exit if we have not loaded the data yet
   if (!countryInfos) {
   if (!countryInfos) {
     return;
     return;
   }
   }
 
 
-  const position = {x: event.clientX, y: event.clientY};
+  const position = getCanvasRelativePosition(event);
   const id = pickHelper.pick(position, pickingScene, camera);
   const id = pickHelper.pick(position, pickingScene, camera);
   if (id > 0) {
   if (id > 0) {
     const countryInfo = countryInfos[id - 1];
     const countryInfo = countryInfos[id - 1];
@@ -582,12 +598,22 @@ to drag the globe.
 +const maxMoveDeltaSq = 5 * 5;
 +const maxMoveDeltaSq = 5 * 5;
 +const startPosition = {};
 +const startPosition = {};
 +let startTimeMs;
 +let startTimeMs;
++
 +function recordStartTimeAndPosition(event) {
 +function recordStartTimeAndPosition(event) {
 +  startTimeMs = performance.now();
 +  startTimeMs = performance.now();
-+  startPosition.x = event.clientX;
-+  startPosition.y = event.clientY;
++  const pos = getCanvasRelativePosition(event);
++  startPosition.x = pos.x;
++  startPosition.y = pos.y;
 +}
 +}
 
 
+function getCanvasRelativePosition(event) {
+  const rect = canvas.getBoundingClientRect();
+  return {
+    x: event.clientX - rect.left,
+    y: event.clientY - rect.top,
+  };
+}
+
 function pickCountry(event) {
 function pickCountry(event) {
   // exit if we have not loaded the data yet
   // exit if we have not loaded the data yet
   if (!countryInfos) {
   if (!countryInfos) {
@@ -602,14 +628,14 @@ function pickCountry(event) {
 +  }
 +  }
 +
 +
 +  // if they moved assume it was a drag action
 +  // if they moved assume it was a drag action
-+  const moveDeltaSq = (startPosition.x - event.clientX) ** 2 +
-+                      (startPosition.y - event.clientY) ** 2;
++  const position = getCanvasRelativePosition(event);
++  const moveDeltaSq = (startPosition.x - position.x) ** 2 +
++                      (startPosition.y - position.y) ** 2;
 +  if (moveDeltaSq > maxMoveDeltaSq) {
 +  if (moveDeltaSq > maxMoveDeltaSq) {
 +    return;
 +    return;
 +  }
 +  }
 
 
-
-  const position = {x: event.clientX, y: event.clientY};
+-  const position = {x: event.clientX, y: event.clientY};
   const id = pickHelper.pick(position, pickingScene, camera);
   const id = pickHelper.pick(position, pickingScene, camera);
   if (id > 0) {
   if (id > 0) {
     const countryInfo = countryInfos[id - 1];
     const countryInfo = countryInfos[id - 1];

+ 51 - 14
threejs/lessons/threejs-offscreencanvas.md

@@ -22,7 +22,7 @@ copy all the JavaScript from [the responsive example](threejs-responsive.html) i
 make the changes needed for it to run in a worker.
 make the changes needed for it to run in a worker.
 
 
 We still need some JavaScript in our HTML file. The first thing
 We still need some JavaScript in our HTML file. The first thing
-we do there is look up the canvas and then transfer control of that
+we need to do there is look up the canvas and then transfer control of that
 canvas to be offscreen by calling `canvas.transferControlToOffscreen`.
 canvas to be offscreen by calling `canvas.transferControlToOffscreen`.
 
 
 ```js
 ```js
@@ -34,7 +34,7 @@ function main() {
 ```
 ```
 
 
 We can then start our worker with `new Worker(pathToScript)`.
 We can then start our worker with `new Worker(pathToScript)`.
-We then pass the `offscreen` object to the worker.
+and pass the `offscreen` object to it.
 
 
 ```js
 ```js
 function main() {
 function main() {
@@ -270,7 +270,7 @@ we will now have 3 files
 
 
    `threejs-offscreencanvas-w-fallback.html`
    `threejs-offscreencanvas-w-fallback.html`
 
 
-2. a JavaScript the contains our three.js code.
+2. a JavaScript that contains our three.js code.
 
 
    `shared-cubes.js`
    `shared-cubes.js`
 
 
@@ -456,9 +456,18 @@ const pickHelper = new PickHelper();
 We updated `pickPosition` from the mouse like this
 We updated `pickPosition` from the mouse like this
 
 
 ```js
 ```js
+function getCanvasRelativePosition(event) {
+  const rect = canvas.getBoundingClientRect();
+  return {
+    x: event.clientX - rect.left,
+    y: event.clientY - rect.top,
+  };
+}
+
 function setPickPosition(event) {
 function setPickPosition(event) {
-  pickPosition.x = (event.clientX / canvas.clientWidth ) *  2 - 1;
-  pickPosition.y = (event.clientY / canvas.clientHeight) * -2 + 1;  // note we flip Y
+  const pos = getCanvasRelativePosition(event);
+  pickPosition.x = (pos.x / canvas.clientWidth ) *  2 - 1;
+  pickPosition.y = (pos.y / canvas.clientHeight) * -2 + 1;  // note we flip Y
 }
 }
 window.addEventListener('mousemove', setPickPosition);
 window.addEventListener('mousemove', setPickPosition);
 ```
 ```
@@ -551,11 +560,12 @@ make just minor changes to use `sendMouse`
 
 
 ```js
 ```js
 function setPickPosition(event) {
 function setPickPosition(event) {
--  pickPosition.x = (event.clientX / canvas.clientWidth ) *  2 - 1;
--  pickPosition.y = (event.clientY / canvas.clientHeight) * -2 + 1;  // note we flip Y
+  const pos = getCanvasRelativePosition(event);
+-  pickPosition.x = (pos.x / canvas.clientWidth ) *  2 - 1;
+-  pickPosition.y = (pos.y / canvas.clientHeight) * -2 + 1;  // note we flip Y
 +  sendMouse(
 +  sendMouse(
-+      (event.clientX / canvas.clientWidth ) *  2 - 1,
-+      (event.clientY / canvas.clientHeight) * -2 + 1);  // note we flip Y
++      (pos.x / canvas.clientWidth ) *  2 - 1,
++      (pos.y / canvas.clientHeight) * -2 + 1);  // note we flip Y
 }
 }
 
 
 function clearPickPosition() {
 function clearPickPosition() {
@@ -745,12 +755,22 @@ to the shared three.js code as well while changing
 `canvas` to `inputElement`.
 `canvas` to `inputElement`.
 
 
 ```js
 ```js
+function getCanvasRelativePosition(event) {
+-  const rect = canvas.getBoundingClientRect();
++  const rect = inputElement.getBoundingClientRect();
+  return {
+    x: event.clientX - rect.left,
+    y: event.clientY - rect.top,
+  };
+}
+
 function setPickPosition(event) {
 function setPickPosition(event) {
+  const pos = getCanvasRelativePosition(event);
 -  sendMouse(
 -  sendMouse(
--      (event.clientX / canvas.clientWidth ) *  2 - 1,
--      (event.clientY / canvas.clientHeight) * -2 + 1);  // note we flip Y
-+  pickPosition.x = (event.clientX / inputElement.clientWidth ) *  2 - 1;
-+  pickPosition.y = (event.clientY / inputElement.clientHeight) * -2 + 1;  // note we flip Y
+-      (pos.x / canvas.clientWidth ) *  2 - 1,
+-      (pos.y / canvas.clientHeight) * -2 + 1);  // note we flip Y
++  pickPosition.x = (pos.x / inputElement.clientWidth ) *  2 - 1;
++  pickPosition.y = (pos.y / inputElement.clientHeight) * -2 + 1;  // note we flip Y
 }
 }
 
 
 function clearPickPosition() {
 function clearPickPosition() {
@@ -984,9 +1004,21 @@ class ElementProxyReceiver extends THREE.EventDispatcher {
 +  }
 +  }
 +  get clientHeight() {
 +  get clientHeight() {
 +    return this.height;
 +    return this.height;
++  }
++  getBoundingClientRect() {
++    return {
++      left: this.left,
++      top: this.top,
++      width: this.width,
++      height: this.height,
++      right: this.left + this.width,
++      bottom: this.top + this.height,
++    };
 +  }
 +  }
   handleEvent(data) {
   handleEvent(data) {
 +    if (data.type === 'size') {
 +    if (data.type === 'size') {
++      this.left = data.left;
++      this.top = data.top;
 +      this.width = data.width;
 +      this.width = data.width;
 +      this.height = data.height;
 +      this.height = data.height;
 +      return;
 +      return;
@@ -1001,7 +1033,9 @@ class ElementProxyReceiver extends THREE.EventDispatcher {
 }
 }
 ```
 ```
 
 
-back in the main page we need to send the size
+back in the main page we need to send the size and the left and top positions as well.
+Note that as is we don't handle if the canvas moves, only if it resizes. If you wanted
+to handle moving you'd need to call `sendSize` anytime something moved the canvas.
 
 
 ```js
 ```js
 class ElementProxy {
 class ElementProxy {
@@ -1029,8 +1063,11 @@ class ElementProxy {
     }
     }
 
 
 +    function sendSize() {
 +    function sendSize() {
++      const rect = element.getBoundingClientRect();
 +      sendEvent({
 +      sendEvent({
 +        type: 'size',
 +        type: 'size',
++        left: rect.left,
++        top: rect.top,
 +        width: element.clientWidth,
 +        width: element.clientWidth,
 +        height: element.clientHeight,
 +        height: element.clientHeight,
 +      });
 +      });

+ 16 - 6
threejs/lessons/threejs-picking.md

@@ -131,9 +131,18 @@ clearPickPosition();
 
 
 ...
 ...
 
 
+function getCanvasRelativePosition(event) {
+  const rect = canvas.getBoundingClientRect();
+  return {
+    x: event.clientX - rect.left,
+    y: event.clientY - rect.top,
+  };
+}
+
 function setPickPosition(event) {
 function setPickPosition(event) {
-  pickPosition.x = (event.clientX / canvas.clientWidth ) *  2 - 1;
-  pickPosition.y = (event.clientY / canvas.clientHeight) * -2 + 1;  // note we flip Y
+  const pos = getCanvasRelativePosition(event);
+  pickPosition.x = (pos.x / canvas.clientWidth ) *  2 - 1;
+  pickPosition.y = (pos.y / canvas.clientHeight) * -2 + 1;  // note we flip Y
 }
 }
 
 
 function clearPickPosition() {
 function clearPickPosition() {
@@ -318,10 +327,11 @@ Because we're picking from pixels instead of ray casting we can change the code
 
 
 ```js
 ```js
 function setPickPosition(event) {
 function setPickPosition(event) {
--  pickPosition.x = (event.clientX / canvas.clientWidth ) *  2 - 1;
--  pickPosition.y = (event.clientY / canvas.clientHeight) * -2 + 1;  // note we flip Y
-+  pickPosition.x = event.clientX;
-+  pickPosition.y = event.clientY;
+  const pos = getCanvasRelativePosition(event);
+-  pickPosition.x = (pos.x / canvas.clientWidth ) *  2 - 1;
+-  pickPosition.y = (pos.y / canvas.clientHeight) * -2 + 1;  // note we flip Y
++  pickPosition.x = pos.x;
++  pickPosition.y = pos.y;
 }
 }
 ```
 ```
 
 

+ 17 - 6
threejs/lessons/threejs-voxel-geometry.md

@@ -969,13 +969,18 @@ the position of intersection and the normal of the face
 hit.
 hit.
 
 
 ```js
 ```js
-const mouse = {
-  x: 0,
-  y: 0,
-};
+function getCanvasRelativePosition(event) {
+  const rect = canvas.getBoundingClientRect();
+  return {
+    x: event.clientX - rect.left,
+    y: event.clientY - rect.top,
+  };
+}
+
 function placeVoxel(event) {
 function placeVoxel(event) {
-  const x = (event.clientX / canvas.clientWidth ) *  2 - 1;
-  const y = (event.clientY / canvas.clientHeight) * -2 + 1;  // note we flip Y
+  const pos = getCanvasRelativePosition(event);
+  const x = (pos.x / canvas.clientWidth ) *  2 - 1;
+  const y = (pos.y / canvas.clientHeight) * -2 + 1;  // note we flip Y
 
 
   const start = new THREE.Vector3();
   const start = new THREE.Vector3();
   const end = new THREE.Vector3();
   const end = new THREE.Vector3();
@@ -997,6 +1002,12 @@ function placeVoxel(event) {
     requestRenderIfNotRequested();
     requestRenderIfNotRequested();
   }
   }
 }
 }
+
+const mouse = {
+  x: 0,
+  y: 0,
+};
+
 function recordStartPosition(event) {
 function recordStartPosition(event) {
   mouse.x = event.clientX;
   mouse.x = event.clientX;
   mouse.y = event.clientY;
   mouse.y = event.clientY;

+ 12 - 0
threejs/offscreencanvas-worker-orbitcontrols.js

@@ -19,8 +19,20 @@ class ElementProxyReceiver extends THREE.EventDispatcher {
   get clientHeight() {
   get clientHeight() {
     return this.height;
     return this.height;
   }
   }
+  getBoundingClientRect() {
+    return {
+      left: this.left,
+      top: this.top,
+      width: this.width,
+      height: this.height,
+      right: this.left + this.width,
+      bottom: this.top + this.height,
+    };
+  }
   handleEvent(data) {
   handleEvent(data) {
     if (data.type === 'size') {
     if (data.type === 'size') {
+      this.left = data.left;
+      this.top = data.top;
       this.width = data.width;
       this.width = data.width;
       this.height = data.height;
       this.height = data.height;
       return;
       return;

+ 11 - 2
threejs/shared-orbitcontrols.js

@@ -118,9 +118,18 @@ function init(data) {   /* eslint-disable-line no-unused-vars */
 
 
   requestAnimationFrame(render);
   requestAnimationFrame(render);
 
 
+  function getCanvasRelativePosition(event) {
+    const rect = inputElement.getBoundingClientRect();
+    return {
+      x: event.clientX - rect.left,
+      y: event.clientY - rect.top,
+    };
+  }
+
   function setPickPosition(event) {
   function setPickPosition(event) {
-    pickPosition.x = (event.clientX / inputElement.clientWidth ) *  2 - 1;
-    pickPosition.y = (event.clientY / inputElement.clientHeight) * -2 + 1;  // note we flip Y
+    const pos = getCanvasRelativePosition(event);
+    pickPosition.x = (pos.x / inputElement.clientWidth ) *  2 - 1;
+    pickPosition.y = (pos.y / inputElement.clientHeight) * -2 + 1;  // note we flip Y
   }
   }
 
 
   function clearPickPosition() {
   function clearPickPosition() {

+ 9 - 1
threejs/threejs-indexed-textures-picking-and-highlighting.html

@@ -348,13 +348,21 @@ function main() {
 
 
   const pickHelper = new GPUPickHelper();
   const pickHelper = new GPUPickHelper();
 
 
+  function getCanvasRelativePosition(event) {
+    const rect = canvas.getBoundingClientRect();
+    return {
+      x: event.clientX - rect.left,
+      y: event.clientY - rect.top,
+    };
+  }
+
   function pickCountry(event) {
   function pickCountry(event) {
     // exit if we have not loaded the data yet
     // exit if we have not loaded the data yet
     if (!countryInfos) {
     if (!countryInfos) {
       return;
       return;
     }
     }
 
 
-    const position = {x: event.clientX, y: event.clientY};
+    const position = getCanvasRelativePosition(event);
     const id = pickHelper.pick(position, pickingScene, camera);
     const id = pickHelper.pick(position, pickingScene, camera);
     if (id > 0) {
     if (id > 0) {
       const countryInfo = countryInfos[id - 1];
       const countryInfo = countryInfos[id - 1];

+ 15 - 6
threejs/threejs-indexed-textures-picking-debounced.html

@@ -352,10 +352,20 @@ function main() {
   const maxMoveDeltaSq = 5 * 5;
   const maxMoveDeltaSq = 5 * 5;
   const startPosition = {};
   const startPosition = {};
   let startTimeMs;
   let startTimeMs;
+
+  function getCanvasRelativePosition(event) {
+    const rect = canvas.getBoundingClientRect();
+    return {
+      x: event.clientX - rect.left,
+      y: event.clientY - rect.top,
+    };
+  }
+
   function recordStartTimeAndPosition(event) {
   function recordStartTimeAndPosition(event) {
     startTimeMs = performance.now();
     startTimeMs = performance.now();
-    startPosition.x = event.clientX;
-    startPosition.y = event.clientY;
+    const pos = getCanvasRelativePosition(event);
+    startPosition.x = pos.x;
+    startPosition.y = pos.y;
   }
   }
 
 
   function pickCountry(event) {
   function pickCountry(event) {
@@ -372,14 +382,13 @@ function main() {
     }
     }
 
 
     // if they moved assume it was a drag action
     // if they moved assume it was a drag action
-    const moveDeltaSq = (startPosition.x - event.clientX) ** 2 +
-                        (startPosition.y - event.clientY) ** 2;
+    const position = getCanvasRelativePosition(event);
+    const moveDeltaSq = (startPosition.x - position.x) ** 2 +
+                        (startPosition.y - position.y) ** 2;
     if (moveDeltaSq > maxMoveDeltaSq) {
     if (moveDeltaSq > maxMoveDeltaSq) {
       return;
       return;
     }
     }
 
 
-
-    const position = {x: event.clientX, y: event.clientY};
     const id = pickHelper.pick(position, pickingScene, camera);
     const id = pickHelper.pick(position, pickingScene, camera);
     if (id > 0) {
     if (id > 0) {
       const countryInfo = countryInfos[id - 1];
       const countryInfo = countryInfos[id - 1];

+ 9 - 1
threejs/threejs-indexed-textures-picking.html

@@ -279,13 +279,21 @@ function main() {
 
 
   const pickHelper = new GPUPickHelper();
   const pickHelper = new GPUPickHelper();
 
 
+  function getCanvasRelativePosition(event) {
+    const rect = canvas.getBoundingClientRect();
+    return {
+      x: event.clientX - rect.left,
+      y: event.clientY - rect.top,
+    };
+  }
+
   function pickCountry(event) {
   function pickCountry(event) {
     // exit if we have not loaded the data yet
     // exit if we have not loaded the data yet
     if (!countryInfos) {
     if (!countryInfos) {
       return;
       return;
     }
     }
 
 
-    const position = {x: event.clientX, y: event.clientY};
+    const position = getCanvasRelativePosition(event);
     const id = pickHelper.pick(position, pickingScene, camera);
     const id = pickHelper.pick(position, pickingScene, camera);
     if (id > 0) {
     if (id > 0) {
       // we clicked a country. Toggle its 'selected' property
       // we clicked a country. Toggle its 'selected' property

+ 9 - 1
threejs/threejs-indexed-textures-random-colors.html

@@ -328,13 +328,21 @@ function main() {
 
 
   const pickHelper = new GPUPickHelper();
   const pickHelper = new GPUPickHelper();
 
 
+  function getCanvasRelativePosition(event) {
+    const rect = canvas.getBoundingClientRect();
+    return {
+      x: event.clientX - rect.left,
+      y: event.clientY - rect.top,
+    };
+  }
+
   function pickCountry(event) {
   function pickCountry(event) {
     // exit if we have not loaded the data yet
     // exit if we have not loaded the data yet
     if (!countryInfos) {
     if (!countryInfos) {
       return;
       return;
     }
     }
 
 
-    const position = {x: event.clientX, y: event.clientY};
+    const position = getCanvasRelativePosition(event);
     const id = pickHelper.pick(position, pickingScene, camera);
     const id = pickHelper.pick(position, pickingScene, camera);
     if (id > 0) {
     if (id > 0) {
       const countryInfo = countryInfos[id - 1];
       const countryInfo = countryInfos[id - 1];

+ 3 - 0
threejs/threejs-offscreencanvas-w-orbitcontrols.html

@@ -128,8 +128,11 @@ class ElementProxy {
     }
     }
 
 
     function sendSize() {
     function sendSize() {
+      const rect = element.getBoundingClientRect();
       sendEvent({
       sendEvent({
         type: 'size',
         type: 'size',
+        left: rect.left,
+        top: rect.top,
         width: element.clientWidth,
         width: element.clientWidth,
         height: element.clientHeight,
         height: element.clientHeight,
       });
       });

+ 11 - 2
threejs/threejs-offscreencanvas-w-picking.html

@@ -81,10 +81,19 @@ function main() {  /* eslint consistent-return: 0 */
     startMainPage(canvas);
     startMainPage(canvas);
   }
   }
 
 
+  function getCanvasRelativePosition(event) {
+    const rect = canvas.getBoundingClientRect();
+    return {
+      x: event.clientX - rect.left,
+      y: event.clientY - rect.top,
+    };
+  }
+
   function setPickPosition(event) {
   function setPickPosition(event) {
+    const pos = getCanvasRelativePosition(event);
     sendMouse(
     sendMouse(
-        (event.clientX / canvas.clientWidth ) *  2 - 1,
-        (event.clientY / canvas.clientHeight) * -2 + 1);  // note we flip Y
+        (pos.x / canvas.clientWidth ) *  2 - 1,
+        (pos.y / canvas.clientHeight) * -2 + 1);  // note we flip Y
   }
   }
 
 
   function clearPickPosition() {
   function clearPickPosition() {

+ 11 - 2
threejs/threejs-picking-gpu.html

@@ -205,9 +205,18 @@ function main() {
   }
   }
   requestAnimationFrame(render);
   requestAnimationFrame(render);
 
 
+  function getCanvasRelativePosition(event) {
+    const rect = canvas.getBoundingClientRect();
+    return {
+      x: event.clientX - rect.left,
+      y: event.clientY - rect.top,
+    };
+  }
+
   function setPickPosition(event) {
   function setPickPosition(event) {
-    pickPosition.x = event.clientX;
-    pickPosition.y = event.clientY;
+    const pos = getCanvasRelativePosition(event);
+    pickPosition.x = pos.x;
+    pickPosition.y = pos.y;
   }
   }
 
 
   function clearPickPosition() {
   function clearPickPosition() {

+ 11 - 2
threejs/threejs-picking-raycaster-complex-geo.html

@@ -136,9 +136,18 @@ function main() {
   }
   }
   requestAnimationFrame(render);
   requestAnimationFrame(render);
 
 
+  function getCanvasRelativePosition(event) {
+    const rect = canvas.getBoundingClientRect();
+    return {
+      x: event.clientX - rect.left,
+      y: event.clientY - rect.top,
+    };
+  }
+
   function setPickPosition(event) {
   function setPickPosition(event) {
-    pickPosition.x = event.clientX;
-    pickPosition.y = event.clientY;
+    const pos = getCanvasRelativePosition(event);
+    pickPosition.x = pos.x;
+    pickPosition.y = pos.y;
   }
   }
 
 
   function clearPickPosition() {
   function clearPickPosition() {

+ 11 - 2
threejs/threejs-picking-raycaster-transparency.html

@@ -153,9 +153,18 @@ function main() {
   }
   }
   requestAnimationFrame(render);
   requestAnimationFrame(render);
 
 
+  function getCanvasRelativePosition(event) {
+    const rect = canvas.getBoundingClientRect();
+    return {
+      x: event.clientX - rect.left,
+      y: event.clientY - rect.top,
+    };
+  }
+
   function setPickPosition(event) {
   function setPickPosition(event) {
-    pickPosition.x = (event.clientX / canvas.clientWidth ) *  2 - 1;
-    pickPosition.y = (event.clientY / canvas.clientHeight) * -2 + 1;  // note we flip Y
+    const pos = getCanvasRelativePosition(event);
+    pickPosition.x = (pos.x / canvas.clientWidth ) *  2 - 1;
+    pickPosition.y = (pos.y / canvas.clientHeight) * -2 + 1;  // note we flip Y
   }
   }
 
 
   function clearPickPosition() {
   function clearPickPosition() {

+ 11 - 2
threejs/threejs-picking-raycaster.html

@@ -146,9 +146,18 @@ function main() {
   }
   }
   requestAnimationFrame(render);
   requestAnimationFrame(render);
 
 
+  function getCanvasRelativePosition(event) {
+    const rect = canvas.getBoundingClientRect();
+    return {
+      x: event.clientX - rect.left,
+      y: event.clientY - rect.top,
+    };
+  }
+
   function setPickPosition(event) {
   function setPickPosition(event) {
-    pickPosition.x = (event.clientX / canvas.clientWidth ) *  2 - 1;
-    pickPosition.y = (event.clientY / canvas.clientHeight) * -2 + 1;  // note we flip Y
+    const pos = getCanvasRelativePosition(event);
+    pickPosition.x = (pos.x / canvas.clientWidth ) *  2 - 1;
+    pickPosition.y = (pos.y / canvas.clientHeight) * -2 + 1;  // note we flip Y
   }
   }
 
 
   function clearPickPosition() {
   function clearPickPosition() {

+ 11 - 2
threejs/threejs-tips-preservedrawingbuffer.html

@@ -106,10 +106,19 @@ function main() {
   }
   }
   requestAnimationFrame(render);
   requestAnimationFrame(render);
 
 
+  function getCanvasRelativePosition(event) {
+    const rect = canvas.getBoundingClientRect();
+    return {
+      x: event.clientX - rect.left,
+      y: event.clientY - rect.top,
+    };
+  }
+
   const temp = new THREE.Vector3();
   const temp = new THREE.Vector3();
   function setPosition(e) {
   function setPosition(e) {
-    const x = e.clientX / canvas.clientWidth  *  2 - 1;
-    const y = e.clientY / canvas.clientHeight * -2 + 1;
+    const pos = getCanvasRelativePosition(e);
+    const x = pos.x / canvas.clientWidth  *  2 - 1;
+    const y = pos.y / canvas.clientHeight * -2 + 1;
     temp.set(x, y, 0).unproject(camera);
     temp.set(x, y, 0).unproject(camera);
     state.x = temp.x;
     state.x = temp.x;
     state.y = temp.y;
     state.y = temp.y;

+ 17 - 6
threejs/threejs-voxel-geometry-culled-faces-ui.html

@@ -523,13 +523,18 @@ function main() {
     }
     }
   }
   }
 
 
-  const mouse = {
-    x: 0,
-    y: 0,
-  };
+  function getCanvasRelativePosition(event) {
+    const rect = canvas.getBoundingClientRect();
+    return {
+      x: event.clientX - rect.left,
+      y: event.clientY - rect.top,
+    };
+  }
+
   function placeVoxel(event) {
   function placeVoxel(event) {
-    const x = (event.clientX / canvas.clientWidth ) *  2 - 1;
-    const y = (event.clientY / canvas.clientHeight) * -2 + 1;  // note we flip Y
+    const pos = getCanvasRelativePosition(event);
+    const x = (pos.x / canvas.clientWidth ) *  2 - 1;
+    const y = (pos.y / canvas.clientHeight) * -2 + 1;  // note we flip Y
 
 
     const start = new THREE.Vector3();
     const start = new THREE.Vector3();
     const end = new THREE.Vector3();
     const end = new THREE.Vector3();
@@ -551,6 +556,12 @@ function main() {
       requestRenderIfNotRequested();
       requestRenderIfNotRequested();
     }
     }
   }
   }
+
+  const mouse = {
+    x: 0,
+    y: 0,
+  };
+
   function recordStartPosition(event) {
   function recordStartPosition(event) {
     mouse.x = event.clientX;
     mouse.x = event.clientX;
     mouse.y = event.clientY;
     mouse.y = event.clientY;