您在代码中设置z-index:-1;
了#background和display:absolute;
. 这意味着:它位于body z 层后面,鼠标事件将针对 body 或上面一层中的任何其他内容。
您可以设置pointer-events:none;
主体甚至所有元素,但这可能是一件坏事,因为稍后将其他代码包含在站点中。更喜欢将其他覆盖元素绝对定位为具有更高的 z-index,它们是:#container和#container>canvas。
查看路径是如何传播 DOM 事件的:http: //www.w3.org/TR/DOM-Level-3-Events/
在捕获阶段,事件首先传播到视图,然后传播到 html 和正文,然后它会到达鼠标光标下显示的目标。比在冒泡阶段传播采取相反的方式回到视图。
渲染的场景对象不在此 DOM 事件路径中。您已经在文档事件侦听器中对命中进行了光线追踪。在传播到达#background 元素之前执行此操作,并且如果已知要处理该事件,则在某处是安全的。你试过了preventDefault()
。不幸的是 mousemove 事件是不可取消的,所以这没有效果。稍后调用事件侦听器,检查preventedDefault
将获得false
. 的含义是防止用户代理传播后preventDefault()
执行的默认操作,例如双击后在鼠标指针下标记单词。如果应该执行默认操作,那么您不能用来告诉其他侦听器该事件已被处理。preventDefault()
您可以将 Event 的自定义属性设置为 true。它至少在 Firefox 中有效,但是,W3C 并未严格指定主机对象。这可能会导致其他浏览器或将来出现一些错误。stopPropagation()
如果不应调用其他侦听器,则可以使用。在某些情况下,这可能会导致与某些 JavaScript 框架结合使用的不良行为或错误。
另一种方法是在所有事件侦听器都可以访问的范围内设置一个变量,例如全局范围或更好的封装匿名函数。这可以保存最新处理的事件,并且在传播完成之前不会改变(下一个事件之前不会被触发)。
如果光线追踪器没有击中场景对象,您还可以在文档的侦听器中处理以#background为目标的事件。Event.target
保存鼠标事件命中的最顶层 DOM 元素。
您修改后的代码演示了不同的方法:
<!DOCTYPE html>
<html>
<head>
<title>events passthrough</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style type="text/css">
body {
font-family: Monospace;
margin: 0px;
overflow: hidden;
}
div#background {
position: absolute;
top: 40px;
left: 40px;
width: 100px;
height: 100px;
background-color: pink;
/*z-index: -1; /* this is behind the body */
}
#container>canvas
{ position: absolute;
z-index: 100;
}
/* Explicitly disable mouse events on covering element. */
/* If z-index of background object is below zero then also disable body */
/* body, */
#container, #container canvas
{ pointer-events:none;
}
/* but let enabled all other elements */
*
{ pointer-events:auto;
}
</style>
</head>
<body>
<script type="text/javascript" src="https://mrdoob.github.io/three.js/build/three.min.js"></script>
<script type="text/javascript" src="https://mrdoob.github.io/three.js/examples/js/libs/stats.min.js"></script>
<div id="background"></div>
<div id="container"></div>
<script type="text/javascript">
var lastHandledEvent;
var container, stats;
var camera, scene, projector, renderer;
var particleMaterial;
var objects = [];
// don't run DOM relevant scripts before construction of DOM is guaranteed
document.addEventListener("DOMContentLoaded", function()
{
init();
animate();
}, false);
function init() {
container = document.getElementById( 'container' );
camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 1, 10000 );
camera.position.set( 10, 300, 500 );
scene = new THREE.Scene();
var geometry = new THREE.BoxGeometry( 100, 100, 100 );
for ( var i = 0; i < 10; i ++ ) {
var object = new THREE.Mesh( geometry, new THREE.MeshBasicMaterial( { color: Math.random() * 0xffffff, opacity: 0.5 } ) );
object.position.x = Math.random() * 800 - 400;
object.position.y = Math.random() * 800 - 400;
object.position.z = Math.random() * 800 - 400;
object.scale.x = Math.random() * 2 + 1;
object.scale.y = Math.random() * 2 + 1;
object.scale.z = Math.random() * 2 + 1;
object.rotation.x = Math.random() * 2 * Math.PI;
object.rotation.y = Math.random() * 2 * Math.PI;
object.rotation.z = Math.random() * 2 * Math.PI;
scene.add( object );
objects.push( object );
}
var PI2 = Math.PI * 2;
particleMaterial = new THREE.SpriteCanvasMaterial( {
color: 0x000000,
program: function ( context ) {
context.beginPath();
context.arc( 0, 0, 0.5, 0, PI2, true );
context.fill();
}
} );
projector = new THREE.Projector();
renderer = new THREE.WebGLRenderer({ alpha: true });
renderer.setSize( window.innerWidth, window.innerHeight );
// renderer.domElement.style.position = 'absolute'; // is done in CSS
// renderer.domElement.style.zIndex = 100; // is done in CSS
container.appendChild( renderer.domElement );
// register document mousemove in capturing phase
// if you want to use another handlers subsequently
document.addEventListener('mousemove', onDocumentMouseMove, true );
document.querySelector('#background').addEventListener('mousemove', onMouseMove, false);
window.addEventListener( 'resize', onWindowResize, false );
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
var background = document.getElementById('background');
function onDocumentMouseMove( event )
{
var vector = new THREE.Vector3( ( event.clientX / window.innerWidth ) * 2 - 1, - ( event.clientY / window.innerHeight ) * 2 + 1, 0.5 );
projector.unprojectVector( vector, camera );
var raycaster = new THREE.Raycaster( camera.position, vector.sub( camera.position ).normalize() );
var intersects = raycaster.intersectObjects( objects );
if ( intersects.length > 0 )
{
intersects[ 0 ].object.material.color.setHex( Math.random() * 0xffffff );
//event.preventDefault(); // mousemove is not cancelable
// extending event objects works at least in Firefox, not sure if crossbrowser combatible
// always be careful when extending DOM
event.handled = true; // add custom property
lastHandledEvent = event; // another way: store in a variable accessible by all handlers
// to remember this one was the last handled event
console.log('event marked as handled in document listener');
//event.stopPropagation(); // or could stop propagation, however,
// often not a good praxis in conjuction with frameworks
}
else if(event.target === background)
console.log('background could be handled in document listener');
}
function onMouseMove( event )
{
// if(event.defaultPrevented // mousemove not cancable, always false
if(event.handled) // TODO: check crossbrowser compatibility
// of custom properties on event objects.
// In doubt use a var outside the functions.
console.log('other listener: event.handled is: '+event.handled);
if(lastHandledEvent === event) // the safer way: use variable accessible
{
console.log('NOT handling event in other listener');
return;
}
console.log(event.target, '...or handled in other listener');
event.target.style.backgroundColor = '#'+('00000' + (Math.random() * 0xffffff).toString(16)).slice(-6);
}
function animate() {
requestAnimationFrame( animate );
render();
}
var radius = 600;
var theta = 0;
function render() {
theta += 0.1;
camera.position.x = radius * Math.sin( THREE.Math.degToRad( theta ) );
camera.position.y = radius * Math.sin( THREE.Math.degToRad( theta ) );
camera.position.z = radius * Math.cos( THREE.Math.degToRad( theta ) );
camera.lookAt( scene.position );
renderer.render( scene, camera );
}
</script>
</body>
</html>