blackhole.js 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. // Created by Darryl Huffman
  2. // https://codepen.io/darrylhuffman/pen/vXjVJg
  3. blackhole('#blackhole');
  4. function blackhole(element) {
  5. var h = $(element).height(),
  6. w = $(element).width(),
  7. cw = w,
  8. ch = h,
  9. maxorbit = 300, // distance from center
  10. centery = ch/2,
  11. centerx = cw/2;
  12. var startTime = new Date().getTime();
  13. var currentTime = 0;
  14. var stars = [];
  15. window.expanse = false; // if clicked
  16. var canvas = $('<canvas/>').attr({width: cw, height: ch}).appendTo(element);
  17. var context = document.querySelector(element).querySelector('canvas').getContext("2d");
  18. context.globalCompositeOperation = "multiply";
  19. function setDPI(canvas, dpi) {
  20. // Set up CSS size if it's not set up already
  21. if (!canvas.get(0).style.width)
  22. canvas.get(0).style.width = `${canvas.get(0).width}px`;
  23. if (!canvas.get(0).style.height)
  24. canvas.get(0).style.height = `${canvas.get(0).height}px`;
  25. var scaleFactor = dpi / 96;
  26. canvas.get(0).width = Math.ceil(canvas.get(0).width * scaleFactor);
  27. canvas.get(0).height = Math.ceil(canvas.get(0).height * scaleFactor);
  28. var ctx = canvas.get(0).getContext('2d');
  29. ctx.scale(scaleFactor, scaleFactor);
  30. }
  31. function rotate(cx, cy, x, y, angle) {
  32. var radians = angle,
  33. cos = Math.cos(radians),
  34. sin = Math.sin(radians),
  35. nx = (cos * (x - cx)) + (sin * (y - cy)) + cx,
  36. ny = (cos * (y - cy)) - (sin * (x - cx)) + cy;
  37. return [nx, ny];
  38. }
  39. setDPI(canvas, 218);
  40. var star = function(){
  41. // Get a weighted random number, so that the majority of stars will form in the center of the orbit
  42. var rands = [];
  43. rands.push(Math.random() * (maxorbit/2) + 1);
  44. rands.push(Math.random() * (maxorbit/2) + maxorbit);
  45. this.orbital = (rands.reduce(function(p, c) {
  46. return p + c;
  47. }, 0) / rands.length);
  48. // Done getting that random number, it's stored in this.orbital
  49. this.x = centerx; // All of these stars are at the center x position at all times
  50. this.y = centery + this.orbital; // Set Y position starting at the center y + the position in the orbit
  51. this.yOrigin = centery + this.orbital; // this is used to track the particles origin
  52. this.speed = (Math.floor(Math.random() * 1.5) + 1)*Math.PI/180; // The rate at which this star will orbit
  53. this.rotation = 0; // current Rotation
  54. this.startRotation = (Math.floor(Math.random() * 360) + 1)*Math.PI/180; // Starting rotation. If not random, all stars will be generated in a single line.
  55. this.id = stars.length; // This will be used when expansion takes place.
  56. this.collapseBonus = this.orbital - (maxorbit * 0.7); // This "bonus" is used to randomly place some stars outside of the blackhole on hover
  57. if(this.collapseBonus < 0){ // if the collapse "bonus" is negative
  58. this.collapseBonus = 0; // set it to 0, this way no stars will go inside the blackhole
  59. }
  60. stars.push(this);
  61. this.color = `rgba(255,180,255,${1 - ((this.orbital) / 255 * 0.8)})`; // Color the star white, but make it more transparent the further out it is generated
  62. this.hoverPos = centery + (maxorbit/2) + this.collapseBonus; // Where the star will go on hover of the blackhole
  63. this.expansePos = centery + (this.id%100)*-10 + (Math.floor(Math.random() * 20) + 1); // Where the star will go when expansion takes place
  64. this.prevR = this.startRotation;
  65. this.prevX = this.x;
  66. this.prevY = this.y;
  67. // The reason why I have yOrigin, hoverPos and expansePos is so that I don't have to do math on each animation frame. Trying to reduce lag.
  68. }
  69. star.prototype.draw = function(){
  70. // the stars are not actually moving on the X axis in my code. I'm simply rotating the canvas context for each star individually so that they all get rotated with the use of less complex math in each frame.
  71. if(!window.expanse){
  72. this.rotation = this.startRotation + (currentTime * this.speed);
  73. if(this.y > this.yOrigin){
  74. this.y-= 2.5;
  75. }
  76. if(this.y < this.yOrigin-4){
  77. this.y+= (this.yOrigin - this.y) / 10;
  78. }
  79. } else {
  80. this.rotation = this.startRotation + (currentTime * (this.speed / 2));
  81. if(this.y > this.expansePos){
  82. this.y-= Math.floor(this.expansePos - this.y) / -140;
  83. }
  84. }
  85. context.save();
  86. context.fillStyle = this.color;
  87. context.strokeStyle = this.color;
  88. context.beginPath();
  89. var oldPos = rotate(centerx,centery,this.prevX,this.prevY,-this.prevR);
  90. context.moveTo(oldPos[0],oldPos[1]);
  91. context.translate(centerx, centery);
  92. context.rotate(this.rotation);
  93. context.translate(-centerx, -centery);
  94. context.lineTo(this.x,this.y);
  95. context.stroke();
  96. context.restore();
  97. this.prevR = this.rotation;
  98. this.prevX = this.x;
  99. this.prevY = this.y;
  100. }
  101. window.expanse = false;
  102. window.requestFrame = (function(){
  103. return window.requestAnimationFrame ||
  104. window.webkitRequestAnimationFrame ||
  105. window.mozRequestAnimationFrame ||
  106. function( callback ){
  107. window.setTimeout(callback, 1000 / 60);
  108. };
  109. })();
  110. function loop(){
  111. var now = new Date().getTime();
  112. currentTime = (now - startTime) / 50;
  113. context.fillStyle = 'rgba(25,25,25,0.2)'; // somewhat clear the context, this way there will be trails behind the stars
  114. context.fillRect(0, 0, cw, ch);
  115. for(var i = 0; i < stars.length; i++){ // For each star
  116. if(stars[i] != stars){
  117. stars[i].draw(); // Draw it
  118. }
  119. }
  120. requestFrame(loop);
  121. }
  122. function init(){
  123. context.fillStyle = 'rgba(25,25,25,1)'; // Initial clear of the canvas, to avoid an issue where it all gets too dark
  124. context.fillRect(0, 0, cw, ch);
  125. for(var i = 0; i < 2000; i++){ // create 2000 stars
  126. new star();
  127. }
  128. loop();
  129. }
  130. init();
  131. }