Three.js渲染Canvas透镜效果下透明显示底层HTML及滚动异常问题咨询
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:
- 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; }
- Update the Lens component to use this captured texture
Modify theLenscomponent 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(), andfilterto mimic lens distortion. This is lighter weight and avoids Three.js entirely.
内容的提问来源于stack exchange,提问作者Roma N




