Three.js触摸事件无法在移动设备上正常运行

劳拉·安娜贝尔

因此,我有一个交互式的3D地球仪,它用three.js和一个模拟数据库构建。

当您单击一个国家时,它将调用模拟数据库并在弹出窗口中返回信息。它可以完美地在台式机上运行,​​但是我在移动设备方面一直遇到问题。

通过添加触摸事件,我现在已经能够在移动设备上进行触摸时获得国家/地区信息,但是似乎并没有使用UsersManager函数来调用数据库。

我很欣赏这有点冗长,但是由于它目前大约有50个文件,因此我也无法将其弹出到codeandbox上。

如果有人能在“测试”功能中发现我需要做的任何事情,我将不胜感激。

桌面弹出窗口工作

import { PerspectiveCamera, Raycaster, WebGLRenderer } from "three";

import CountriesManager from "./CountriesManager";
import HitDetector from "./HitDetector";
import Loader from "./loader";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
import SceneDetection from "./scenes/SceneDetection";
import SceneMain from "./scenes/SceneMain";
import UsersManager from "./UsersManager";
import { getMouse } from "./utils";
import { onCountryClick } from "./AppSignals";

export default class SceneApp {
  init() {
    this.loadAssets(); // FIRST THING -> load assets
  }

  loadAssets() {
    const loader = new Loader();

    loader.load(
      ["assets/earthtemplate.jpg", "assets/circle.png", "assets/worldmap.gif"],
      this.onLoadingComplete
    );
  }

  // only initialise scene when the assets are all loaded
  onLoadingComplete = () => {
    this.countryText = document.getElementById("countryName");
    this.renderer = new WebGLRenderer({
      antialias: true
    });
    document.body.appendChild(this.renderer.domElement);

    this.countryText = document.getElementById("countryName");

    // lazy, to know which country we hovering we will update this html element
    // TODO: as we're using React, may be best to signal to the parent component to update its state.countryName ?

    UsersManager.init(); // ideally we won't have to do that when it's linked to a proper db
    CountriesManager.init(); // will link the countries colors to their name / id

    this.currentState = STATES.explore;
    this.easing = 0.2; // is camera animating around the globe?
    this.animating = false; // is camera animating around the globe?
    this._down = false; // is mouse down
    this._downAndMove = false; // is mouse down and moving at the same time?
    this._mouse = { x: 0, y: 0 }; // could probably be in a class specific for Interaction
    this._target = { x: 0, y: 0, z: 0 }; // the position for the camera to rotate around the globe
    this.scene = new SceneMain(); // main Scene with all the visual things in it
    this.scene.init();

    this.sceneDetection = new SceneDetection(); // this scene will only be used as a render texture to know which country we're hovering
    this.sceneDetection.init(); // it will only contain the colored globe

    const ratio = window.innerWidth / window.innerHeight;
    this.camera = new PerspectiveCamera(45, ratio, 1, 3000);
    this.camera.position.set(0, 20, CAMERA_DIST);
    this.camera.lookAt(this.scene.position);

    this.controls = new OrbitControls(this.camera, this.renderer.domElement);

    this.controls.enableDamping = true;
    this.controls.enablePan = false;

    this.hitDetector = new HitDetector(
      this.sceneDetection,
      this.renderer,
      this.camera
    ); // hit detector will check what color we're hovering

    this.raycaster = new Raycaster();
    this.addEvents();
    this.update(); // starts the RAF loop
  };

  addEvents() {
    // resize
    window.addEventListener("resize", this.resize);
    window.addEventListener("mousedown", this.onDown);
    window.addEventListener("mouseup", this.onUp);
    window.addEventListener("mousemove", this.onMove);
    window.addEventListener("click", this.onClick);
    window.addEventListener("touchstart", this.test);
    window.addEventListener("touchmove", this.onMove);
    window.addEventListener("touchend", this.onDown);

    this.resize();
  }

  removeEvents() {
    window.removeEventListener("resize", this.resize);
    window.removeEventListener("mousedown", this.onDown);
    window.removeEventListener("mouseup", this.onUp);
    window.removeEventListener("mousemove", this.onMove);
    window.removeEventListener("click", this.onClick);
    window.removeEventListener("touchstart", this.test);
    window.removeEventListener("touchmove", this.test);
    window.removeEventListener("touchend", this.onDown);
  }

