Source Code Given Below
1. ( index.html )
3D Particle Morphing Presented By Code Zing
html
<!DOCTYPE html>
<html lang="az">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>3D Particle Morphing @codezing</title>
</head>
<body>
<div id="canvas-container"></div>
<div class="ui-container">
<input type="text" id="text-input" maxlength="15" autocomplete="off">
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
<script>
let scene, camera, renderer, particles;
const particleCount = 40000;
let targetPositions = [];
let velocities = [];
let mouse = new THREE.Vector2(-1000, -1000);
let isTextMode = false;
const textCanvas = document.createElement('canvas');
const textCtx = textCanvas.getContext('2d');
textCanvas.width = 800;
textCanvas.height = 300;
function getHeartPoint(t, u) {
const x = 16 * Math.pow(Math.sin(u), 3) * Math.sin(t);
const y = (13 * Math.cos(u) - 5 * Math.cos(2 * u) - 2 * Math.cos(3 * u) - Math.cos(4 * u)) * Math.sin(t);
const z = 10 * Math.cos(t);
return new THREE.Vector3(x * 0.75, y * 0.75, z * 0.75);
}
function sampleText(text) {
if (!text || text.trim() === "") return null;
textCtx.clearRect(0, 0, textCanvas.width, textCanvas.height);
textCtx.fillStyle = "white";
const fontSize = text.length > 10 ? 70 : 100;
textCtx.font = `900 ${fontSize}px sans-serif`;
textCtx.textAlign = "center";
textCtx.textBaseline = "middle";
textCtx.fillText(text, textCanvas.width / 2, textCanvas.height / 2);
const imageData = textCtx.getImageData(0, 0, textCanvas.width, textCanvas.height).data;
const points = [];
for (let y = 0; y < textCanvas.height; y += 1) {
for (let x = 0; x < textCanvas.width; x += 1) {
const alpha = imageData[(y * textCanvas.width + x) * 4 + 3];
if (alpha > 150) {
points.push({
x: (x - textCanvas.width / 2) * 0.1,
y: -(y - textCanvas.height / 2) * 0.1,
z: (Math.random() - 0.5) * 1.5
});
}
}
}
return points;
}
function updateTargets(text) {
const textPoints = sampleText(text);
isTextMode = textPoints !== null && textPoints.length > 0;
// Fırlanmanı hər iki rejimdə sıfırlayırıq ki, obyekt düz qalsın
if (particles) {
particles.rotation.y = 0;
particles.rotation.x = 0;
particles.rotation.z = 0;
}
for (let i = 0; i < particleCount; i++) {
if (isTextMode) {
const target = textPoints[i % textPoints.length];
targetPositions[i] = {
x: target.x + (Math.random() - 0.5) * 0.1,
y: target.y + (Math.random() - 0.5) * 0.1,
z: target.z + (Math.random() - 0.5) * 0.1
};
} else {
const t = Math.acos(Math.random() * 2 - 1);
const u = Math.random() * Math.PI * 2;
const p = getHeartPoint(t, u);
targetPositions[i] = {
x: p.x + (Math.random()-0.5) * 0.4,
y: p.y + (Math.random()-0.5) * 0.4,
z: p.z + (Math.random()-0.5) * 0.4
};
}
}
}
function init() {
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.z = 35;
renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(window.devicePixelRatio);
document.getElementById('canvas-container').appendChild(renderer.domElement);
const geometry = new THREE.BufferGeometry();
const posArray = new Float32Array(particleCount * 3);
const colorArray = new Float32Array(particleCount * 3);
for (let i = 0; i < particleCount; i++) {
posArray[i * 3] = (Math.random() - 0.5) * 200;
posArray[i * 3 + 1] = (Math.random() - 0.5) * 200;
posArray[i * 3 + 2] = (Math.random() - 0.5) * 200;
velocities.push({x: 0, y: 0, z: 0});
const r = 0.7 + Math.random() * 0.3;
const g = 0.1 + Math.random() * 0.15;
const b = 0.3 + Math.random() * 0.25;
colorArray[i * 3] = r;
colorArray[i * 3 + 1] = g;
colorArray[i * 3 + 2] = b;
}
geometry.setAttribute('position', new THREE.BufferAttribute(posArray, 3));
geometry.setAttribute('color', new THREE.BufferAttribute(colorArray, 3));
const material = new THREE.PointsMaterial({
size: 0.12,
vertexColors: true,
transparent: true,
opacity: 0.85,
blending: THREE.AdditiveBlending
});
particles = new THREE.Points(geometry, material);
scene.add(particles);
updateTargets("");
window.addEventListener('resize', onWindowResize);
window.addEventListener('mousemove', onMouseMove);
document.getElementById('text-input').addEventListener('input', (e) => {
updateTargets(e.target.value);
});
animate();
}
function onMouseMove(event) {
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
function animate() {
requestAnimationFrame(animate);
const posAttr = particles.geometry.attributes.position;
const vector = new THREE.Vector3(mouse.x, mouse.y, 0.5);
vector.unproject(camera);
const dir = vector.sub(camera.position).normalize();
const distance = -camera.position.z / dir.z;
const mPos = camera.position.clone().add(dir.multiplyScalar(distance));
for (let i = 0; i < particleCount; i++) {
let px = posAttr.getX(i);
let py = posAttr.getY(i);
let pz = posAttr.getZ(i);
const target = targetPositions[i];
const dx = px - mPos.x;
const dy = py - mPos.y;
const dz = pz - mPos.z;
const distSq = dx*dx + dy*dy + dz*dz;
const dist = Math.sqrt(distSq);
if (dist < 8) {
const force = (8 - dist) / 8;
velocities[i].x += (dx / dist) * force * 1.8;
velocities[i].y += (dy / dist) * force * 1.8;
velocities[i].z += (dz / dist) * force * 1.8;
}
velocities[i].x += (target.x - px) * 0.08;
velocities[i].y += (target.y - py) * 0.08;
velocities[i].z += (target.z - pz) * 0.08;
velocities[i].x *= 0.78;
velocities[i].y *= 0.78;
velocities[i].z *= 0.78;
posAttr.setXYZ(i, px + velocities[i].x, py + velocities[i].y, pz + velocities[i].z);
}
posAttr.needsUpdate = true;
// Bütün rotasiya kodları silindi və ya sıfırlandı
particles.rotation.y = 0;
particles.rotation.z = 0;
particles.rotation.x = 0;
renderer.render(scene, camera);
}
window.onload = init;
</script>
</body>
</html>