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

能否实现3D/动态透视路径上的无限跑马灯效果?

动态透视无限跑马灯实现方案

问题背景

我要做一个动态透视无限跑马灯效果,参考的是带3D透视的滚动样式,透视网格的效果有对应截图。之前改编了一个有类似3D透视效果的Codepen示例,但把原示例的鼠标X轴控制改成自动无限滚动的尝试全失败了,原示例的核心transform代码如下:

.left-3d {
  position: absolute;
  transform-origin: right center;
  transform: rotateY(100deg);
  top: 0;
  bottom: 0;
  right: 100%;
}

#left {
  transform: translateX(0%);
}

#center {
  transform: translateX(-100%);
}

.right-3d {
  position: absolute;
  transform-origin: left center;
  transform: rotateY(-100deg);
  top: 0;
  bottom: 0;
  left: 100%;
}

#right {
  transform: translateX(-200%);
}

目前用原生HTML/CSS/JS开发,有人说用Three.js或React Three Fiber能轻松实现,但我完全没接触过这类工具,求具体的实现思路。


两种可行的实现思路

思路1:原生CSS/JS改造现有示例

原示例是鼠标控制视角,改成无限滚动的核心是循环复制内容+持续更新偏移量:

  1. 内容无缝衔接准备
    把跑马灯的核心内容复制至少2份,放在同一个父容器里,确保滚动到末尾时能衔接上另一份内容,不会出现空白。比如原内容是.marquee-item,复制成.marquee-item.clone。
  2. 持续滚动动画逻辑
    用requestAnimationFrame做帧动画,持续更新内容的translateX值,当偏移量达到单个内容宽度的负数时,重置偏移量实现无限循环。3D透视的rotateY和transform-origin保留原示例的设置,只修改偏移逻辑:
    const contentList = document.querySelectorAll('.marquee-item');
    let offset = 0;
    const scrollSpeed = 0.4; // 滚动速度可自行调整
    
    function runMarquee() {
      offset -= scrollSpeed;
      // 当内容完全滚出视野,重置偏移量
      if (offset <= -contentList[0].offsetWidth) {
        offset += contentList[0].offsetWidth;
      }
      // 给所有内容项设置偏移
      contentList.forEach(item => {
        item.style.transform = `translateX(${offset}px)`;
      });
      requestAnimationFrame(runMarquee);
    }
    runMarquee();
    
  3. 透视容器适配
    父容器必须开启perspective属性,否则3D旋转效果无法生效:
    .marquee-container {
      perspective: 800px;
      overflow: hidden;
      position: relative;
      width: 100%;
      height: 200px;
    }
    

思路2:Three.js快速实现(新手友好版)

如果用Three.js,核心是实现滚动的重复纹理+透视相机角度,步骤清晰易上手:

  1. 基础环境搭建
    先引入Three.js的CDN资源:
    <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r158/three.min.js"></script>
    
  2. 创建滚动纹理与平面
    准备一张跑马灯内容的图片(比如文字排成一行的透明背景PNG),设置纹理为重复模式,确保可以无限滚动:
    // 初始化场景、相机、渲染器
    const scene = new THREE.Scene();
    const camera = new THREE.PerspectiveCamera(75, window.innerWidth/window.innerHeight, 0.1, 1000);
    const renderer = new THREE.WebGLRenderer({ antialias: true });
    renderer.setSize(window.innerWidth, window.innerHeight);
    document.body.appendChild(renderer.domElement);
    
    // 加载纹理并设置重复属性
    const textureLoader = new THREE.TextureLoader();
    const marqueeTex = textureLoader.load('你的跑马灯图片路径.png');
    marqueeTex.wrapS = THREE.RepeatWrapping;
    marqueeTex.repeat.x = 3; // 重复3次足够实现无缝滚动
    
    // 创建带纹理的平面网格
    const planeGeo = new THREE.PlaneGeometry(12, 2);
    const planeMat = new THREE.MeshBasicMaterial({ map: marqueeTex, side: THREE.DoubleSide });
    const marqueePlane = new THREE.Mesh(planeGeo, planeMat);
    scene.add(marqueePlane);
    
    camera.position.z = 6;
    
  3. 实现滚动与透视效果
    在动画循环中更新纹理的offset.x值实现滚动,调整平面的rotation.y模拟透视角度:
    function animate() {
      requestAnimationFrame(animate);
      // 纹理滚动速度,数值越小滚动越慢
      marqueeTex.offset.x -= 0.003;
      // 透视角度,可根据需求调整
      marqueePlane.rotation.y = Math.PI / 7;
      renderer.render(scene, camera);
    }
    animate();
    
    如果要模拟左右两侧的透视面板,只需多创建几个平面,分别设置不同的rotation.y和position即可。

新手注意点

  • 原生方案中,内容复制的数量要至少等于容器能显示的内容数量+1,避免滚动时出现空白。
  • Three.js方案中,图片要提前加载完成,或者用textureLoader.load的回调函数启动动画,防止纹理未加载导致的黑屏问题。
  • 不管哪种方案,都要多调整参数(滚动速度、透视角度、容器尺寸),直到效果贴近预期。

内容的提问来源于stack exchange,提问作者Ciaran Gutteridge

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 01:00:58