  test = e => {
    getMouse(e, this._mouse); // get the mouse position

    if (this.currentState !== STATES.explore) {
      // if we are focusing on a country, no white overlays on other countries
      if (this._down && this.currentState === STATES.select)
        this.currentState = STATES.animateOut; // if we're moving while having the mousepressing down, get out of the zoom

      return;
    }

    if (this._down) this._downAndMove = true; // we're moving while pressing down
    const color = this.hitDetector.update(this._mouse.x, this._mouse.y); // get the color from the hit detector
    const country = CountriesManager.getCountry(color); // get the country depending of the color

    if (country && country !== this.lastCountry) {
      // if that's a different country that we're hovering
      this.lastCountry = country;
      this.countryText.innerText = country.name;
      this.scene.onHoverCountry(country.id); // this will call a change of uniform in the shaders
      console.log(this.lastCountry);
    } else if (color === "ffffff".toUpperCase()) {
      this.lastCountry = null;

      this.scene.onHoverCountry(999999); // this will call a change of uniform in the shaders
    }

    console.log("this is working on mobile as a click", country);
  };

  onUp = e => {
    this._down = false;
    // if we were moving, means we're exploring so don't continue further more
    if (this._downAndMove) {
      this._downAndMove = false;

      return;
    }

    if (this.currentState === STATES.explore && this.lastCountry) {
      const int = this.getIntersection(this._mouse);
      if (int) {
        // zoom on the country
        const { x, y, z } = int.point;
        let scale =
          (GLOBE_RADIUS + (CAMERA_DIST - GLOBE_RADIUS) / 1.5) / GLOBE_RADIUS;

        this.animating = true;
        this._target.x = x * scale;
        this._target.y = y * scale;
        this._target.z = z * scale;
        this.currentState = STATES.animateIn; // see update function
      }

      // dispatch country / users
      const users = UsersManager.getUsersPerCountry(this.lastCountry.name);
      onCountryClick.dispatch(this.lastCountry.name, users);
    }
  };

  onDown = e => {
    this._down = true;
  };

  onMove = e => {
    getMouse(e, this._mouse); // get the mouse position

    // move particles up when moving the mouse on the sphere
    const int = this.getIntersection(this._mouse);
    if (int) this.scene.pushParticlesUp(int);

    if (this.currentState !== STATES.explore) {
      // if we are focusing on a country, no white overlays on other countries
      if (this._down && this.currentState === STATES.select)
        this.currentState = STATES.animateOut; // if we're moving while having the mousepressing down, get out of the zoom

      return;
    }

    if (this._down) this._downAndMove = true; // we're moving while pressing down
    const color = this.hitDetector.update(this._mouse.x, this._mouse.y); // get the color from the hit detector
    const country = CountriesManager.getCountry(color); // get the country depending of the color

    if (country && country !== this.lastCountry) {
      // if that's a different country that we're hovering
      this.lastCountry = country;
      this.countryText.innerText = country.name;
      this.scene.onHoverCountry(country.id); // this will call a change of uniform in the shaders
    } else if (color === "ffffff".toUpperCase()) {
      this.lastCountry = null;
      this.countryText.innerText = "";
      this.scene.onHoverCountry(999999); // this will call a change of uniform in the shaders
    }
  };

  getIntersection(pos) {
    const x = (pos.x / window.innerWidth) * 2 - 1; // get the mouse coordinates between -1 and 1 (clipspace coordinates)
    const y = -(pos.y / window.innerHeight) * 2 + 1;

    this.raycaster.setFromCamera({ x, y }, this.camera); // raycast
    let intersects = this.raycaster.intersectObjects(
      this.sceneDetection.children
    ); // should only be one sphere
    return intersects[0]; // get first intersection
  }

