'Cluster', space-time travel,
inspired by 'Nebula'
A Pen by valery sntx on CodePen.
'Cluster', space-time travel,
inspired by 'Nebula'
A Pen by valery sntx on CodePen.
| <!-- click canvas to make lightyears pass --> | |
| <div id="status"> </div> |
| var canvas, c, w, h, | |
| twoPI = Math.PI * 2, | |
| mX, mY, status; | |
| window.onload = function(){ | |
| canvas = document.createElement('canvas') | |
| w = canvas.width = window.innerWidth * 4; | |
| h = canvas.height = window.innerHeight * 4 ; | |
| c = canvas.getContext('2d'); | |
| status = document.getElementById("status"); | |
| document.body.appendChild(canvas); | |
| generate(); | |
| canvas.addEventListener('click', function(){ | |
| generate(); | |
| }); | |
| window.addEventListener('resize', function(){ | |
| canvas.width = w = window.innerWidth * 4 ; | |
| canvas.height = h = window.innerHeight * 4 ; | |
| }); | |
| } | |
| function generate(){ | |
| status.style.display = "block"; | |
| status.innerHTML = 'scaling time...'; | |
| window.setTimeout( function(){ | |
| var nebula = new Nebula(6,100); | |
| }, 100); | |
| } | |
| function Star (x, y, r, c, b){ | |
| this.x = x; | |
| this.y = y; | |
| this.r = r; | |
| this.c = c; | |
| this.b = b; | |
| } | |
| function Colour (h, s, l, a){ | |
| this.h = h; | |
| this.s = s; | |
| this.l = l; | |
| this.a = a; | |
| this.fillStyle = function(){ | |
| return "hsla(" + this.h + "," + this.s + "%," + l + "%," + this.a + ")"; | |
| }; | |
| } | |
| function Nebula (clusters,starsPerCluster){ | |
| this.points = []; | |
| this.draw = function(){ | |
| var p = this.points; | |
| // background star-field | |
| c.fillStyle = "hsla(0,100%,100%,1)"; | |
| for(var i = 0; i < 1000; i++){ | |
| c.beginPath(); | |
| c.arc(Math.random()*w, Math.random()*h, Math.random()*0.75, 0, twoPI, true); | |
| c.closePath(); | |
| c.fill(); | |
| } | |
| // nebula clouds | |
| c.globalCompositeOperation = "lighter"; | |
| for(var i = 0; i < p.length; i ++){ | |
| var grad = c.createRadialGradient(p[i].x, p[i].y, p[i].r, p[i].x, p[i].y, p[i].b); | |
| grad.addColorStop(0, "hsla(" + p[i].c.h + ",100%,20%,0.06)"); | |
| grad.addColorStop(1, "hsla(" + p[i].c.h + ",100%,20%,0)" ); | |
| c.fillStyle = grad; | |
| c.beginPath(); | |
| c.arc(p[i].x, p[i].y, p[i].b, 0, twoPI, true); | |
| c.closePath(); | |
| c.fill(); | |
| } | |
| // nebula stars | |
| for(var i = 0; i < p.length; i++){ | |
| c.beginPath(); | |
| c.arc(p[i].x, p[i].y, p[i].r, 0, twoPI, true); | |
| c.closePath(); | |
| c.fillStyle = p[i].c.fillStyle(); | |
| c.fill(); | |
| } | |
| // interstellar dust | |
| c.globalCompositeOperation = "source-over"; | |
| var x1 = 0, | |
| y1 = Math.random() * h * 0.40 + h * 0.30, | |
| x2 = w, | |
| y2 = Math.random() * h * 0.40 + h * 0.30, | |
| v1 = x2-x1, | |
| v2 = y2-y1, | |
| vD = Math.sqrt(v1*v1 + v2*v2), | |
| vA = Math.atan2(v2, v1); | |
| for(var i = 0; i < 1000; i++){ | |
| var rnd = i - 500, | |
| r = Math.abs(Math.random()*rnd/2)+10, | |
| x = x1 + (Math.cos(vA) * vD/1000 * i) + Math.random() * rnd - rnd/2, | |
| y = y1 + (Math.sin(vA) * vD/1000 * i) + Math.random() * rnd - rnd/2, | |
| grad = c.createRadialGradient(x,y,0,x,y,r); | |
| grad.addColorStop(0, "hsla(0,0%,0%,0.08)"); | |
| grad.addColorStop(1, "hsla(0,0%,0%,0)"); | |
| c.fillStyle = grad; | |
| c.beginPath(); | |
| c.arc(x,y,r,0,twoPI,true); | |
| c.closePath(); | |
| c.fill(); | |
| } | |
| c.fillStyle = "hsla(0,100%,100%,0.25)"; | |
| for(var i = 0; i < 200; i++){ | |
| c.beginPath(); | |
| c.arc(Math.random()*w, Math.random()*h, Math.random()*0.75, 0, twoPI, true); | |
| c.closePath(); | |
| c.fill(); | |
| } | |
| // close stars | |
| c.globalCompositeOperation = "lighter"; | |
| for(var i = 0; i < 5; i++){ | |
| var x = Math.random()*w, | |
| y = Math.random()*h, | |
| r = Math.random()*400, | |
| hue = Math.floor(Math.random()*360), | |
| grad = c.createRadialGradient(x,y,0,x,y,r); | |
| grad.addColorStop(0, "hsla(" + hue + "0,25%,100%,1)"); | |
| grad.addColorStop(0.01, "hsla(" + hue + ",25%,75%,0.75)"); | |
| grad.addColorStop(1, "hsla(" + hue + ",0%,0%,0)"); | |
| c.fillStyle = grad; | |
| c.beginPath(); | |
| c.arc(x,y,r,0,twoPI,true); | |
| c.closePath(); | |
| c.fill(); | |
| } | |
| window.setTimeout( function(){ | |
| status.style.display = "none"; | |
| }, 500); | |
| }; | |
| this.init = function(){ | |
| for(var j = 0; j < clusters; j++){ | |
| var x1 = Math.random() * w * 0.50 + w * 0.25, | |
| y1 = Math.random() * h * 0.40 + h * 0.30; | |
| for(var i = 0; i < starsPerCluster; i++){ | |
| var x = x1 + Math.cos(Math.random()*twoPI) * (Math.random() * 7000/(i+1)), | |
| y = y1 + Math.sin(Math.random()*twoPI) * (Math.random() * 7000/(i+1)), | |
| r = Math.random()*2; | |
| if(x > w) continue; | |
| if(x < 0) continue; | |
| if(y > h) continue; | |
| if(y < 0) continue; | |
| var col = new Colour( Math.random()*360, 50, Math.floor(Math.random()*50+50), 1 ); | |
| this.points.push( new Star(x, y, r, col, Math.random()*200) ); | |
| } | |
| } | |
| this.draw(); | |
| }; | |
| this.init(); | |
| }; |
| <script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script> |
| *{ | |
| margin:0;padding:0;-moz-box-sizing:border-box;-webkit-box-sizing:border-box} | |
| body{ | |
| overflow-x: hidden; | |
| font-size:7%;background:#222;font-family: terminal;padding:20px 0 0 20px} | |
| canvas{ | |
| display:block; | |
| position: absolute; | |
| top: -30%; | |
| left: -30%; | |
| background:transparent; | |
| opacity:0.4; | |
| animation-name:rotater; | |
| animation-duration: 60s; | |
| animation-iteration-count: infinite; | |
| animation-timing-function: ease-in-out; | |
| } | |
| #status{ | |
| position:absolute; | |
| left:50%;top:50%; | |
| margin:-25px 0 0 -150px; | |
| width:300px; | |
| height:50px; | |
| background:transparent; | |
| border:0px; | |
| font-size:100px; | |
| text-transform:lowercase; | |
| color:#888; | |
| text-align:center; | |
| line-height:50px; | |
| display:none; | |
| } | |
| @keyframes rotater { | |
| 0% { transform:rotate(0) scale(1) } | |
| 25% { transform:rotate(20deg) scale(1) } | |
| 50% { transform:rotate(40deg) scale(3) } | |
| 75% { transform:rotate(20deg) scale(1) } | |
| 100% { transform:rotate(-10deg) scale(1) } | |
| } |