leaf-ai-app / assets /js /three_leaf.js
Hphuccoder28's picture
Deploy LEAF_AI Botanical Recruiter & Diagnostic SPA
f67ba6f verified
Raw History Blame Contribute Delete
4.51 kB
/**
* LEAF_AI - Natural Botanical 3D Leaf Visualizer (Three.js)
* Mô phỏng lá cây 3D tự nhiên, mềm mại, màu xanh thực vật tự nhiên (không chói neon).
*/
class Leaf3DVisualizer {
constructor(containerId) {
this.container = document.getElementById(containerId);
if (!this.container || typeof THREE === 'undefined') {
return;
}
this.scene = null;
this.camera = null;
this.renderer = null;
this.leafMesh = null;
this.veinMesh = null;
this.animId = null;
this.isScanning = false;
this.init();
}
init() {
const width = this.container.clientWidth || 200;
const height = this.container.clientHeight || 240;
// Scene & Camera
this.scene = new THREE.Scene();
this.camera = new THREE.PerspectiveCamera(45, width / height, 0.1, 1000);
this.camera.position.set(0, 0, 18);
// Renderer nền trong suốt
this.renderer = new THREE.WebGLRenderer({ alpha: true, antialias: true });
this.renderer.setSize(width, height);
this.renderer.setPixelRatio(window.devicePixelRatio || 1);
this.container.innerHTML = '';
this.container.appendChild(this.renderer.domElement);
// Ánh sáng tự nhiên mềm mại
const ambientLight = new THREE.AmbientLight(0xffffff, 1.2);
this.scene.add(ambientLight);
const sunLight = new THREE.DirectionalLight(0xfef08a, 1.5);
sunLight.position.set(5, 10, 8);
this.scene.add(sunLight);
const bounceLight = new THREE.DirectionalLight(0x86efac, 0.8);
bounceLight.position.set(-5, -5, 5);
this.scene.add(bounceLight);
// Dáng lá cây cà chua tự nhiên
const shape = new THREE.Shape();
shape.moveTo(0, -6);
shape.bezierCurveTo(2.8, -4, 4.2, 0, 3.2, 4);
shape.bezierCurveTo(2.2, 6, 0.8, 7.5, 0, 8);
shape.bezierCurveTo(-0.8, 7.5, -2.2, 6, -3.2, 4);
shape.bezierCurveTo(-4.2, 0, -2.8, -4, 0, -6);
const extrudeSettings = {
depth: 0.25,
bevelEnabled: true,
bevelSegments: 4,
steps: 2,
bevelSize: 0.15,
bevelThickness: 0.15
};
const geometry = new THREE.ExtrudeGeometry(shape, extrudeSettings);
geometry.center();
// Chất liệu màu xanh lá cây tự nhiên (Organic Leaf Green)
const leafMaterial = new THREE.MeshStandardMaterial({
color: 0x16a34a,
roughness: 0.35,
metalness: 0.05,
flatShading: false
});
this.leafMesh = new THREE.Mesh(geometry, leafMaterial);
this.scene.add(this.leafMesh);
// Gân lá chính màu vàng chanh nhạt tự nhiên
const veinCurve = new THREE.LineCurve3(
new THREE.Vector3(0, -5.8, 0.22),
new THREE.Vector3(0, 7.6, 0.22)
);
const veinGeom = new THREE.TubeGeometry(veinCurve, 20, 0.1, 8, false);
const veinMat = new THREE.MeshStandardMaterial({ color: 0x86efac, roughness: 0.4 });
this.veinMesh = new THREE.Mesh(veinGeom, veinMat);
this.scene.add(this.veinMesh);
// Tương tác xoay nhẹ bằng chuột
let isDragging = false;
let prevMousePos = { x: 0, y: 0 };
this.container.addEventListener('mousedown', (e) => {
isDragging = true;
prevMousePos = { x: e.clientX, y: e.clientY };
});
window.addEventListener('mouseup', () => isDragging = false);
window.addEventListener('mousemove', (e) => {
if (!isDragging || !this.leafMesh) return;
const dx = e.clientX - prevMousePos.x;
const dy = e.clientY - prevMousePos.y;
this.leafMesh.rotation.y += dx * 0.01;
this.veinMesh.rotation.y += dx * 0.01;
this.leafMesh.rotation.x += dy * 0.01;
this.veinMesh.rotation.x += dy * 0.01;
prevMousePos = { x: e.clientX, y: e.clientY };
});
this.animate();
}
animate() {
this.animId = requestAnimationFrame(() => this.animate());
const rotSpeed = this.isScanning ? 0.025 : 0.005;
if (this.leafMesh && this.veinMesh) {
this.leafMesh.rotation.y += rotSpeed;
this.veinMesh.rotation.y += rotSpeed;
}
this.renderer.render(this.scene, this.camera);
}
setScanning(isScanning) {
this.isScanning = isScanning;
if (this.leafMesh) {
this.leafMesh.material.color.setHex(isScanning ? 0xf59e0b : 0x16a34a);
}
}
resize() {
if (!this.container || !this.renderer || !this.camera) return;
const w = this.container.clientWidth;
const h = this.container.clientHeight;
this.camera.aspect = w / h;
this.camera.updateProjectionMatrix();
this.renderer.setSize(w, h);
}
}