  update() {
    this.renderer.render(this.scene, this.camera);

    this.controls.update();

    // depending of the state of the globe (see consts file) we need to zoom in / out, etc.
    if (this.currentState === STATES.animateOut) {
      // in animate out of the selected view, need the camera to zoom back
      const dist = this.camera.position.length();
      const diff = CAMERA_DIST - dist;

      if (diff < 1) {
        // change the state if the camera is far enough
        this.currentState = STATES.explore;
      } else {
        const scale = CAMERA_DIST / dist;
        const newX = this.camera.position.x * scale;
        const newY = this.camera.position.y * scale;
        const newZ = this.camera.position.z * scale;

        this.camera.position.x += (newX - this.camera.position.x) * this.easing;
        this.camera.position.y += (newY - this.camera.position.y) * this.easing;
        this.camera.position.z += (newZ - this.camera.position.z) * this.easing;
      }
    } else if (this.currentState === STATES.animateIn) {
      // if animateIn, ease to the camera!
      // TODO to aboid jerkyness in the movement, you can clamp diffX, diffY, diffZ so it doesn't add too much in one frame
      const diffX = (this._target.x - this.camera.position.x) * this.easing;
      const diffY = (this._target.y - this.camera.position.y) * this.easing;
      const diffZ = (this._target.z - this.camera.position.z) * this.easing;
      this.camera.position.x += diffX;
      this.camera.position.y += diffY;
      this.camera.position.z += diffZ;

      // if we're close enough from the target, change state
      if (Math.abs(diffX * diffY * diffZ) < 0.01)
        this.currentState = STATES.select;
    } else if (this.currentState === STATES.explore && !this._down) {
      this.sceneDetection.update();
      this.scene.update();
    }

    this.raf = requestAnimationFrame(this.update.bind(this));
  }

  resize = () => {
    this.renderer.setSize(window.innerWidth, window.innerHeight);
    this.camera.aspect = window.innerWidth / window.innerHeight;
    this.camera.updateProjectionMatrix();
  };

  unmount() {
    this.removeEvents();

    this.renderer.domElement.remove();
    cancelAnimationFrame(this.raf);
  }
}
TheJim01

添加我的评论作为完整答案,因此该问题可以标记为已解决。

您的touchend事件当前指向与mousedown事件相同的处理程序

window.addEventListener("touchend", this.onDown);

在大多数情况下(尽管可能不在您的情况下),它touchend等同于mouseup事件(在该事件中您可以进行数据库调用)。

window.addEventListener("touchend", this.onUp);

如果正确,您也应该更新您的removeEventListener

本文收集自互联网,转载请注明来源。

如有侵权,请联系[email protected] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章

来自分类Dev

Three.js:SVGRenderer?

来自分类Dev

onClick无法在移动设备上运行(触摸)

来自分类Dev

THREE.meshphongmaterial无法正常工作-黑色

来自分类Dev

Three.js /交集

来自分类Dev

移动设备上Google地图的触摸事件

来自分类Dev

Three.js-TransformControls

来自分类Dev

支持WebGL,但Three.js中的等角矩形示例无法在设备上运行?

来自分类Dev

Three.js:同时通过触摸和设备方向旋转相机

来自分类Dev

面向移动设备的Three.js问题

来自分类Dev

THREE.js OrbitControls无法正常工作

来自分类Dev

Three.js相机外观无法正常工作

来自分类Dev

Three.JS通过移动设备围绕对象旋转相机

来自分类Dev

我的three.js应用程序未在移动设备上运行

来自分类Dev

为什么Three.js中的DirectionalLight无法正常工作?

来自分类Dev

为什么three.js投射阴影无法在3D模型上运行

来自分类Dev

THREE.SpriteCanvasMaterial无法正常工作

来自分类Dev

Three.js抗锯齿无法在Chrome上运行

来自分类Dev

JS中的移动设备上的点击与触摸

来自分类Dev

Three.JS与流

来自分类Dev

Three.js旋转

来自分类Dev

THREE.js-在从TubeGeometry构造的网格上移动纹理

来自分类Dev

THREE.ObjectLoader的Three.js错误

来自分类Dev

three.js渲染到纹理Alpha无法正常工作

来自分类Dev

媒体查询无法在移动设备上正常运行

来自分类Dev

移动设备上Google地图的触摸事件

来自分类Dev

Three.js-点

来自分类Dev

Three.js-TransformControls

来自分类Dev

Three.js相机外观无法正常工作

来自分类Dev

在three.js中旋转相机无法正常工作