当DOM元素位于场景上方时,THREE.JS OrbitControls无法工作。

4
我正在尝试使用position:absolute;将标签作为元素放置在THREE.JS场景上。问题是当鼠标悬停在其中一个标签上(下面示例中的红色框),触发OrbitControls的事件被标签“停止”,不会传播到渲染器。

我已经复制了代码的最小版本以查看问题。

enter image description here

<html lang="en">

<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
    <style>
        body {
            margin: 0px;
            overflow: hidden;
        }

        #overlay {
            position: absolute;
            top: 40%;
            left: 40%;
            width: 20%;
            height: 20%;
            background-color: #f00;
            padding: 3%;
            text-align: center;
            color: #fff;
            box-sizing: border-box;
        }
    </style>
</head>

<body>
    <div id="container"></div>

    <!-- This div below stop the OrbitControls events, why? -->
    <div id="overlay">I am a div with position:absolute</div>

    <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/87/three.min.js"></script>
    <!-- https://raw.githubusercontent.com/mrdoob/three.js/r87/examples/js/controls/OrbitControls.js -->
    <script src="orbit-controls.js"></script>
    <script>
        var container;
        var camera, scene, renderer;
        var uniforms, material, mesh;
        var controls;
        init();
        animate();

        function init() {
            container = document.getElementById('container');
            var aspect = window.innerWidth / window.innerHeight;
            camera = new THREE.PerspectiveCamera(45, aspect, 0.1, 1500);
            camera.position.set(1, 1, 1);
            scene = new THREE.Scene();
            renderer = new THREE.WebGLRenderer();
            container.appendChild(renderer.domElement);
            renderer.setSize(window.innerWidth, window.innerHeight);
            controls = new THREE.OrbitControls(this.camera, this.renderer.domElement);

            var geometry = new THREE.BoxGeometry(1, 1, 1);
            var material = new THREE.MeshBasicMaterial({
                color: 0x00ff00
            });
            var cube = new THREE.Mesh(geometry, material);

            scene.add(cube);
        }

        function animate() {
            requestAnimationFrame(animate);
            render();
        }

        function render() {
            renderer.render(scene, camera);
        }
    </script>
</body>

</html>

这里有一个类似的项目链接,标签不会阻止事件传播,因此相机可以跟随鼠标交互。然而,我还没有找到是什么使得这个例子能够工作而我的不能。 http://armsglobe.chromeexperiments.com 如何让OrbitControls在
标签后仍然起作用?

1
尝试这个:controls = new THREE.OrbitControls(this.camera);。默认情况下,domElement 将是 document - prisoner849
谢谢@prisoner849!它解决了问题。你想写答案还是我写? - Ben
2
你可能想在覆盖元素的CSS中添加 pointer-events: none;,这也可以解决问题。 - Martin Schuhfuß
甚至更好的解决方案@MartinSchuhfuß。更改渲染器的domElement可以解决覆盖问题,但会在我的项目中与链接到OrbitControls的相机引起其他问题。 pointer-events: none; 最好的效果。只需要注意浏览器兼容性即可。 - Ben
@MartinSchuhfuß 有没有类似的解决方案,可以允许在标签上进行“点击”事件? - Ben
2个回答

6

总结评论中的答案:

如果您不需要从覆盖层中获取任何鼠标事件,最简单的方法是通过CSS禁用事件处理:

<div class="overlay" style="pointer-events: none">...</div>

您还可以使用一个共同的父元素来处理事件:
<div class="parent" style="position:relative">
  <canvas ... /> <!-- this is the canvas from renderer.domElement -->
  <div class="overlay"></div>
</div>

而对于控件

const controls = new THREE.OrbitControls(camera, document.querySelector('.parent'));

这样,所有到达父级的鼠标事件(包括覆盖层的事件)都将由控件处理(一个特殊情况是通过省略dom元素(或指定document.documentElement)来处理根级别的所有事件,用于OrbitControls构造函数)。
如果您想处理来自覆盖层的某些事件,例如点击事件,您可以使用stopPropagation()阻止它们被转发到轨道控件。
overlayEl.addEventListener('click', event => {
  event.stopPropagation();

  // handle event
});

马丁,我尝试了这个。似乎事件在根处被处理,所以遮罩层事件从未触发,因此无法阻止冒泡,因为它是遮罩层事件代码。我该怎么解决? - Ayush

3

如果你想在背景场景上应用轨道控制器,可以这样做:

const controls = new orbitControls.OrbitControls(camera, document.body);

我点赞这个解决方案,因为它对我有用,但我希望能够解释一下为什么它不是 => new OrbitControls(camera, renderer.domElement)。 - Abdel Shokair

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接