JavaScript给2D坐标加默认高度创建3D对象问题排查及替代方案咨询
Hey there! Let's break down what's going wrong with your Three.js code and fix it step by step, then I'll cover alternative ways to create a 3D-like shape without Three.js.
Key Issues in Your Code
- Scope & Incorrect
THREE.ShapeSetup: You're definingshapeinside your for loop, so it's not accessible in theinit()function. Also,THREE.Shapedoesn't accept a flat array of[x1,y1,x2,y2,...]directly—you need to build the path usingmoveTo()andlineTo()with individual points, or pass an array ofTHREE.Vector2objects. - Unnecessary 2D Canvas Code: The
ctx.moveTo()/ctx.lineTo()part is for 2D canvas rendering, which has nothing to do with your Three.js 3D model. It's also causing potential array out-of-bounds errors wheni+2exceeds the length ofcoords. - Undefined
mycanvasVariable: You're passingmycanvastoTHREE.WebGLRendererbut never defining it—this will cause a renderer initialization failure. - Camera Position Not Set: The default camera position is
(0,0,0), which is inside your extruded mesh, so you won't see anything. You need to move it back along the Z-axis. - Missing Semicolons & Minor Syntax: Some lines (like
dirLight.shadowMapHeight = 2048) are missing semicolons, which can lead to unexpected behavior.
Fixed Three.js Code
$(document).ready(function(){ // Store coordinates as THREE.Vector2 objects instead of flat values function storeCoordinate(x, y, array) { array.push(new THREE.Vector2(x, y)); } var coords = []; storeCoordinate(3, 5, coords); storeCoordinate(10, 100, coords); storeCoordinate(30, 120, coords); storeCoordinate(3, 5, coords); // Close the shape // Create the Shape correctly by building the path var shape = new THREE.Shape(); // Move to the first point shape.moveTo(coords[0].x, coords[0].y); // Draw lines to subsequent points for (var i = 1; i < coords.length; i++) { shape.lineTo(coords[i].x, coords[i].y); } var scene, camera, renderer, extrudedMesh, controls; init(); animate(); function init(){ scene = new THREE.Scene(); // Set up camera and move it back camera = new THREE.PerspectiveCamera(75, window.innerWidth/window.innerHeight, 1, 1000); camera.position.z = 300; // Move camera away from the mesh // Create extruded geometry from the shape var extrudedGeometry = new THREE.ExtrudeGeometry(shape, { amount: 5, bevelEnabled: false }); extrudedMesh = new THREE.Mesh( extrudedGeometry, new THREE.MeshPhongMaterial({color: 0xff0000}) ); scene.add(extrudedMesh); // Mouse rotation (fixed to use correct event properties) document.body.onmousemove = function(e){ extrudedMesh.rotation.z = (e.clientX / window.innerWidth) * Math.PI * 2; extrudedMesh.rotation.x = (e.clientY / window.innerHeight) * Math.PI * 2; } // Lights setup const dirLight = new THREE.DirectionalLight(0xffffff); dirLight.intensity = 0.9; dirLight.position.set(500, 140, 500); dirLight.castShadow = true; dirLight.shadowMapHeight = 2048; dirLight.shadowMapWidth = 2048; dirLight.shadowDarkness = 0.15; scene.add(dirLight); const spotLight = new THREE.PointLight(0xffffff); spotLight.intensity = 0.5; spotLight.position.set(-500, 140, -500); scene.add(spotLight); const ambientLight = new THREE.AmbientLight(0x020202, 1); scene.add(ambientLight); // Renderer setup (either use existing canvas or let Three.js create one) renderer = new THREE.WebGLRenderer({antialias: true}); renderer.setSize(window.innerWidth, window.innerHeight); document.body.appendChild(renderer.domElement); // OrbitControls setup (make sure you've included the OrbitControls script) controls = new THREE.OrbitControls(camera, renderer.domElement); controls.autoRotate = true; controls.enableZoom = true; controls.enablePan = true; controls.rotateSpeed = 3.0; controls.zoomSpeed = 1.0; controls.panSpeed = 2.0; controls.enableDamping = true; controls.dampingFactor = 0.25; controls.minDistance = 1.1; controls.maxDistance = 1000; controls.keys = [65, 83, 68]; // [ rotateKey, zoomKey, panKey ] } function animate() { window.requestAnimationFrame(animate); controls.update(); // Update controls for damping render(); } function render() { renderer.render(scene, camera); } });
What Changed:
- Coordinate Storage: We now store each point as a
THREE.Vector2to make building the shape easier. - Shape Construction: We manually build the shape path with
moveTo()andlineTo()instead of passing a flat array. - Removed 2D Canvas Code: We got rid of the irrelevant 2D canvas rendering logic.
- Defined Camera Position: Moved the camera back along the Z-axis so you can see the mesh.
- Fixed Renderer Initialization: Let Three.js create its own canvas (you can still attach it to an existing one if needed).
- Added
controls.update(): Required for OrbitControls damping to work. - Cleaned Up Light Setup: Fixed semicolons and added
scene.add()for lights where missing.
Alternatives Without Three.js
If you want to create a 3D-like effect without using Three.js, here are two simple options:
1. CSS 3D Transforms
You can use CSS perspective, transform-style: preserve-3d, and rotateX/rotateY to create a pseudo-3D shape. For example, extruding a 2D div into a 3D prism:
<style> .container { perspective: 800px; width: 100vw; height: 100vh; display: flex; justify-content: center; align-items: center; } .prism { position: relative; transform-style: preserve-3d; animation: rotate 10s infinite linear; } .face { position: absolute; background: rgba(255, 0, 0, 0.8); border: 1px solid #000; } /* Front face (your 2D shape) */ .front { width: 200px; height: 150px; transform: translateZ(2.5px); /* Half the extrusion depth */ clip-path: polygon(3px 5px, 10px 100px, 30px 120px, 3px 5px); } /* Back face */ .back { width: 200px; height: 150px; transform: translateZ(-2.5px) rotateY(180deg); clip-path: polygon(3px 5px, 10px 100px, 30px 120px, 3px 5px); } /* Side face example */ .side1 { width: 5px; height: 150px; transform: rotateY(90deg) translateZ(3px); left: -2.5px; } @keyframes rotate { from { transform: rotateY(0) rotateX(0); } to { transform: rotateY(360deg) rotateX(360deg); } } </style> <div class="container"> <div class="prism"> <div class="face front"></div> <div class="face back"></div> <div class="face side1"></div> <!-- Add other side faces based on your shape's edges --> </div> </div>
Note: This works best for simple shapes, as you have to manually create each side face.
2. 2D Canvas with Pseudo-3D Drawing
You can use 2D canvas to draw a "flat 3D" representation by projecting your 2D points into a pseudo-3D space:
const canvas = document.getElementById('myCanvas'); const ctx = canvas.getContext('2d'); canvas.width = window.innerWidth; canvas.height = window.innerHeight; // Your 2D coordinates const coords = [ {x: 3, y: 5}, {x: 10, y: 100}, {x: 30, y: 120}, {x: 3, y: 5} ]; // Extrusion depth (Z-axis offset) const depth = 20; // Perspective factor const perspective = 0.1; function drawPseudo3DShape() { ctx.clearRect(0, 0, canvas.width, canvas.height); ctx.translate(canvas.width/2, canvas.height/2); // Center the shape // Draw front face ctx.fillStyle = 'rgba(255,0,0,0.8)'; ctx.beginPath(); ctx.moveTo(coords[0].x, coords[0].y); for(let i=1; i<coords.length; i++) { ctx.lineTo(coords[i].x, coords[i].y); } ctx.fill(); // Draw back face (offset by depth, scaled for perspective) ctx.fillStyle = 'rgba(200,0,0,0.5)'; ctx.beginPath(); ctx.moveTo(coords[0].x + depth*perspective, coords[0].y + depth*perspective); for(let i=1; i<coords.length; i++) { ctx.lineTo(coords[i].x + depth*perspective, coords[i].y + depth*perspective); } ctx.fill(); // Draw connecting lines between front and back points ctx.strokeStyle = '#000'; ctx.lineWidth = 2; for(let i=0; i<coords.length-1; i++) { ctx.beginPath(); ctx.moveTo(coords[i].x, coords[i].y); ctx.lineTo(coords[i].x + depth*perspective, coords[i].y + depth*perspective); ctx.stroke(); } } drawPseudo3DShape();
This creates a simple flat 3D illusion without any 3D libraries.
内容的提问来源于stack exchange,提问作者Daniel Bisceanu

