230 lines
6.5 KiB
JavaScript
230 lines
6.5 KiB
JavaScript
/*--------------------
|
|
Vars
|
|
--------------------*/
|
|
let ball;
|
|
let pattern;
|
|
let palettes;
|
|
const win = { w: window.innerWidth, h: window.innerHeight };
|
|
const mouse = { x: win.w * 0.5, y: win.h * 0.5 };
|
|
|
|
|
|
/*--------------------
|
|
Utils
|
|
--------------------*/
|
|
const lerp = (v0, v1, t) => v0 * (1 - t) + v1 * t;
|
|
|
|
|
|
/*--------------------
|
|
Pattern Generator
|
|
--------------------*/
|
|
class Pattern {
|
|
constructor(obj) {
|
|
Object.assign(this, obj);
|
|
this.init();
|
|
}
|
|
|
|
init() {
|
|
this.canvas = document.querySelector(`#${this.id}`) || document.createElement('canvas');
|
|
this.canvas.id = this.id;
|
|
this.canvas.width = this.width;
|
|
this.canvas.height = this.height;
|
|
document.body.appendChild(this.canvas);
|
|
this.ctx = this.canvas.getContext('2d');
|
|
this.generate();
|
|
}
|
|
|
|
random(min, max) {
|
|
return Math.floor(min + Math.random() * (max - min));
|
|
}
|
|
|
|
generate() {
|
|
let randomPalette = this.random(0, 100);
|
|
// randomPalette = 65
|
|
this.palette = palettes[randomPalette];
|
|
console.log('palette ---->', randomPalette);
|
|
let start = 0;
|
|
while (start < this.height + this.maxStroke) {
|
|
const off = this.random(this.minStroke, this.maxStroke);
|
|
this.ctx.beginPath();
|
|
this.ctx.strokeStyle = this.palette[this.random(0, 5)];
|
|
this.ctx.lineWidth = off;
|
|
this.ctx.moveTo(-this.maxStroke, start);
|
|
this.ctx.lineTo(this.width + this.maxStroke, start);
|
|
this.ctx.stroke();
|
|
this.ctx.closePath();
|
|
start += off;
|
|
}
|
|
|
|
if (ball) {
|
|
ball.material.map.needsUpdate = true;
|
|
}
|
|
}}
|
|
|
|
|
|
|
|
/*--------------------
|
|
Ball
|
|
--------------------*/
|
|
class Ball {
|
|
constructor(obj) {
|
|
Object.assign(this, obj);
|
|
this.init();
|
|
this.events = this.events.bind(this);
|
|
}
|
|
|
|
init() {
|
|
this.renderer = new THREE.WebGLRenderer({ antialias: true, transparent: true });
|
|
this.renderer.setSize(window.innerWidth, window.innerHeight);
|
|
document.body.appendChild(this.renderer.domElement);
|
|
|
|
this.camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
|
|
this.camera.position.z = 5;
|
|
|
|
this.scene = new THREE.Scene();
|
|
|
|
this.createLights();
|
|
this.createGeo();
|
|
|
|
this.events();
|
|
this.render();
|
|
}
|
|
|
|
createLights() {
|
|
this.ambientLight = new THREE.AmbientLight(0xffffff, .7);
|
|
this.scene.add(this.ambientLight);
|
|
this.ambientLight.castShadow = true;
|
|
|
|
this.light = new THREE.SpotLight(0xffffff, .5);
|
|
this.light.position.set(0, 0, 10);
|
|
this.light.castShadow = true;
|
|
|
|
this.scene.add(this.light);
|
|
this.scene.add(this.ambientLight);
|
|
}
|
|
|
|
createGeo() {
|
|
this.texture = new THREE.Texture(document.getElementById(this.textureID));
|
|
this.texture.wrapS = THREE.RepeatWrapping;
|
|
this.texture.wrapT = THREE.RepeatWrapping;
|
|
|
|
const path = 'https://threejs.org/examples/textures/cube/SwedishRoyalCastle/';
|
|
const format = '.jpg';
|
|
const urls = [
|
|
`${path}px${format}`, `${path}nx${format}`,
|
|
`${path}py${format}`, `${path}ny${format}`,
|
|
`${path}pz${format}`, `${path}nz${format}`];
|
|
|
|
this.cubeTexture = new THREE.CubeTextureLoader().load(urls);
|
|
this.cubeTexture.mapping = THREE.CubeRefractionMapping;
|
|
|
|
console.log(this.cubeTexture);
|
|
this.geo = new THREE.SphereBufferGeometry(1.5, 100, 100);
|
|
this.material = new THREE.MeshStandardMaterial({
|
|
roughness: 0.1,
|
|
metalness: 0.2,
|
|
emissive: 0x111111,
|
|
map: this.texture,
|
|
envMap: this.cubeTexture,
|
|
reflectivity: 0,
|
|
refractionRatio: 0.8 });
|
|
|
|
this.material.map.needsUpdate = true;
|
|
|
|
this.material.onBeforeCompile = function (shader) {
|
|
shader.uniforms.time = { value: 0 };
|
|
|
|
shader.vertexShader = `
|
|
uniform float time;
|
|
${shader.vertexShader}`;
|
|
shader.vertexShader = shader.vertexShader.replace(
|
|
'#include <begin_vertex>',
|
|
`
|
|
#include <begin_vertex>
|
|
float speed = 8.;
|
|
float TAO = 3.14159265 * 2.;
|
|
float t = mod(time, speed) / speed;
|
|
mat3 mat = mat3(
|
|
vec3(1. + sin(TAO * t * 2. + position.z * 2.5) * .5, 0., 0.),
|
|
vec3(0., 1. + cos(TAO * t + position.x * 2.) * .5, 0.),
|
|
vec3(0., 0., 1. + sin(TAO * t + position.y * 3.) * .5)
|
|
);
|
|
transformed *= mat;
|
|
vNormal *= mat;
|
|
`);
|
|
|
|
this.userData.shader = shader;
|
|
};
|
|
this.material.castShadow = true;
|
|
this.material.receiveShadow = true;
|
|
|
|
this.box = new THREE.Mesh(this.geo, this.material);
|
|
this.scene.add(this.box);
|
|
}
|
|
|
|
render() {
|
|
this.renderer.render(this.scene, this.camera);
|
|
requestAnimationFrame(this.render.bind(this));
|
|
|
|
const time = performance.now() / 1000;
|
|
|
|
if (this.material.userData) {
|
|
this.material.userData.shader.uniforms.time.value = time;
|
|
this.material.map.offset.y = time % 8 / 8;
|
|
}
|
|
this.box.rotation.y = lerp(this.box.rotation.z, -mouse.x * 0.005, 0.08);
|
|
this.box.rotation.z = lerp(this.box.rotation.y, mouse.y * 0.005, 0.08);
|
|
this.box.rotation.x = Math.PI * 2 * (time % 8) / 8;
|
|
|
|
this.light.position.x = lerp(this.light.position.x, -10 + mouse.x / win.w * 20, 0.08);
|
|
this.light.position.y = lerp(this.light.position.y, 10 + mouse.y / win.h * -20, 0.08);
|
|
this.light.position.z = lerp(this.light.position.z, -10 + Math.sin(mouse.x / win.w * Math.PI) * 20, 0.08);
|
|
}
|
|
|
|
handleMouse(e) {
|
|
mouse.x = e.clientX || e.touches[0].clientX;
|
|
mouse.y = e.clientY || e.touches[0].clientY;
|
|
}
|
|
|
|
events() {
|
|
window.addEventListener('resize', () => {
|
|
this.camera.aspect = window.innerWidth / window.innerHeight;
|
|
this.camera.updateProjectionMatrix();
|
|
|
|
this.renderer.setSize(window.innerWidth, window.innerHeight);
|
|
|
|
win.w = window.innerWidth;
|
|
win.h = window.innerHeight;
|
|
});
|
|
|
|
window.addEventListener('touchmove', e => {this.handleMouse(e);});
|
|
window.addEventListener('mousemove', e => {this.handleMouse(e);});
|
|
}}
|
|
|
|
|
|
|
|
/*--------------------
|
|
Init
|
|
--------------------*/
|
|
fetch('https://cdn.jsdelivr.net/npm/nice-color-palettes@3.0.0/100.json').
|
|
then(response => response.json()).
|
|
then(data => {
|
|
palettes = data;
|
|
|
|
pattern = new Pattern({
|
|
id: 'canvas-pattern',
|
|
width: 1024,
|
|
height: 1024,
|
|
minStroke: 2,
|
|
maxStroke: 6 });
|
|
|
|
|
|
ball = new Ball({
|
|
textureID: 'canvas-pattern' });
|
|
|
|
});
|
|
|
|
|
|
/*--------------------
|
|
Listeners
|
|
--------------------*/
|
|
document.body.addEventListener('click', () => {pattern.generate();}); |