You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Three.js渲染Canvas透镜效果下透明显示底层HTML及滚动异常问题咨询

React+Three.js Lens Effect: Fixes & Prototype Adjustment Advice

Hey there! Let's work through your lens effect issues step by step, and also talk about whether you might need to tweak your Figma prototype implementation.


Problem 1: Blue Area Above Lens Moves Erratically On Scroll

Root Cause

Your current setup has the <Canvas> element in the normal document flow, so when you scroll the page, the Canvas shifts position relative to the fixed-position gray sidebar. The lens rendering logic doesn't account for this scroll-induced positional shift, leading to the visual mismatch with the blue background area.

Fix

Adjust the Canvas to use fixed positioning so it stays anchored to the viewport, matching the fixed sidebar's behavior. This keeps the lens and surrounding elements aligned during scroll:

<Canvas
  camera={{
    position: [0, 0, 20],
    fov: 15,
    near: 0.1,
    far: 1000,
  }}
  style={{ 
    width: '100%', 
    height: '100px', 
    display: 'block',
    position: 'fixed', // Add fixed positioning
    top: '200px', // Match the height of the blue div above it
    left: 0,
    zIndex: 1 // Ensure it sits above underlying content
  }}
  gl={{ alpha: true }}
>
  {/* ... rest of your Canvas content ... */}
</Canvas>

You'll also need to adjust the spacing of the content below the Canvas to avoid overlap (fixed elements are removed from the document flow):

<div style={{ backgroundColor: '#cbdbd5', height: '200px', marginTop: '100px' }}>
  <p style={{ backgroundColor: '#cbdbd5', padding: '0', margin: '0' }}>此处Canvas下方可见背景</p>
</div>

Problem 2: Lens Area Is Black, Can't See Underlying HTML Content

Root Cause

Right now, your lens only renders the text content from your offscreen FBO, with no mechanism to capture or display the underlying HTML elements through the lens. The black area comes from the FBO's clear color combined with the material not having access to the page's HTML content.

Fix

To make the lens show the underlying HTML, you need to capture the page's content as a texture and feed it into your MeshTransmissionMaterial. Here's how to implement this:

  1. Add a function to capture page content to a canvas
function capturePageContent() {
  const canvas = document.createElement('canvas');
  const ctx = canvas.getContext('2d');
  canvas.width = window.innerWidth;
  canvas.height = window.innerHeight;
  
  // Capture the entire document body
  ctx.drawImage(document.body, 0, 0, canvas.width, canvas.height);
  return canvas;
}
  1. Update the Lens component to use this captured texture
    Modify the Lens component to create and update a texture from the captured page content, then pass it to the transmission material:
import { useEffect } from 'react';

function Lens({ children, ...props }) {
  const ref = useRef();
  const { nodes } = useGLTF('/my-lens2.glb');
  const buffer = useFBO();
  const viewport = useThree((state) => state.viewport);
  const [scene] = useState(() => new THREE.Scene());
  const textureRef = useRef(null);

  // Initialize and update page capture texture
  useEffect(() => {
    const canvas = capturePageContent();
    const texture = new THREE.CanvasTexture(canvas);
    textureRef.current = texture;
    
    // Update texture on scroll/resize to keep it in sync
    const updateTexture = () => {
      const newCanvas = capturePageContent();
      textureRef.current.image = newCanvas;
      textureRef.current.needsUpdate = true;
    };
    
    window.addEventListener('scroll', updateTexture);
    window.addEventListener('resize', updateTexture);
    
    return () => {
      window.removeEventListener('scroll', updateTexture);
      window.removeEventListener('resize', updateTexture);
    };
  }, []);

  useFrame((state) => {
    state.gl.setRenderTarget(buffer);
    state.gl.setClearColor('#000000', 0);
    state.gl.clear();
    state.gl.render(scene, state.camera);
    state.gl.setRenderTarget(null);
  });

  return (
    <>
      {createPortal(children, scene)}
      <mesh
        ref={ref}
        scale={5}
        rotation-x={Math.PI / 2}
        geometry={nodes.Cylinder.geometry}
        position={[0, 0.7, 0]}
        {...props}
      >
        <MeshTransmissionMaterial
          buffer={buffer.texture} // Keep your text overlay
          backside={textureRef.current} // Add captured page content as backside
          ior={1.2}
          thickness={1.5}
          anisotropy={0.1}
          chromaticAberration={0.04}
          color={'#fcfcfc'}
          transparent={true}
          roughness={0}
        />
      </mesh>
    </>
  );
}

Note: Real-time page capture can cause performance lag on large pages. Optimize by throttling scroll/resize updates or capturing only the region under the lens instead of the entire page.


Prototype Implementation Adjustment Advice

Whether you need to adjust your Figma prototype depends on your performance goals and complexity:

  • Keep current HTML + Three.js setup: Works for simple underlying content. Just optimize the page capture logic to avoid lag.
  • Switch to full Three.js rendering: If your content can be recreated in Three.js (text, simple shapes), this gives better performance and cleaner lens effects—no HTML capture needed.
  • CSS-only simulation: If complex 3D refraction isn't critical, use clip-path, perspective(), and filter to mimic lens distortion. This is lighter weight and avoids Three.js entirely.

内容的提问来源于stack exchange,提问作者Roma N

火山引擎 最新活动