A cool starry night scene. Made for Christmas (actually new year) 2016.

Starlight-Renderer.js 3.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. "use strict";
  2. import random from './Random'; // Bounded random number generation
  3. import Vector from './Vector'; // 2D vector class
  4. import Range from './Range'; // Range representation
  5. // Subclasses
  6. import Star from './Star';
  7. // ~~~
  8. class StarlightRenderer
  9. {
  10. constructor(canvas)
  11. {
  12. // The colour of the sky in the background.
  13. this.skyColour1 = "hsla(248, 100%, 23%, 1)";
  14. this.skyColour2 = "hsla(248, 100%, 5%, 1)";
  15. this.starCount = 1000;
  16. this.starSize = new Range(2, 10);
  17. this.twinkleDuration = new Range(0.5, 1.5);
  18. this.twinkleSize = new Range(1.2, 2);
  19. // ~~~
  20. this.canvas = canvas;
  21. this.context = canvas.getContext("2d");
  22. this.trackWindowSize();
  23. this.lastFrame = +new Date() / 1000;
  24. // ~~~
  25. this.stars = [];
  26. for(let i = 0; i < this.starCount; i++)
  27. {
  28. let nextStar = new Star(
  29. this.canvas,
  30. new Vector(
  31. random(0, this.canvas.width),
  32. random(0, this.canvas.height)
  33. ),
  34. random(this.starSize.min, this.starSize.max)
  35. );
  36. nextStar.pointCount = random(4, 8);
  37. // Make larger stars tend towards having longer points
  38. nextStar.innerRingRatio = random(0.2, 0.8, true);
  39. nextStar.innerRingRatio = (nextStar.innerRingRatio + nextStar.innerRingRatio*(1 - (nextStar.size / this.starSize.max))) / 2;
  40. nextStar.rotation = random(0, Math.PI*2, true);
  41. nextStar.rotationStep = random(0.1, 1, true);
  42. if(random(0, 2) == 0)
  43. nextStar.rotationStep *= -1;
  44. nextStar.alpha = random(0.2, 0.9, true);
  45. nextStar.twinkleDuration = random(this.twinkleDuration.min, this.twinkleDuration.max, true);
  46. nextStar.twinkleSize = random(this.twinkleSize.min, this.twinkleSize.max, true);
  47. this.stars.push(nextStar);
  48. }
  49. }
  50. generateGradients(canvas, context)
  51. {
  52. this.generateBackgroundGradient(canvas, context);
  53. this.generateOverlayGradient(canvas, context);
  54. }
  55. generateBackgroundGradient(canvas, context)
  56. {
  57. this.backgroundGradient = context.createRadialGradient(
  58. canvas.width / 2, canvas.height, canvas.height / 5,
  59. canvas.width / 2, canvas.height, canvas.height * 1.2
  60. );
  61. this.backgroundGradient.addColorStop(0, this.skyColour1);
  62. this.backgroundGradient.addColorStop(1, this.skyColour2);
  63. }
  64. generateOverlayGradient(canvas, context)
  65. {
  66. this.overlayGradient = context.createRadialGradient(
  67. canvas.width / 2, canvas.height, canvas.height / 5,
  68. canvas.width / 2, canvas.height, canvas.height * 1.2
  69. );
  70. this.overlayGradient.addColorStop(0, "hsla(248, 100%, 7%, 0.6)");
  71. this.overlayGradient.addColorStop(1, "hsla(248, 100%, 2%, 0.01)");
  72. }
  73. nextFrame()
  74. {
  75. this.update();
  76. this.render(this.canvas, this.context);
  77. requestAnimationFrame(this.nextFrame.bind(this));
  78. }
  79. update()
  80. {
  81. // Calculate the time between this frame and the last one
  82. this.currentFrame = (+new Date()) / 1000;
  83. this.currentDt = this.currentFrame - this.lastFrame;
  84. // Update all the stars
  85. for(let star of this.stars)
  86. star.update(this.currentDt);
  87. this.lastFrame = this.currentFrame;
  88. }
  89. render(canvas, context)
  90. {
  91. // Background
  92. context.fillStyle = this.backgroundGradient;
  93. context.fillRect(0, 0, this.canvas.width, this.canvas.height);
  94. // Stars
  95. for(let star of this.stars)
  96. star.render(context);
  97. // Overlay
  98. context.fillStyle = this.overlayGradient;
  99. context.fillRect(0, 0, this.canvas.width, this.canvas.height);
  100. }
  101. /**
  102. * Updates the canvas size to match the current viewport size.
  103. */
  104. matchWindowSize() {
  105. this.canvas.width = window.innerWidth;
  106. this.canvas.height = window.innerHeight;
  107. this.generateGradients(this.canvas, this.context);
  108. //this.render(this.context);
  109. }
  110. /**
  111. * Makes the canvas size track the window size.
  112. */
  113. trackWindowSize() {
  114. this.matchWindowSize();
  115. window.addEventListener("resize", this.matchWindowSize.bind(this));
  116. }
  117. }
  118. window.addEventListener("load", function (event) {
  119. var canvas = document.getElementById("canvas-main"),
  120. renderer = new StarlightRenderer(canvas);
  121. renderer.nextFrame();
  122. window.renderer = renderer;
  123. });