Spaces:
Running
Running
| import WindowManager from './WindowManager.js' | |
| const t = THREE; | |
| let camera, scene, renderer, world; | |
| let near, far; | |
| let pixR = window.devicePixelRatio ? window.devicePixelRatio : 1; | |
| let cubes = []; | |
| let sceneOffsetTarget = {x: 0, y: 0}; | |
| let sceneOffset = {x: 0, y: 0}; | |
| let today = new Date(); | |
| today.setHours(0); | |
| today.setMinutes(0); | |
| today.setSeconds(0); | |
| today.setMilliseconds(0); | |
| today = today.getTime(); | |
| let internalTime = getTime(); | |
| let windowManager; | |
| let initialized = false; | |
| // get time in seconds since beginning of the day (so that all windows use the same time) | |
| function getTime () | |
| { | |
| return (new Date().getTime() - today) / 1000.0; | |
| } | |
| if (new URLSearchParams(window.location.search).get("clear")) | |
| { | |
| localStorage.clear(); | |
| } | |
| else | |
| { | |
| // this code is essential to circumvent that some browsers preload the content of some pages before you actually hit the url | |
| document.addEventListener("visibilitychange", () => | |
| { | |
| if (document.visibilityState != 'hidden' && !initialized) | |
| { | |
| init(); | |
| } | |
| }); | |
| window.onload = () => { | |
| if (document.visibilityState != 'hidden') | |
| { | |
| init(); | |
| } | |
| }; | |
| function init () | |
| { | |
| initialized = true; | |
| // add a short timeout because window.offsetX reports wrong values before a short period | |
| setTimeout(() => { | |
| setupScene(); | |
| // createParticleSystem(); | |
| setupWindowManager(); | |
| resize(); | |
| updateWindowShape(false); | |
| render(); | |
| window.addEventListener('resize', resize); | |
| }, 500) | |
| } | |
| function setupScene () | |
| { | |
| camera = new t.OrthographicCamera(0, 0, window.innerWidth, window.innerHeight, -10000, 10000); | |
| camera.position.z = 2.5; | |
| near = camera.position.z - .5; | |
| far = camera.position.z + 0.5; | |
| scene = new t.Scene(); | |
| scene.background = new t.Color(0.0); | |
| scene.add( camera ); | |
| var starGeometry = new THREE.Geometry(); | |
| for (let i = 0; i < 5000; i++) { | |
| var star = new THREE.Vector3(); | |
| star.x = Math.random() * 5000 - 2000; | |
| star.y = Math.random() * 5000 - 2000; | |
| star.z = Math.random() * 5000 - 2000; | |
| starGeometry.vertices.push(star); | |
| var color = new THREE.Color(); | |
| if (Math.random() < 0.5) { | |
| color.setHSL(0.16, 0.5, Math.random() * 0.5 + 0.25); | |
| } else { | |
| color.setHSL(0.0, 0.0, Math.random() * 0.5 + 0.5); | |
| } | |
| starGeometry.colors.push(color); | |
| } | |
| var starMaterial = new THREE.PointsMaterial({ | |
| size: 2, | |
| vertexColors: THREE.VertexColors | |
| }); | |
| var starField = new THREE.Points(starGeometry, starMaterial); | |
| scene.add(starField); | |
| renderer = new t.WebGLRenderer({antialias: true, depthBuffer: true}); | |
| renderer.setPixelRatio(pixR); | |
| world = new t.Object3D(); | |
| scene.add(world); | |
| renderer.domElement.setAttribute("id", "scene"); | |
| document.body.appendChild( renderer.domElement ); | |
| // Lights | |
| var light = new THREE.AmbientLight( 0x404040 ); // soft white light | |
| scene.add( light ); | |
| var directionalLight = new THREE.DirectionalLight( 0xffffff, 1 ); | |
| directionalLight.position.set( 0, 128, 128 ); | |
| scene.add( directionalLight ); | |
| } | |
| function setupWindowManager () | |
| { | |
| windowManager = new WindowManager(); | |
| windowManager.setWinShapeChangeCallback(updateWindowShape); | |
| windowManager.setWinChangeCallback(windowsUpdated); | |
| // here you can add your custom metadata to each windows instance | |
| let metaData = {foo: "bar"}; | |
| // this will init the windowmanager and add this window to the centralised pool of windows | |
| windowManager.init(metaData); | |
| // call update windows initially (it will later be called by the win change callback) | |
| windowsUpdated(); | |
| } | |
| function windowsUpdated () | |
| { | |
| updateNumberOfCubes(); | |
| } | |
| function updateNumberOfCubes () | |
| { | |
| let wins = windowManager.getWindows(); | |
| cubes.forEach((c) => { | |
| world.remove(c); | |
| }) | |
| cubes = []; | |
| for (let i = 0; i < wins.length; i++) | |
| { | |
| let win = wins[i]; | |
| let c; | |
| if (i == 0) { | |
| c = new t.Color('hsl(230, 80%, 75%)'); | |
| } else if (i == 1) { | |
| c = new t.Color('hsl(350, 60%, 65%)'); | |
| } else { | |
| let idBasedHueValue = (win.id % 10) / 10; | |
| let hue; | |
| if(idBasedHueValue < 0.5) { | |
| hue = 240 - (idBasedHueValue * 2 * 60); | |
| } else { | |
| hue = 360 - ((idBasedHueValue - 0.5) * 2 * 60); | |
| } | |
| c = new t.Color(`hsl(${hue}, 50%, 70%)`); | |
| } | |
| let s = 100 + i * 50; | |
| let radius = s / 2; | |
| let sphere = createComplexSphere(radius, c); | |
| sphere.position.x = win.shape.x + (win.shape.w * .5); | |
| sphere.position.y = win.shape.y + (win.shape.h * .5); | |
| world.add(sphere); | |
| cubes.push(sphere); | |
| } | |
| } | |
| function createComplexSphere(radius, color) { | |
| let innerSize = radius * 0.9; | |
| let outerSize = radius; | |
| let innerColor = color; | |
| let outerColor = color; | |
| let complexSphere = new THREE.Group(); | |
| let sphereWireframeInner = new THREE.Mesh( | |
| new THREE.IcosahedronGeometry(innerSize, 2), | |
| new THREE.MeshLambertMaterial({ | |
| color: innerColor, | |
| wireframe: true, | |
| transparent: true, | |
| shininess: 0 | |
| }) | |
| ); | |
| complexSphere.add(sphereWireframeInner); | |
| let sphereWireframeOuter = new THREE.Mesh( | |
| new THREE.IcosahedronGeometry(outerSize, 3), | |
| new THREE.MeshLambertMaterial({ | |
| color: outerColor, | |
| wireframe: true, | |
| transparent: true, | |
| shininess: 0 | |
| }) | |
| ); | |
| complexSphere.add(sphereWireframeOuter); | |
| let sphereGlassInner = new THREE.Mesh( | |
| new THREE.SphereGeometry(innerSize, 32, 32), | |
| new THREE.MeshPhongMaterial({ | |
| color: innerColor, | |
| transparent: true, | |
| shininess: 25, | |
| opacity: 0.3 | |
| }) | |
| ); | |
| complexSphere.add(sphereGlassInner); | |
| let sphereGlassOuter = new THREE.Mesh( | |
| new THREE.SphereGeometry(outerSize, 32, 32), | |
| new THREE.MeshPhongMaterial({ | |
| color: outerColor, | |
| transparent: true, | |
| shininess: 25, | |
| opacity: 0.3 | |
| }) | |
| ); | |
| complexSphere.add(sphereGlassOuter); | |
| let particlesOuter = createParticles(outerSize, outerColor); | |
| complexSphere.add(particlesOuter); | |
| let particlesInner = createParticles(innerSize, innerColor); | |
| complexSphere.add(particlesInner); | |
| return complexSphere; | |
| } | |
| function createParticles(size, color) { | |
| let geometry = new THREE.Geometry(); | |
| for (let i = 0; i < 35000; i++) { | |
| let x = -1 + Math.random() * 2; | |
| let y = -1 + Math.random() * 2; | |
| let z = -1 + Math.random() * 2; | |
| let d = 1 / Math.sqrt(x * x + y * y + z * z); | |
| x *= d * size; | |
| y *= d * size; | |
| z *= d * size; | |
| geometry.vertices.push(new THREE.Vector3(x, y, z)); | |
| } | |
| let material = new THREE.PointsMaterial({ | |
| size: 0.1, | |
| color: color, | |
| transparent: true | |
| }); | |
| return new THREE.Points(geometry, material); | |
| } | |
| function updateWindowShape (easing = true) | |
| { | |
| // storing the actual offset in a proxy that we update against in the render function | |
| sceneOffsetTarget = {x: -window.screenX, y: -window.screenY}; | |
| if (!easing) sceneOffset = sceneOffsetTarget; | |
| } | |
| function render () | |
| { | |
| let t = getTime(); | |
| windowManager.update(); | |
| // calculate the new position based on the delta between current offset and new offset times a falloff value (to create the nice smoothing effect) | |
| let falloff = .05; | |
| sceneOffset.x = sceneOffset.x + ((sceneOffsetTarget.x - sceneOffset.x) * falloff); | |
| sceneOffset.y = sceneOffset.y + ((sceneOffsetTarget.y - sceneOffset.y) * falloff); | |
| // set the world position to the offset | |
| world.position.x = sceneOffset.x; | |
| world.position.y = sceneOffset.y; | |
| let wins = windowManager.getWindows(); | |
| // loop through all our cubes and update their positions based on current window positions | |
| for (let i = 0; i < cubes.length; i++) | |
| { | |
| let complexSphere = cubes[i]; | |
| let win = wins[i]; | |
| let _t = t; | |
| let posTarget = {x: win.shape.x + (win.shape.w * .5), y: win.shape.y + (win.shape.h * .5)} | |
| complexSphere.position.x = complexSphere.position.x + (posTarget.x - complexSphere.position.x) * falloff; | |
| complexSphere.position.y = complexSphere.position.y + (posTarget.y - complexSphere.position.y) * falloff; | |
| complexSphere.rotation.x = _t * .5; | |
| complexSphere.rotation.y = _t * .3; | |
| updateComplexSphere(complexSphere, t); | |
| }; | |
| // updateParticles(); | |
| renderer.render(scene, camera); | |
| requestAnimationFrame(render); | |
| } | |
| function updateComplexSphere(complexSphere, elapsedTime) { | |
| let sphereWireframeInner = complexSphere.children[0]; | |
| let sphereWireframeOuter = complexSphere.children[1]; | |
| let sphereGlassInner = complexSphere.children[2]; | |
| let sphereGlassOuter = complexSphere.children[3]; | |
| let particlesOuter = complexSphere.children[4]; | |
| let particlesInner = complexSphere.children[5]; | |
| sphereWireframeInner.rotation.x += 0.002; | |
| sphereWireframeInner.rotation.z += 0.002; | |
| sphereWireframeOuter.rotation.x += 0.001; | |
| sphereWireframeOuter.rotation.z += 0.001; | |
| sphereGlassInner.rotation.y += 0.005; | |
| sphereGlassInner.rotation.z += 0.005; | |
| sphereGlassOuter.rotation.y += 0.01; | |
| sphereGlassOuter.rotation.z += 0.01; | |
| particlesOuter.rotation.y += 0.0005; | |
| particlesInner.rotation.y -= 0.002; | |
| var innerShift = Math.abs(Math.cos(((elapsedTime + 2.5) / 20))); | |
| var outerShift = Math.abs(Math.cos(((elapsedTime + 5) / 10))); | |
| sphereWireframeOuter.material.color.setHSL(0.55, 1, outerShift); | |
| sphereGlassOuter.material.color.setHSL(0.55, 1, outerShift); | |
| particlesOuter.material.color.setHSL(0.55, 1, outerShift); | |
| sphereWireframeInner.material.color.setHSL(0.08, 1, innerShift); | |
| particlesInner.material.color.setHSL(0.08, 1, innerShift); | |
| sphereGlassInner.material.color.setHSL(0.08, 1, innerShift); | |
| sphereWireframeInner.material.opacity = Math.abs(Math.cos((elapsedTime + 0.5) / 0.9) * 0.5); | |
| sphereWireframeOuter.material.opacity = Math.abs(Math.cos(elapsedTime / 0.9) * 0.5); | |
| } | |
| // resize the renderer to fit the window size | |
| function resize () | |
| { | |
| let width = window.innerWidth; | |
| let height = window.innerHeight | |
| camera = new t.OrthographicCamera(0, width, 0, height, -10000, 10000); | |
| camera.updateProjectionMatrix(); | |
| renderer.setSize( width, height ); | |
| } | |
| } | |