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

使用ScrollControls时Html组件无法固定位置的解决咨询

解决React Three Fiber中Html组件在ScrollControls下无法固定位置的问题

核心原因

你把Html组件放在Mesh内部,ScrollControls的滚动会驱动整个3D场景的变换(比如相机位移、场景偏移),再加上transformGroup属性让Html继承了Mesh的3D变换——这会干扰CSS position: fixed的生效逻辑:fixed定位本应相对于浏览器视口,但3D变换会创建新的包含块,导致Html跟着场景滚动。而Decal是直接依附在Mesh表面的3D组件,自然能正常跟随Mesh动作。

解决方案

方案1:将Html组件移出ScrollControls层级

这是最直接的解决方法,把Html放在ScrollControls外部,脱离3D场景的滚动变换影响:

// 放在ScrollControls外层,独立于3D场景滚动逻辑
<Html
  portal={{ current: gl.domElement.parentNode }}
  scale={100}
>
  <div
    className="about-wall about-wall-text"
    style={{
      width: '40rem',
      position: 'fixed',
      top: '50%',
      left: '50%',
      transform: 'translate(-50%, -50%)',
      willChange: 'transform, opacity',
      pointerEvents: 'none',
    }}
  >
    <h2>About Us</h2>
  </div>
</Html>

<ScrollControls hideScrollbar={true} pages={35} distance={1} damping={0.5}>
  <Newmodel isMobile={isMobile} />
</ScrollControls>

方案2:保留在Mesh内但取消3D变换继承

如果必须让Html和Mesh关联展示,需要去掉transformGroup,并通过useFrame手动控制Html位置,确保它始终对齐视口固定点:

import { useFrame, useThree } from '@react-three/fiber'
import { Html } from '@react-three/drei'
import { useState, useRef } from 'react'
import { Vector3 } from 'three'

function FixedHtmlOnMesh() {
  const { camera, gl } = useThree()
  const htmlRef = useRef(null)

  useFrame(() => {
    if (!htmlRef.current) return
    // 将视口中心坐标转换为3D空间坐标
    const viewportCenter = new Vector3(0, 0, 0.5)
    viewportCenter.unproject(camera)
    // 更新Html位置到视口中心对应的3D点
    htmlRef.current.position.copy(viewportCenter)
    // 让Html始终朝向相机,避免视角翻转导致文字反向
    htmlRef.current.lookAt(camera.position)
  })

  return (
    <Html
      ref={htmlRef}
      portal={{ current: gl.domElement.parentNode }}
      scale={100}
      // 关闭transformGroup,避免继承Mesh的3D变换
    >
      <div
        className="about-wall about-wall-text"
        style={{
          width: '40rem',
          position: 'relative',
          willChange: 'transform, opacity',
          pointerEvents: 'none',
        }}
      >
        <h2>About Us</h2>
      </div>
    </Html>
  )
}

// 在Mesh组件内引入该固定Html组件
function Newmodel({ isMobile }) {
  return (
    <mesh>
      {/* ...你的Mesh模型代码... */}
      <FixedHtmlOnMesh />
    </mesh>
  )
}

方案3:调整CSS定位方式

如果不想改动组件层级,尝试去掉position: fixed,改用position: absolute基于canvas元素定位:

<Html
  portal={{ current: gl.domElement.parentNode }}
  scale={100}
  transformGroup={false} // 关闭3D变换继承
>
  <div
    className="about-wall about-wall-text"
    style={{
      width: '40rem',
      position: 'absolute',
      top: '50%',
      left: '50%',
      transform: 'translate(-50%, -50%)',
      willChange: 'transform, opacity',
      pointerEvents: 'none',
      zIndex: 10, // 确保显示在canvas上方
    }}
  >
    <h2>About Us</h2>
  </div>
</Html>

关键注意点

  • 不要在ScrollControls内部的3D元素中嵌套需要固定定位的Html,ScrollControls的滚动逻辑会修改场景/相机变换,必然带动内部Html移动。
  • transformGroup属性会让Html完全继承父级3D元素的变换,这和CSS固定定位逻辑冲突,需根据实际需求决定是否开启。

内容的提问来源于stack exchange,提问作者Portgas D. Ace

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 18:35:15