diff --git a/src/parallax-css-testpage/particles.js b/src/parallax-css-testpage/particles.js index b7dd7f68ba67ceb5f94eec0b11b0d439477eccc2..9b59090b193984fe4fcce64139585d4dac07453a 100644 --- a/src/parallax-css-testpage/particles.js +++ b/src/parallax-css-testpage/particles.js @@ -2,25 +2,64 @@ * Particles.js */ +const Debug = false +const Static = true + +// Needed for fps calculation. +let Then = Date.now() // Argh.. Global, mutable, variable..! + // // Helper // -// Variables needed for fps calculation. -let Then = Date.now() // Argh.. Global, mutable, variable..! - +// Random Value without zero function randomValue(min, max) { - return (Math.random() * (+max - +min) + +min) + const value = (Math.random() * (+max - +min) + +min) + return (value === 0) ? randomValue(min, max) : value } -function initCanvas(id) { - const canvas = document.getElementById(id) +function getNodeYOffset(node) { + const dimensions = node.getBoundingClientRect() + return (dimensions) ? dimensions.y : 0 +} + +function getNodeHeight(node) { + const dimensions = node.getBoundingClientRect() + return (dimensions) ? dimensions.height : window.innerHeight +} + +function getScene(canvasId, height) { + const width = window.innerWidth + const fov = 0.8 + const perspective = width * fov + const yOffset = 0 + const canvas = document.getElementById(canvasId) const ctx = canvas.getContext('2d') - ctx.canvas.width = window.innerWidth + + ctx.canvas.width = width ctx.canvas.height = window.innerHeight - return ctx + + return { width, height, yOffset, perspective, ctx } +} + +// function to2dParticle(scene, particle) { +// const perspective = scene.perspective +// const scale = perspective / (perspective + particle.position.z) +// const yOffset = scene.yOffset * scale +// const offset = particle.size.value * 0.5 +// const x = particle.position.x - offset +// const y = (yOffset + (particle.position.y - offset)) +// const size = particle.size.value +// // const y = (yOffset + (particle.position.y - offset)) * scale +// // const size = particle.size.value * scale +// return { x, y, size } +// } + +function calcScrolledYPos(ctx, yPos, yOffset) { + const y = (yPos + yOffset) + return (y <= 0) ? ctx.canvas.height - y : y } @@ -33,6 +72,10 @@ function Point2D(x, y) { return { x, y } } +function Point3D(x, y, z) { + return { x, y, z } +} + function MinMax(min, max) { return { min, max } } @@ -47,6 +90,16 @@ function RandomPoint2D(minMaxX, minMaxY) { return Point2D(x, y) } +function RandomPoint3D(minMaxX, minMaxY, minMaxZ) { + const x = randomValue(minMaxX.min, minMaxX.max) + const y = randomValue(minMaxY.min, minMaxY.max) + const z = + (typeof minMaxZ === 'number') + ? minMaxZ + : randomValue(minMaxZ.min, minMaxZ.max) + return Point3D(x, y, z) +} + function Particle({ position, direction, size, color }) { return { position, direction, size, color } } @@ -57,7 +110,11 @@ function Particle({ position, direction, size, color }) { // -function Update(canvas, particle) { +function Update(scene, particle) { + // Cache object access + const width = scene.width + const height = scene.height + // Animate the pulse effect of the particle // const isMaxSize = particle.size.value >= particle.size.bound.max @@ -83,85 +140,100 @@ function Update(canvas, particle) { // Animate the movement of the particle // const sizeOffset = particle.size.value * 0.5 - const xCenter = particle.position.x - sizeOffset - const yCenter = particle.position.y - sizeOffset - const isRightEdge = - (particle.position.x + particle.size.value >= canvas.width) - const isLeftEdge = (particle.position.x <= 0) - const isTopEdge = (yCenter <= 0) - const isBottomEdge = (yCenter >= canvas.height) - const scrollOffset = window.pageYOffset + const y = particle.position.y + + const top = y - sizeOffset + const bottom = y + sizeOffset + const left = particle.position.x - sizeOffset + const right = particle.position.x + sizeOffset + + const isRightEdge = (right >= width) + const isLeftEdge = (left <= 0) + const isTopEdge = (bottom < 0) + const isBottomEdge = (top > height) const updateDirection = Object.assign({}, updateSize, { direction: Point2D( + // Bounce of the right and left edges of the screen. (isRightEdge || isLeftEdge) ? updateSize.direction.x * -1 : updateSize.direction.x, - // (isTopEdge || isBottomEdge) - // ? particle.direction.y * -1 - // : particle.direction.y - updateSize.direction.y + (isTopEdge || isBottomEdge) + ? particle.direction.y * -1 + : particle.direction.y, ) }) const updatePosition = Object.assign({}, updateDirection, { position: - Point2D( + Point3D( updateDirection.position.x + updateDirection.direction.x, - (isTopEdge) - ? (canvas.height - 1) + updateDirection.direction.y - : (isBottomEdge) - ? sizeOffset + 1 - : updateDirection.position.y + updateDirection.direction.y, + updateDirection.position.y + updateDirection.direction.y, + // (isTopEdge) + // ? height + sizeOffset + // : (isBottomEdge) + // ? -sizeOffset + // : (updateDirection.position.y + updateDirection.direction.y), + updateDirection.position.z ) }) - const updatedParticle = Particle(updatePosition) return updatedParticle } -function Clear(ctx) { - ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height) +function Draw(scene, particle) { + // Cache object access + const size = particle.size.value + + const perspective = scene.perspective + const scale = perspective / (perspective + particle.position.z) + const yOffset = scene.yOffset * scale + const drawOffset = size * 0.5 + const y = yOffset + (particle.position.y - drawOffset) + const ctx = scene.ctx + + const isVisible = (size > 0) && (y < window.innerHeight) && (y > 0) + if (isVisible) { + ctx.fillStyle = particle.color + ctx.fillRect(particle.position.x - drawOffset, y, size, size) + } +} + +function Clear(scene) { + scene.ctx.clearRect(0, 0, scene.width, scene.height) } -function Render(ctx, particles) { +function Render(scene, particles) { + Clear(scene) particles.forEach( function (particle) { - if (particle.size.value > 0) { - const offset = (particle.size.value*0.5) - ctx.fillStyle = particle.color - ctx.fillRect( - particle.position.x - offset, - particle.position.y - offset, - particle.size.value, - particle.size.value - ) - } + Draw(scene, particle) } ) } -function Animate(ctx, fpsInterval, particles) { +function Animate({ scene, fpsInterval, particles, anchorNode }) { + scene.yOffset = getNodeYOffset(anchorNode) + // Update particle positions and register for the next frame // to render. requestAnimationFrame(() => { const updatedParticles = particles.map(function (p) { - return Update(ctx.canvas, p) + return Update(scene, p) }) - Animate(ctx, fpsInterval, updatedParticles) + Animate({ scene, fpsInterval, particles: updatedParticles, anchorNode }) }) - // Render particles to canvas + // Render particles to canvas and do some fps calculations const now = Date.now() const elapsed = now - Then if (elapsed > fpsInterval) { Then = now - (elapsed % fpsInterval) - Clear(ctx) - Render(ctx, particles) + Render(scene, particles) } } @@ -171,50 +243,88 @@ function Animate(ctx, fpsInterval, particles) { // -function Particles({ canvasId, fps, amount, color, size, lifespan, speed }) { +function Particles({ + canvasId, + contentId, + fps, + amount, + color, + size, + speed, + depth, + lifespan, +}) { + const contentNode = document.getElementById(contentId) + const height = getNodeHeight(contentNode) + // Init the canvas for rendering. // - const ctx = initCanvas(canvasId) - if (!ctx) { + const scene = getScene(canvasId, height) + if (!scene.ctx) { console.error("Particles: Can't find Canvas ", canvasId) return } + // Generate some particles // + + const z = depth * -1000 const particles = - Array.from( - { length: amount }, - () => Particle({ - position: - RandomPoint2D( - MinMax(0, (ctx.canvas.width - (size*2))), - MinMax(0, (ctx.canvas.height - (size*2))) - ), - direction: - RandomPoint2D( - MinMax(-speed, speed), - MinMax(-speed, speed) - ), - size: { - value: randomValue(-size, size), - bound: MinMax(-size, size), - step: size / ((lifespan) / fps), + (Debug) + ? (Static) + ? [ Particle({ + position: Point3D(100, 500, z), + direction: Point2D(0, 0), + size: { + value: 15, + bound: MinMax(100, 100), + step: 0, + direction: 1 + }, + color: 'white' + }) ] + : [ Particle({ + position: Point3D(100, 100, z), + direction: Point2D(0, +1), + size: { + value: 100, + bound: MinMax(100, 100), + step: 0, + direction: 1 + }, + color: 'white' + }) ] + : Array.from( + { length: amount }, + () => Particle({ + position: + RandomPoint3D( + MinMax(0, (scene.width - (size*2))), + MinMax(0, (scene.height - (size*2))), + z + ), direction: - (randomValue(0, 100) < 50) - ? -1 - : +1, - }, - color - }) - ) + RandomPoint2D( + MinMax(-speed, speed), + MinMax(-speed, speed) + ), + size: { + value: randomValue(-size, size), + bound: MinMax(-size, size), + step: size / ((lifespan) / fps), + direction: + (randomValue(0, 100) < 50) + ? -1 + : +1, + }, + color + }) + ) // Prepare Animation and animate const fpsInterval = 1000 / fps Then = Date.now() - Animate(ctx, fpsInterval, particles) + Animate({ scene, fpsInterval, particles, anchorNode: contentNode }) return } - - - diff --git a/src/parallax-css-testpage/template.html b/src/parallax-css-testpage/template.html index e2eb3e6d1f6cf7dd712b19f946bba56d0a7279c5..7819f5ee959fac5ff3efdd0ea3746e17793251a7 100644 --- a/src/parallax-css-testpage/template.html +++ b/src/parallax-css-testpage/template.html @@ -83,11 +83,17 @@ <script> var bgParticles = { + // Id of the canvas to render to canvasId: 'bg-particles', - fps: 14, - amount: 100, + + // Id of the content container, we're using it to get the + // rendering height and it's the anchor to get the y offset + // to simulate scrolling. + contentId: 'content', + fps: 16, + amount: 120, color: 'white', - size: 5, + size: 10, speed: 0.2, depth: -8, lifespan: 4000 // in milliseconds @@ -96,7 +102,8 @@ var fgParticles = { canvasId: 'fg-particles', - fps: 20, + contentId: 'content', + fps: 24, amount: 20, color: 'white', size: 30,