Skip to content
Snippets Groups Projects
Commit 4f2dd94e authored by Dj's avatar Dj
Browse files

Add support for multiple layer of particles.

parent d63762cb
Branches
No related tags found
No related merge requests found
/* /*
* Particles.js * Particles.js
*
* Particle emitter and renderer.
*/ */
const Debug = false const Debug = false
const Static = true const Static = true
// Needed for fps calculation.
let Then = Date.now() // Argh.. Global, mutable, variable..!
// //
// Helper // Helper
// //
// Random Value without zero
function randomValue(min, max) { function randomValue(min, max) {
const value = (Math.random() * (+max - +min) + +min) const value = (Math.random() * (+max - +min) + +min)
return (value === 0) ? randomValue(min, max) : value // Prevent values that can be possible boring
return (value === 0 || value === 1)
? randomValue(min, max)
: value
} }
function getNodeYOffset(node) { function getNodeYOffset(node) {
...@@ -30,38 +31,6 @@ function getNodeHeight(node) { ...@@ -30,38 +31,6 @@ function getNodeHeight(node) {
return (dimensions) ? dimensions.height : window.innerHeight 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 = width
ctx.canvas.height = window.innerHeight
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
}
// //
// Data // Data
...@@ -72,10 +41,6 @@ function Point2D(x, y) { ...@@ -72,10 +41,6 @@ function Point2D(x, y) {
return { x, y } return { x, y }
} }
function Point3D(x, y, z) {
return { x, y, z }
}
function MinMax(min, max) { function MinMax(min, max) {
return { min, max } return { min, max }
} }
...@@ -90,16 +55,6 @@ function RandomPoint2D(minMaxX, minMaxY) { ...@@ -90,16 +55,6 @@ function RandomPoint2D(minMaxX, minMaxY) {
return Point2D(x, y) 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 }) { function Particle({ position, direction, size, color }) {
return { position, direction, size, color } return { position, direction, size, color }
} }
...@@ -132,8 +87,8 @@ function Update(scene, particle) { ...@@ -132,8 +87,8 @@ function Update(scene, particle) {
Object.assign({}, updateSizeStep, { Object.assign({}, updateSizeStep, {
size: Object.assign({}, updateSizeStep.size, { size: Object.assign({}, updateSizeStep.size, {
value: value:
// Todo: Animate puls effect with the help of Math.sin()
updateSizeStep.size.value + updateSizeStep.size.step * updateSizeStep.size.direction, updateSizeStep.size.value + updateSizeStep.size.step * updateSizeStep.size.direction,
// updateSizeStep.size.value
}) })
}) })
...@@ -142,8 +97,8 @@ function Update(scene, particle) { ...@@ -142,8 +97,8 @@ function Update(scene, particle) {
const sizeOffset = particle.size.value * 0.5 const sizeOffset = particle.size.value * 0.5
const y = particle.position.y const y = particle.position.y
const top = y - sizeOffset const top = y + sizeOffset
const bottom = y + sizeOffset const bottom = y - sizeOffset
const left = particle.position.x - sizeOffset const left = particle.position.x - sizeOffset
const right = particle.position.x + sizeOffset const right = particle.position.x + sizeOffset
...@@ -156,10 +111,11 @@ function Update(scene, particle) { ...@@ -156,10 +111,11 @@ function Update(scene, particle) {
Object.assign({}, updateSize, { Object.assign({}, updateSize, {
direction: direction:
Point2D( Point2D(
// Bounce of the right and left edges of the screen. // Bounce of the right and left edges of the scene
(isRightEdge || isLeftEdge) (isRightEdge || isLeftEdge)
? updateSize.direction.x * -1 ? updateSize.direction.x * -1
: updateSize.direction.x, : updateSize.direction.x,
// Bounce from the top and bottom edges of the scene
(isTopEdge || isBottomEdge) (isTopEdge || isBottomEdge)
? particle.direction.y * -1 ? particle.direction.y * -1
: particle.direction.y, : particle.direction.y,
...@@ -168,7 +124,7 @@ function Update(scene, particle) { ...@@ -168,7 +124,7 @@ function Update(scene, particle) {
const updatePosition = const updatePosition =
Object.assign({}, updateDirection, { Object.assign({}, updateDirection, {
position: position:
Point3D( Point2D(
updateDirection.position.x + updateDirection.direction.x, updateDirection.position.x + updateDirection.direction.x,
updateDirection.position.y + updateDirection.direction.y, updateDirection.position.y + updateDirection.direction.y,
// (isTopEdge) // (isTopEdge)
...@@ -176,7 +132,6 @@ function Update(scene, particle) { ...@@ -176,7 +132,6 @@ function Update(scene, particle) {
// : (isBottomEdge) // : (isBottomEdge)
// ? -sizeOffset // ? -sizeOffset
// : (updateDirection.position.y + updateDirection.direction.y), // : (updateDirection.position.y + updateDirection.direction.y),
updateDirection.position.z
) )
}) })
...@@ -187,18 +142,19 @@ function Update(scene, particle) { ...@@ -187,18 +142,19 @@ function Update(scene, particle) {
function Draw(scene, particle) { function Draw(scene, particle) {
// Cache object access // Cache object access
const size = particle.size.value const size = particle.size.value
const position = particle.position
const perspective = scene.perspective // Draw calculations
const scale = perspective / (perspective + particle.position.z) const yOffset = scene.yOffset * scene.depthScale
const yOffset = scene.yOffset * scale
const drawOffset = size * 0.5 const drawOffset = size * 0.5
const y = yOffset + (particle.position.y - drawOffset) const y = yOffset + (position.y - drawOffset)
const ctx = scene.ctx const ctx = scene.ctx
const isVisible = (size > 0) && (y < window.innerHeight) && (y > 0) const isVisible = (size > 0) && (y < window.innerHeight) && (y > 0)
// Draw
if (isVisible) { if (isVisible) {
ctx.fillStyle = particle.color ctx.fillStyle = particle.color
ctx.fillRect(particle.position.x - drawOffset, y, size, size) ctx.fillRect(position.x - drawOffset, y, size, size)
} }
} }
...@@ -209,100 +165,79 @@ function Clear(scene) { ...@@ -209,100 +165,79 @@ function Clear(scene) {
function Render(scene, particles) { function Render(scene, particles) {
Clear(scene) Clear(scene)
particles.forEach( particles.forEach(
function (particle) { function doDraw(particle) {
Draw(scene, particle) Draw(scene, particle)
} }
) )
} }
function Animate({ scene, fpsInterval, particles, anchorNode }) { function Animate(scene, particles) {
scene.yOffset = getNodeYOffset(anchorNode) var fpsInterval = scene.fpsInterval
var now = Date.now()
var then = scene.then
var elapsed = now - then
// Update particle positions and register for the next frame // Just render when we're reaching the configured fps
// to render. if (elapsed > scene.fpsInterval) {
requestAnimationFrame(() => { scene.yOffset = getNodeYOffset(scene.anchorNode)
const updatedParticles = scene.then = now - (elapsed % fpsInterval)
particles.map(function (p) { Render(scene, particles)
}
// Update particle positions for the next render call
const updatedParticles = particles.map(function doUpdate(p) {
return Update(scene, p) return Update(scene, p)
}) })
Animate({ scene, fpsInterval, particles: updatedParticles, anchorNode })
})
// Render particles to canvas and do some fps calculations // Function to animate the next frame
const now = Date.now() return function () {
const elapsed = now - Then return Animate(scene, updatedParticles)
if (elapsed > fpsInterval) {
Then = now - (elapsed % fpsInterval)
Render(scene, particles)
} }
} }
function CreateScene({ canvasId, contentId, depth, fps }) {
const contentNode = document.getElementById(contentId)
const height = getNodeHeight(contentNode)
// const fpsInterval = 1000 / fps
// Api const width = window.innerWidth
// const yOffset = 0
const canvas = document.getElementById(canvasId)
const ctx = canvas.getContext('2d')
const depthScaledHeight = height * (depth+1)
ctx.canvas.width = width
ctx.canvas.height = window.innerHeight
function Particles({ return {
canvasId, width,
contentId, height: depthScaledHeight,
yOffset,
depthScale: depth,
ctx,
fps, fps,
fpsInterval,
then: Date.now(),
anchorNode: contentNode
}
}
function CreateParticles(scene, {
amount, amount,
color, color,
size, size,
speed, speed,
depth,
lifespan, lifespan,
}) { }) {
const contentNode = document.getElementById(contentId) return Array.from(
const height = getNodeHeight(contentNode)
// Init the canvas for rendering.
//
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 =
(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 }, { length: amount },
() => Particle({ function createParticle() {
return Particle({
position: position:
RandomPoint3D( RandomPoint2D(
MinMax(0, (scene.width - (size * 2))), MinMax(0, (scene.width - (size * 2))),
MinMax(0, (scene.height - (size * 2))), MinMax(0, (scene.height - (size * 2))),
z
), ),
direction: direction:
RandomPoint2D( RandomPoint2D(
...@@ -312,19 +247,44 @@ function Particles({ ...@@ -312,19 +247,44 @@ function Particles({
size: { size: {
value: randomValue(-size, size), value: randomValue(-size, size),
bound: MinMax(-size, size), bound: MinMax(-size, size),
step: size / ((lifespan) / fps), step: size / ((lifespan) / scene.fps),
direction: direction:
// 50% Chance for the particle floating in one
// direction or the other.
(randomValue(0, 100) < 50) (randomValue(0, 100) < 50)
? -1 ? -1
: +1, : +1,
}, },
color color
}) })
}
) )
}
// Prepare Animation and animate
const fpsInterval = 1000 / fps //
Then = Date.now() // Api
Animate({ scene, fpsInterval, particles, anchorNode: contentNode }) //
return
function Particles(configs) {
function doRenderParticles(rendererList) {
requestAnimationFrame(function () {
doRenderParticles(
rendererList.map(function doRender(render) {
return render()
})
)
})
}
return function () {
doRenderParticles(
configs.map(function (particleConfig) {
var scene = CreateScene(particleConfig.scene)
var particles = CreateParticles(scene, particleConfig.particles)
return Animate(scene, particles)
})
)
}
} }
\ No newline at end of file
...@@ -82,36 +82,50 @@ ...@@ -82,36 +82,50 @@
<script> <script>
var bgParticles = { var fgParticleConfig = {
scene: {
// Id of the canvas to render to
canvasId: 'fg-particles',
// 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: 30,
depth: 2,
},
particles: {
amount: 100,
color: 'white',
size: 30,
speed: 0.5,
lifespan: 3000 // in milliseconds
}
}
var bgParticleConfig = {
scene: {
// Id of the canvas to render to // Id of the canvas to render to
canvasId: 'bg-particles', canvasId: 'bg-particles',
// Id of the content container, we're using it to get the // Id of the content container, we're using it to get the
// rendering height and it's the anchor to get the y offset // rendering height and it's the anchor to get the y offset
// to simulate scrolling. // to simulate scrolling.
contentId: 'content', contentId: 'content',
fps: 16, fps: 16,
amount: 120, depth: 0.08,
},
particles: {
amount: 200,
color: 'white', color: 'white',
size: 10, size: 10,
speed: 0.2, speed: 0.2,
depth: -8,
lifespan: 4000 // in milliseconds lifespan: 4000 // in milliseconds
} }
Particles(bgParticles)
var fgParticles = {
canvasId: 'fg-particles',
contentId: 'content',
fps: 24,
amount: 20,
color: 'white',
size: 30,
speed: 0.5,
depth: 2,
lifespan: 4000 // in milliseconds
} }
// Particles(fgParticles)
var animate = Particles([
fgParticleConfig,
bgParticleConfig,
])
animate()
</script> </script>
</body> </body>
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment