You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

three.js中iframe鼠标事件异常求助:坐标转换错误

CSS3DRenderer嵌入iframe的鼠标坐标错位问题解决

问题

点击事件正常工作,但拖拽iframe内元素(如视频时间轴)时,鼠标x坐标为实际值的两倍;在其他嵌入网站中,选中文本复制会因y坐标偏移导致页面滚到底部。

复现代码

var camera, scene, renderer;
var controls;

var Element = function ( id, x, y, z, ry ) {
  var div = document.createElement( 'div' );
  div.style.width = '480px';
  div.style.height = '360px';
  div.style.backgroundColor = '#000';
  var iframe = document.createElement( 'iframe' );
  iframe.style.width = '480px';
  iframe.style.height = '360px';
  iframe.style.border = '0px';
  iframe.src = [ 'https://www.youtube.com/embed/', id, '?rel=0'   ].join( '' );
  div.appendChild( iframe );
  var object = new THREE.CSS3DObject( div );
  object.position.set( x, y, z );
  object.rotation.y = ry;
  return object;
};

init();
animate();
function init() {
  var container = document.getElementById( 'container' );
    camera = new THREE.PerspectiveCamera( 50, window.innerWidth / window.innerHeight, 1, 5000 );
    camera.position.set( 500, 350, 750 );
    scene = new THREE.Scene();
    renderer = new THREE.CSS3DRenderer();
    renderer.setSize( window.innerWidth, window.innerHeight );
    renderer.domElement.style.position = 'absolute';
    renderer.domElement.style.top = 0;
    container.appendChild( renderer.domElement );
    var group = new THREE.Group();
    group.add( new Element( 'xBOqwRRj82A', 0, 0, 240, 0 ) );
    group.add( new Element( 'x4q86IjJFag', 240, 0, 0, Math.PI / 2 ) );
    group.add( new Element( 'JhngfOK_2-0', 0, 0, - 240, Math.PI ) );
    group.add( new Element( 'Grg3461lAPg', - 240, 0, 0, - Math.PI / 2 ) );
    scene.add( group );
    controls = new THREE.TrackballControls( camera );
    controls.rotateSpeed = 4;
    window.addEventListener( 'resize', onWindowResize, false );

    // Block iframe events when dragging camera
    var blocker = document.getElementById( 'blocker' );
    blocker.style.display = 'none';
    document.addEventListener( 'mousedown', function () { blocker.style.display = ''; } );
    document.addEventListener( 'mouseup', function () { blocker.style.display = 'none'; } );
    }

    function onWindowResize() {
      camera.aspect = window.innerWidth / window.innerHeight;
      camera.updateProjectionMatrix();
      renderer.setSize( window.innerWidth, window.innerHeight );
    }
    function animate() {
      requestAnimationFrame( animate );
      controls.update();
        renderer.render( scene, camera );
    }

核心原因

THREE.CSS3DRenderer通过CSS transform对DOM元素进行缩放、旋转变换,但iframe的鼠标事件坐标基于原始文档流坐标,不会自动适配CSS3D变换后的空间,导致坐标映射错位。

解决方案

1. 修正鼠标坐标映射

给每个iframe添加事件监听,将页面坐标转换为CSS3D变换后的局部坐标:

// 修改Element构造函数,添加坐标修正逻辑
var Element = function ( id, x, y, z, ry ) {
  var div = document.createElement( 'div' );
  div.style.width = '480px';
  div.style.height = '360px';
  div.style.backgroundColor = '#000';
  var iframe = document.createElement( 'iframe' );
  iframe.style.width = '480px';
  iframe.style.height = '360px';
  iframe.style.border = '0px';
  iframe.src = [ 'https://www.youtube.com/embed/', id, '?rel=0'   ].join( '' );
  
  // 监听鼠标事件并修正坐标
  const handleMouseEvent = (e) => {
    e.preventDefault();
    const rect = iframe.getBoundingClientRect();
    const parentStyle = window.getComputedStyle(div);
    const transformMatrix = new DOMMatrix(parentStyle.transform);
    
    // 计算修正后的坐标(抵消缩放影响)
    const correctedX = (e.clientX - rect.left) / transformMatrix.a;
    const correctedY = (e.clientY - rect.top) / transformMatrix.d;
    
    // 触发修正后的事件
    const correctedEvent = new MouseEvent(e.type, {
      clientX: correctedX,
      clientY: correctedY,
      bubbles: e.bubbles,
      cancelable: e.cancelable,
      view: window
    });
    iframe.dispatchEvent(correctedEvent);
  };
  
  // 绑定需要修正的事件(根据需求添加mousedown/mousemove/mouseup等)
  iframe.addEventListener('mousedown', handleMouseEvent);
  iframe.addEventListener('mousemove', handleMouseEvent);
  iframe.addEventListener('mouseup', handleMouseEvent);
  
  div.appendChild( iframe );
  var object = new THREE.CSS3DObject( div );
  object.position.set( x, y, z );
  object.rotation.y = ry;
  
  // 保存object引用,用于旋转场景的坐标修正
  iframe.dataset.object = object;
  return object;
};

2. 处理3D旋转场景

如果元素有旋转(如示例中的y轴旋转),需添加旋转变换修正:

// 修改handleMouseEvent函数,添加旋转修正
const handleMouseEvent = (e) => {
  e.preventDefault();
  const rect = iframe.getBoundingClientRect();
  const parentStyle = window.getComputedStyle(div);
  const transformMatrix = new DOMMatrix(parentStyle.transform);
  
  // 先计算局部坐标
  const localX = e.clientX - rect.left;
  const localY = e.clientY - rect.top;
  
  // 处理旋转:将坐标转换为元素未旋转时的原始位置
  const object = iframe.dataset.object;
  const rotationMatrix = new THREE.Matrix3().makeRotationY(object.rotation.y);
  const vec = new THREE.Vector2(localX, localY).applyMatrix3(rotationMatrix.invert());
  
  // 抵消缩放影响
  const correctedX = vec.x / transformMatrix.a;
  const correctedY = vec.y / transformMatrix.d;
  
  // 触发修正事件
  const correctedEvent = new MouseEvent(e.type, {
    clientX: correctedX,
    clientY: correctedY,
    bubbles: e.bubbles,
    cancelable: e.cancelable,
    view: window
  });
  iframe.dispatchEvent(correctedEvent);
};

3. 优化相机拖拽的事件拦截

原全局事件拦截逻辑会干扰iframe交互,改为监听TrackballControls的状态:

// 替换原有的blocker逻辑
var blocker = document.getElementById( 'blocker' );
blocker.style.display = 'none';
controls.addEventListener('start', () => { blocker.style.display = 'block'; });
controls.addEventListener('end', () => { blocker.style.display = 'none'; });

4. 窗口Resize适配

在窗口大小变化时,重新计算元素变换参数,避免坐标错位:

function onWindowResize() {
  camera.aspect = window.innerWidth / window.innerHeight;
  camera.updateProjectionMatrix();
  renderer.setSize(window.innerWidth, window.innerHeight);
  
  // 遍历所有CSS3DObject,更新变换参数缓存
  scene.traverse(obj => {
    if (obj instanceof THREE.CSS3DObject && obj.children[0].tagName === 'IFRAME') {
      const iframe = obj.children[0];
      const rect = iframe.getBoundingClientRect();
      iframe.dataset.scaleX = rect.width / 480;
      iframe.dataset.scaleY = rect.height / 360;
    }
  });
}

内容的提问来源于stack exchange,提问作者Сергей Камбур

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 00:05:21