-1

我为我的网页使用着色器(我使用了 THREE.js)编写了一个流体模拟。我希望它即使在移动设备上也能快速运行,所以我决定以较低的分辨率进行模拟(比渲染目标小 4 倍),我设法让它为鼠标事件工作,但我一直没能破译如何正确缩放触摸事件,使它们匹配真实的触摸位置。

function handleMove(evt) {

        evt.preventDefault();
        var touches = evt.targetTouches;
        var x = 0, y = 0;

        if (BufferBUniforms.iMouse.value.z === 1) {

            var element = document.getElementById("container").getBoundingClientRect();
            var bodyRect = document.body.getBoundingClientRect();

            var h = (element.top - bodyRect.top);
            var w = (element.left - bodyRect.left);

            // One way I tried.
            x = ( touches[0].pageX - w ) / scaleMax;
            y = height - ( touches[0].pageY - h ) / scaleMax;

            // Another way I tried.
            x = ( touches[0].pageX - w ) / scaleMax;
            y = height - ( touches[0].pageY - h ) / scaleMaxO;

            BufferAUniforms.iMouse.value.x = x;
            BufferAUniforms.iMouse.value.y = y;

        }

    }

这是一个片段,我在其中定义了上面提到的一些变量:

scale = window.devicePixelRatio;
renderer.setPixelRatio(scale);
container.appendChild(renderer.domElement);

height = window.innerHeight * 0.25;
height = THREE.Math.floorPowerOfTwo( height )
scaleMax = window.innerHeight / height;
width = window.innerWidth * 0.25;
width = THREE.Math.floorPowerOfTwo(width)
scaleRatio = width / height;
scaleMaxO = window.innerWidth / width;
renderer.setSize(width * scaleMax, height * scaleMaxO);

问题是它在移动模拟器上使用 Chrome 开发工具时有效,但在使用三星 S9 Plus 时无效。

你可以在这里看到整个事情

4

1 回答 1

0

假设您正在绘制较小的渲染目标来填充画布,它应该只是

const rect = renderer.domElement.getBoundingClientRect();
const x = (touches[0].clientX - rect.left) * renderTargetWidth / rect.width;
const y = (touches[0].clientY - rect.top ) * renderTargetHeight / rect.height;

现在 X 和 Y 在渲染目标中以像素为单位,尽管您可能想要翻转 Y

const y = renderTargetHeight - 
     (touches[0].clientY - rect.top) * renderTargetHeight / rect.height;

注意鼠标也不例外。事实上,这应该工作

function computeRenderTargetRelativePosition(e) {
  const rect = renderer.domElement.getBoundingClientRect();
  const x = (e.clientX - rect.left) * renderTargetWidth  / rect.width;
  const y = (e.clientY - rect.top ) * renderTargetHeight / rect.height;
  return {x, y};
}

renderer.domElement.addEventListener('mousemove', (e) => {
  const pos = computeRenderTargetRelativePosition(e);
  ... do something with pos...
});

renderer.domElement.addEventListener('touchmove', (e) => {
  const pos = computeRenderTargetRelativePosition(e.touches[0]);
  ... do something with pos...
});

唯一的复杂情况是,如果您应用 CSS 转换,那么您需要不同的代码。

'use strict';

/* global THREE */

function main() {
  const canvas = document.querySelector('#c');
  const renderer = new THREE.WebGLRenderer({canvas});

  const rtWidth = 24;
  const rtHeight = 12;
  const renderTarget = new THREE.WebGLRenderTarget(rtWidth, rtHeight);

  const rtCamera = new THREE.OrthographicCamera(0, rtWidth, rtHeight, 0, -1, 1);
  const rtScene = new THREE.Scene();

  const boxWidth = 1;
  const boxHeight = 1;
  const boxDepth = 1;
  const geometry = new THREE.BoxGeometry(boxWidth, boxHeight, boxDepth);

  const rtMaterial = new THREE.MeshBasicMaterial({color: 'red'});
  const rtCube = new THREE.Mesh(geometry, rtMaterial);
  rtScene.add(rtCube);

  const camera = new THREE.Camera();
  const scene = new THREE.Scene();

  const planeGeo = new THREE.PlaneBufferGeometry(2, 2);
  const material = new THREE.MeshBasicMaterial({
    //color: 'blue',
    map: renderTarget.texture,
  });
  const plane = new THREE.Mesh(planeGeo, material);
  scene.add(plane);

  function resizeRendererToDisplaySize(renderer) {
    const canvas = renderer.domElement;
    const width = canvas.clientWidth;
    const height = canvas.clientHeight;
    const needResize = canvas.width !== width || canvas.height !== height;
    if (needResize) {
      renderer.setSize(width, height, false);
    }
    return needResize;
  }

  function render(time) {
    time *= 0.001;

    if (resizeRendererToDisplaySize(renderer)) {
      const canvas = renderer.domElement;
    }

    // draw render target scene to render target
    renderer.setRenderTarget(renderTarget);
    renderer.render(rtScene, rtCamera);
    renderer.setRenderTarget(null);

    // render the scene to the canvas
    renderer.render(scene, camera);

    requestAnimationFrame(render);
  }

  requestAnimationFrame(render);
  
  function setPos(e) {
    const rect = renderer.domElement.getBoundingClientRect();
    const x =            (e.clientX - rect.left) * rtWidth  / rect.width;
    const y = rtHeight - (e.clientY - rect.top ) * rtHeight / rect.height;

    rtCube.position.set(x, y, 0);
  }  

  renderer.domElement.addEventListener('mousemove', (e) => {
    setPos(e);
  });

  renderer.domElement.addEventListener('touchmove', (e) => {
    e.preventDefault();

    setPos(e.touches[0]);

  }, {passive: false});
}

main();
body {
  margin: 0;
}
#c {
  width: 100vw;
  height: 100vh;
  display: block;
}
<canvas id="c"></canvas>
<script src="https://threejsfundamentals.org/threejs/resources/threejs/r105/three.min.js"></script>

于 2019-09-28T00:24:40.387 回答