130 lines
3.3 KiB
JavaScript
130 lines
3.3 KiB
JavaScript
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);
|
|
} |