codepens/beating-heart/dist/script.js

130 lines
3.3 KiB
JavaScript
Raw Normal View History

2023-10-06 23:12:53 +02:00
console.clear();
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(
75,
window.innerWidth / window.innerHeight,
0.1,
1000
);
const renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setClearColor(0xff5555);
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
camera.position.z = 1;
const controls = new THREE.TrackballControls(camera, renderer.domElement);
controls.noPan = true;
controls.maxDistance = 3;
controls.minDistance = 0.7;
const group = new THREE.Group();
scene.add(group);
let heart = null;
let sampler = null;
let originHeart = null;
new THREE.OBJLoader().load('https://assets.codepen.io/127738/heart_2.obj',obj => {
heart = obj.children[0];
heart.geometry.rotateX(-Math.PI * 0.5);
heart.geometry.scale(0.04, 0.04, 0.04);
heart.geometry.translate(0, -0.4, 0);
group.add(heart);
heart.material = new THREE.MeshBasicMaterial({
color: 0xff5555
});
originHeart = Array.from(heart.geometry.attributes.position.array);
sampler = new THREE.MeshSurfaceSampler(heart).build();
init();
renderer.setAnimationLoop(render);
});
let positions = [];
const geometry = new THREE.BufferGeometry();
const material = new THREE.LineBasicMaterial({
color: 0xffffff
});
const lines = new THREE.LineSegments(geometry, material);
group.add(lines);
const simplex = new SimplexNoise();
const pos = new THREE.Vector3();
class Grass {
constructor () {
sampler.sample(pos);
this.pos = pos.clone();
this.scale = Math.random() * 0.01 + 0.001;
this.one = null;
this.two = null;
}
update (a) {
const noise = simplex.noise4D(this.pos.x*1.5, this.pos.y*1.5, this.pos.z*1.5, a * 0.0005) + 1;
this.one = this.pos.clone().multiplyScalar(1.01 + (noise * 0.15 * beat.a));
this.two = this.one.clone().add(this.one.clone().setLength(this.scale));
}
}
let spikes = [];
function init (a) {
positions = [];
for (let i = 0; i < 20000; i++) {
const g = new Grass();
spikes.push(g);
}
}
const beat = { a: 0 };
gsap.timeline({
repeat: -1,
repeatDelay: 0.3
}).to(beat, {
a: 1.2,
duration: 0.6,
ease: 'power2.in'
}).to(beat, {
a: 0.0,
duration: 0.6,
ease: 'power3.out'
});
gsap.to(group.rotation, {
y: Math.PI * 2,
duration: 12,
ease: 'none',
repeat: -1
});
function render(a) {
positions = [];
spikes.forEach(g => {
g.update(a);
positions.push(g.one.x, g.one.y, g.one.z);
positions.push(g.two.x, g.two.y, g.two.z);
});
geometry.setAttribute('position', new THREE.BufferAttribute(new Float32Array(positions), 3));
const vs = heart.geometry.attributes.position.array;
for (let i = 0; i < vs.length; i+=3) {
const v = new THREE.Vector3(originHeart[i], originHeart[i+1], originHeart[i+2]);
const noise = simplex.noise4D(originHeart[i]*1.5, originHeart[i+1]*1.5, originHeart[i+2]*1.5, a * 0.0005) + 1;
v.multiplyScalar(1 + (noise * 0.15 * beat.a));
vs[i] = v.x;
vs[i+1] = v.y;
vs[i+2] = v.z;
}
heart.geometry.attributes.position.needsUpdate = true;
controls.update();
renderer.render(scene, camera);
}
window.addEventListener("resize", onWindowResize, false);
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}