Skip to content

Instantly share code, notes, and snippets.

@Synvox
Created September 11, 2016 05:56
Show Gist options
  • Save Synvox/a65026b4c4966aeaba452a73a58d8c12 to your computer and use it in GitHub Desktop.
Save Synvox/a65026b4c4966aeaba452a73a58d8c12 to your computer and use it in GitHub Desktop.
qaOopa
<main>
<canvas></canvas>
<h1>
Ryan Allred
<small>Full Stack Web Engineer</small>
</h1>
</main>
const {atan2, pow, sqrt, cos, sin, max, min, abs, PI, random, round, floor} = Math
const canvas = document.querySelector('canvas')
const cxt = canvas.getContext('2d')
// cxt.globalCompositeOperation = "lighten"
cxt.fillStyle = 'rgba(255,255,255,.1)'
class World {
constructor() {
this.reset()
this.particles = []
this.target = null
}
spawn(x, y) {
this.particles.push(new Particle(x,y))
const overflow = this.particles.length - 300
if (overflow > 0)
for(let i = 0; i < overflow; i++)
this.particles.shift()
}
setTarget(x, y, hue) {
this.target = { x, y, hue, life: 1000, mode: floor(random() * 3) }
this.particles.forEach((part)=>{
if (part.target.life < 0 || !round(random())) {
part.target = {...this.target}
}
})
}
reset() {
canvas.width = window.innerWidth * (window.devicePixelRatio || 1);
canvas.height = window.innerHeight * (window.devicePixelRatio || 1);
}
step() {
this.particles.forEach(part=>part.step(this))
this.particles.forEach(part=>part.draw())
}
}
class Particle {
constructor(x, y) {
this.x = x
this.y = y
this.dir = random() * 2 * PI
this.turning = 20 + random() * 2
this.speed = random() * 20 + 10
this.hue = round(random() * 40) - 20
this.hueDelta = 0.01
this.opacity = random()
this.width = 2 + round(random() * 4)
this.target = null
this.tilt = !round(random())
}
step(world) {
if (!this.target || this.target.life < 0) {
this.target = {...world.target}
} else this.target.life--
const {x, y, hue} = this.target
const dir1 = atan2(y - this.y, x - this.x)
const dir2 = dir1 + PI * 2
let dir = (abs(dir1-this.dir) < abs(dir2-this.dir)) ? dir1 : dir2
dir += random()
if (this.target.mode === 0)
this.dir += (this.tilt?2:1)*(dir - this.dir)/this.turning
else if (this.target.mode === 1)
this.dir += (this.tilt?2:1)*(dir - this.dir)/this.turning*1.2
else
this.dir += (this.tilt?2:1)*(dir - this.dir)/this.turning/1.2
if (this.dir > 2 * PI)
this.dir -= 2 * PI
else if (this.dir < 0)
this.dir += 2 * PI
this.hue += this.hueDelta
}
draw() {
const {hue} = this.target
cxt.beginPath()
cxt.moveTo(this.x, this.y)
this.x = round(this.x + this.speed * cos(this.dir))
this.y = round(this.y + this.speed * sin(this.dir))
cxt.lineTo(this.x, this.y)
cxt.strokeStyle = `hsla(${hue + this.hue},80%, 50%, ${this.opacity})`
cxt.lineWidth = this.width * window.devicePixelRatio
cxt.stroke()
}
}
const world = new World()
const step = ()=>{
window.requestAnimationFrame(step)
world.step()
}
step()
const spawn = (create)=>{
return (e)=>{
const x = e.layerX * window.devicePixelRatio
const y = e.layerY * window.devicePixelRatio
if (create){
world.setTarget(x, y, round(random() * 18) * 20)
} else {
world.setTarget(x, y, world.target.hue)
return
}
const count = round(random() * 5) + 5
for(let i = 0; i < count; i++)
world.spawn(x, y)
}
}
spawn(true)({
layerX: canvas.width / window.devicePixelRatio / 2,
layerY: canvas.height / window.devicePixelRatio * 0.4,
})
const fakeSpawn = ()=>{
window.setTimeout(()=>{
const x = (canvas.width / window.devicePixelRatio)*.8 * random() + (canvas.width / window.devicePixelRatio)*.2
const y = (canvas.height / window.devicePixelRatio)*.8 * random() + (canvas.height / window.devicePixelRatio)*.2
spawn(round(random()*2) !== 0)({
layerX: x,
layerY: y,
})
fakeSpawn()
},10000 + round(random() * 5000))
}
fakeSpawn()
let down = false
canvas.addEventListener('mousedown',(e)=>{
down = true
spawn(true)(e)
})
canvas.addEventListener('mousemove',(e)=>{
if (down)
spawn(false)(e)
})
document.addEventListener('mouseup',()=>{
down = false
})
window.setInterval(()=>{
window.requestAnimationFrame(()=>{
cxt.fillStyle = 'rgba(20,20,20,.2)'
cxt.fillRect(0,0,canvas.width, canvas.height)
})
},1000/10)
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.0.0/jquery.min.js"></script>
html {
box-sizing: border-box;
}
*, *:before, *:after {
box-sizing: inherit;
}
body {
padding: 0;
height: 100vh;
background: rgb(20,20,20);
}
canvas {
position: absolute;
top: 0;
left: 0px;
right: 0;
height: 100vh;
transform: translate3d(0,0,0);
background: black;
}
main {
height: 100vh;
position: relative;
z-index: 100;
background: rgb(20,20,20);
}
h1 {
position: absolute;
top: 40%;
left: 50%;
transform: translate(-50%, -50%);
font-size: 50px;
display: inline-block;
white-space: nowrap;
text-align: center;
padding: .5em 0;
width: 40%;
// background: rgba(255,255,255,.8);
// border-radius: 10000px;
// border: 1px solid white;
color: #333;
color: white;
user-select: none;
pointer-events: none;
font-family: 'Montserrat', 'Helvetica Neue', Arial, sans-serif;
font-weight: 700;
font-size: 40px;
letter-spacing: 9px;
text-transform: uppercase;
small {
display: block;
font-size: .5em;
letter-spacing: 2px;
}
}
<link href="http://fonts.googleapis.com/css?family=Open+Sans|Montserrat:700" rel="stylesheet" />
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment