A Pen by Per Byhring on CodePen.
Created
August 7, 2018 02:15
-
-
Save maxdevjs/00f10799c5e6290efad6decde6d9e41c to your computer and use it in GitHub Desktop.
Let there be light
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<link href='//fonts.googleapis.com/css?family=Raleway:100' rel='stylesheet' type='text/css'> | |
<link href='//fonts.googleapis.com/css?family=Berkshire+Swash' rel='stylesheet' type='text/css'> | |
<div class="intro"> | |
<h1>Let there be light</h1> | |
<p>Paint with your cursor to create a galaxy</p> | |
</div> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var w = window.innerWidth; | |
var h = window.innerHeight; | |
var dustCanvas = document.createElement('canvas'); | |
var dustCtx = dustCanvas.getContext('2d'); | |
var starCanvas = document.createElement('canvas'); | |
var starCtx = starCanvas.getContext('2d'); | |
document.body.appendChild(dustCanvas); | |
document.body.appendChild(starCanvas); | |
dustCanvas.width = starCanvas.width = w; | |
dustCanvas.height = starCanvas.height = h; | |
dustCtx.globalCompositeOperation = 'lighter'; | |
starCtx.globalCompositeOperation = 'lighter'; | |
var galaxies = []; | |
var mouse = { | |
pos: { | |
x: w * 0.5, | |
y: h * 0.5 | |
}, | |
speed: 0 | |
}; | |
var randomNumbers = length => Array.from(new Array(length), () => Math.random()); | |
var PI = Math.PI; | |
var TAU = PI * 2; | |
var r = () => Math.random(); | |
var angle2 = (p1,p2) => Math.atan2( | |
p2[1]-p1[1], | |
p2[0]-p1[0] | |
); | |
var distance2 = (p1,p2) => Math.sqrt( | |
Math.pow(p1[0]-p2[0], 2) + | |
Math.pow(p1[1]-p2[1], 2) | |
); | |
var createDustParticle = (color) => { | |
var canvas = document.createElement('canvas'); | |
var w = 100; | |
var h = 100; | |
var cx = w * 0.5; | |
var cy = h * 0.5; | |
canvas.width = w; | |
canvas.height = h; | |
var ctx = canvas.getContext('2d'); | |
canvas.ctx = ctx; | |
var xRand = -5 + (r()*10); | |
var yRand = -5 + (r()*10); | |
var xRand2 = 10 + (r()*(cx/2)); | |
var yRand2 = 10 + (r()*(cy/2)); | |
var color = color || { | |
r: Math.round(150+(r()*100)), | |
g: Math.round(50+(r()*100)), | |
b: Math.round(50+(r()*100)) | |
}; | |
ctx.fillStyle = `rgba(${color.r},${color.g},${color.b},.02)`; | |
Array | |
.from(new Array(200), () => randomNumbers(3)) | |
.forEach( (p,i,arr) => { | |
var length = arr.length; | |
var x = Math.cos( TAU/xRand/length*i ) * p[2]*xRand2 + cx; | |
var y = Math.sin( TAU/yRand/length*i ) * p[2]*yRand2 + cy; | |
ctx.beginPath(); | |
ctx.moveTo(x, y); | |
ctx.arc(x, y, p[2]*4, 0, TAU); | |
ctx.fill(); | |
}); | |
return canvas; | |
}; | |
var Galaxy = function(x,y) { | |
var g = this; | |
g.x = x; | |
g.y = y; | |
g.stars = []; | |
g.dust = []; | |
g.drag = r(); | |
g.angleOffsetX = TAU*r(); | |
g.angleOffsetY = TAU*r(); | |
g.realAngleOffsetX = 0; | |
g.realAngleOffsetY = 0; | |
g.color = { | |
r: Math.round(50+(r()*100)), | |
g: Math.round(50+(r()*100)), | |
b: Math.round(150+(r()*100)) | |
}; | |
var calculateStarDustParams = o => { | |
o.angle = angle2([g.x,g.y], [o.x,o.y]); | |
o.distance = distance2([g.x,g.y], [o.x,o.y]); | |
o.xAspect = [o.x/o.y]; | |
o.yAspect = [o.y/o.x]; | |
}; | |
g.calculateCenter = () => { | |
if (!g.stars.length) return; | |
g.x = g.stars | |
.map(s => s.x) | |
.reduce((previous,current) => previous + current) | |
/ g.stars.length; | |
g.y = g.stars | |
.map(s => s.y) | |
.reduce((previous,current) => previous + current) | |
/ g.stars.length; | |
g.stars.forEach(calculateStarDustParams); | |
g.dust.forEach(calculateStarDustParams); | |
}; | |
}; | |
var Star = function(x, y, spread) { | |
var s = this; | |
s.x = x + Math.cos(TAU*r()) * spread; | |
s.y = y + Math.sin(TAU*r()) * spread; | |
s.radius = r()+0.25; | |
s.speed = r(); | |
}; | |
var Dust = function(x, y, size) { | |
var d = this; | |
d.x = x; | |
d.y = y; | |
d.size = size; | |
d.texture = createDustParticle(); | |
d.speed = r(); | |
}; | |
var updateStarDust = (s,g) => { | |
if (g == currentGalaxy && drawingMode) return; | |
s.angle += (0.5+(s.speed*0.5))/s.distance; | |
s.x = g.x + (Math.cos(s.angle+g.realAngleOffsetX)*s.distance); | |
s.y = g.y + (Math.sin(s.angle+g.realAngleOffsetY)*s.distance); | |
}; | |
var update = () => { | |
galaxies.forEach(g => { | |
if (g != currentGalaxy) { | |
g.realAngleOffsetX += | |
g.realAngleOffsetX < g.angleOffsetX | |
? (g.angleOffsetX-g.realAngleOffsetX)*0.05 : 0; | |
g.realAngleOffsetY += | |
g.realAngleOffsetY < g.angleOffsetY | |
? (g.angleOffsetY-g.realAngleOffsetY)*0.05 : 0; | |
} | |
g.stars.forEach((s) => { | |
/*s.distance -= s.distance < 2 | |
? 0 | |
: TAU/s.distance;*/ | |
updateStarDust(s,g); | |
}); | |
g.dust.forEach((d) => { | |
/*d.distance -= d.distance < 50 | |
? 0 | |
: TAU/d.distance;*/ | |
updateStarDust(d,g); | |
}); | |
}); | |
}; | |
var render = () => { | |
dustCtx.globalCompositeOperation = 'source-over'; | |
dustCtx.fillStyle = 'rgba(0,0,0,.05)'; | |
dustCtx.fillRect(0,0,w,h); | |
dustCtx.globalCompositeOperation = 'lighter'; | |
starCtx.clearRect(0,0,w,h); | |
starCtx.fillStyle = '#ffffff'; | |
starCtx.strokeStyle = 'rgba(255,255,255,.05)'; | |
starCtx.beginPath(); | |
if (drawingMode) galaxies.forEach(g => { | |
starCtx.moveTo(g.x, g.y); | |
starCtx.arc(g.x,g.y,2,0,TAU); | |
}); | |
galaxies.forEach(g => { | |
g.stars.forEach(s => { | |
starCtx.moveTo(s.x, s.y); | |
starCtx.arc(s.x,s.y,s.radius,0,TAU); | |
}); | |
g.dust.forEach(d => { | |
dustCtx.drawImage(d.texture, d.x-(d.size*0.5), d.y-(d.size*0.5), d.size, d.size) | |
}); | |
}); | |
dustCtx.fill(); | |
starCtx.fill(); | |
if (drawingMode && currentGalaxy) { | |
starCtx.beginPath(); | |
currentGalaxy.stars.forEach((s,i) => { | |
starCtx.moveTo(s.x, s.y); | |
starCtx.lineTo(currentGalaxy.x, currentGalaxy.y); | |
}); | |
starCtx.stroke(); | |
} | |
}; | |
var currentGalaxy = null; | |
var drawingMode = false; | |
var activateDraw = e => { | |
drawingMode = true; | |
mouse.pos.x = e.layerX; | |
mouse.pos.y = e.layerY; | |
currentGalaxy = new Galaxy(e.layerX, e.layerY); | |
galaxies.push(currentGalaxy); | |
}; | |
var disableDraw = e => { | |
drawingMode = false; | |
currentGalaxy = null; | |
}; | |
var draw = e => { | |
if (!drawingMode) return; | |
currentGalaxy.stars.push(new Star(mouse.pos.x, mouse.pos.y, mouse.speed)); | |
currentGalaxy.stars.push(new Star(mouse.pos.x, mouse.pos.y, mouse.speed)); | |
currentGalaxy.stars.push(new Star(mouse.pos.x, mouse.pos.y, mouse.speed)); | |
if (mouse.speed*1.5 > 13 && mouse.speed < 100) currentGalaxy.dust.push( | |
new Dust( | |
(currentGalaxy.x + (Math.cos(TAU*r()) * mouse.speed*0.1)), | |
(currentGalaxy.y + (Math.sin(TAU*r()) * mouse.speed*0.1)), | |
mouse.speed*1.5 | |
) | |
); | |
currentGalaxy.calculateCenter(); | |
}; | |
var loop = () => { | |
draw(); | |
update(); | |
render(); | |
window.requestAnimationFrame(loop); | |
}; | |
loop(); | |
var moveEvent = e => { | |
mouse.speed = distance2( | |
[e.layerX,e.layerY], | |
[mouse.pos.x,mouse.pos.y] | |
); | |
mouse.pos.x = e.layerX; | |
mouse.pos.y = e.layerY; | |
draw(e); | |
}; | |
window.addEventListener('mousedown', activateDraw); | |
window.addEventListener('mousemove', moveEvent); | |
window.addEventListener('mouseup',disableDraw); | |
window.addEventListener('touchstart', activateDraw); | |
window.addEventListener('touchmove', moveEvent); | |
window.addEventListener('touchend',disableDraw); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
body { | |
margin:0; | |
background-color:#000; | |
color:#fff; | |
font-family: 'Raleway', sans-serif; | |
} | |
canvas { | |
position:absolute; | |
top:0; | |
left:0; | |
width:100%; | |
height:100%; | |
} | |
.intro { | |
position:absolute; | |
z-index:2; | |
top:25px; | |
left:25%; | |
width:50%; | |
text-align:center; | |
pointer-events:none; | |
} | |
h1 { | |
margin:10px 0; | |
text-shadow:0 0 20px rgba(255,255,255,.5); | |
font-family: 'Berkshire Swash', cursive; | |
} | |
p { | |
color:rgba(255,255,255,.75); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment