main.js (5918B)
1 'use strict;' 2 3 Math.TAU = 2 * Math.PI; 4 5 (function() { 6 let start_time; 7 let elapsed_time; 8 let previous_timestamp; 9 10 let polygons_vertices = []; 11 let polygon_velocity = []; 12 13 let hsv_values = [[0, 1, 0.7]]; 14 let color_wheel_velocity = 0.01; 15 16 let wanted_number_of_polygons = 50; 17 let pixels_per_step = 10; 18 19 function hsv_to_rgb(hue, saturation, value) { 20 // https://en.wikipedia.org/wiki/HSL_and_HSV#HSV_to_RGB 21 // H in [0, 1] (position on the color wheel) 22 // S in [0, 1] 23 // V in [0, 1] 24 25 let chroma = value * saturation; 26 27 let hue_tag = hue * 6; 28 29 let x = chroma * (1 - Math.abs((hue_tag % 2) - 1)); 30 31 let [red_1, green_1, blue_1] = ((hue_tag, chroma, x)=>{ 32 if (hue_tag >= 0 && hue_tag < 1) { return [chroma, x, 0]; 33 } else if (hue_tag >= 1 && hue_tag < 2) { return [x, chroma, 0]; 34 } else if (hue_tag >= 2 && hue_tag < 3) { return [0, chroma, x]; 35 } else if (hue_tag >= 3 && hue_tag < 4) { return [0, x, chroma]; 36 } else if (hue_tag >= 4 && hue_tag < 5) { return [x, 0, chroma]; 37 } else if (hue_tag >= 5 && hue_tag < 6) { return [chroma, 0, x]; 38 } 39 })(hue_tag, chroma, x); 40 41 let m = value - chroma; 42 43 return [red_1 + m, green_1 + m, blue_1 + m]; 44 } 45 46 function move_and_bounce_1d(position, velocity, maximum_value) { 47 let new_position = position + velocity; 48 49 if (new_position > maximum_value) { 50 return [2 * maximum_value - new_position, -Math.abs(velocity)]; 51 } else if (new_position < 0) { 52 return [Math.abs(new_position), Math.abs(velocity)]; 53 } else { 54 return [new_position, velocity]; 55 }; 56 57 } 58 59 function move_and_bounce_2d(position, velocity, width, height) { 60 let [position_x, velocity_x] = move_and_bounce_1d(position[0], velocity[0], width); 61 let [position_y, velocity_y] = move_and_bounce_1d(position[1], velocity[1], height); 62 63 return [ 64 [position_x, position_y], 65 [velocity_x, velocity_y], 66 ]; 67 } 68 69 function move_polygon(polygon_vertices, polygon_velocity) { 70 for (let vertex_number = 0; vertex_number < polygon_vertices.length; vertex_number++) { 71 let [new_position, new_velocity] = move_and_bounce_2d( 72 polygon_vertices[vertex_number], 73 polygon_velocity[vertex_number], 74 canvas.width, 75 canvas.height, 76 ); 77 78 polygon_vertices[vertex_number] = new_position; 79 polygon_velocity[vertex_number] = new_velocity; 80 } 81 }; 82 83 function draw_polygon(ctx, polygon_vertices, rgb) { 84 ctx.beginPath(); 85 86 ctx.strokeStyle = `rgb(${rgb[0]}, ${rgb[1]}, ${rgb[2]})`; 87 88 ctx.moveTo(polygon_vertices[0][0], polygon_vertices[0][1]); 89 90 for (let vertex_number = 1; vertex_number < polygon_vertices.length; vertex_number++) { 91 ctx.lineTo(polygon_vertices[vertex_number][0], polygon_vertices[vertex_number][1]); 92 } 93 94 ctx.closePath(); 95 ctx.stroke(); 96 } 97 98 function uniform_random_direction() { 99 let uniform = Math.random(); 100 let direction_vector = [ 101 Math.cos(uniform * Math.TAU), 102 Math.sin(uniform * Math.TAU), 103 ]; 104 return direction_vector; 105 } 106 107 function step_polygons() { 108 if (polygons_vertices.length >= wanted_number_of_polygons) { 109 // Remove oldest polygon, the one at position 0. 110 polygons_vertices = polygons_vertices.slice(1, polygons_vertices.length); 111 } 112 113 // Get the newest polygon at position n-1. 114 let current_polygon_vertices = polygons_vertices[polygons_vertices.length - 1]; 115 116 // Clone our latest polygon. 117 let new_polygon_vertices = current_polygon_vertices.map(vertex => vertex.map(axis => axis)); 118 119 move_polygon(new_polygon_vertices, polygon_velocity); 120 121 polygons_vertices.push(new_polygon_vertices); 122 } 123 124 function step_hsv_values() { 125 if (hsv_values.length >= wanted_number_of_polygons) { 126 // Remove oldest hsv value, the one at position 0. 127 hsv_values = hsv_values.slice(1, hsv_values.length); 128 } 129 130 // Get the newest hsv_value at position n-1. 131 let current_hsv_value = hsv_values[hsv_values.length - 1]; 132 133 // Clone. 134 let new_hsv_value = current_hsv_value.map(axis => axis); 135 136 // Spin the hue around in the color wheel. 137 new_hsv_value[0] = (new_hsv_value[0] + color_wheel_velocity) % 1; 138 139 hsv_values.push(new_hsv_value); 140 } 141 142 function draw_polygons(ctx, polygons_vertices, hsv_values) { 143 for (let current_polygon = 0; current_polygon < polygons_vertices.length; current_polygon++) { 144 let rgb = hsv_to_rgb( 145 hsv_values[current_polygon][0], 146 hsv_values[current_polygon][1], 147 hsv_values[current_polygon][2], 148 ).map(x => 255 * x); 149 draw_polygon(ctx, polygons_vertices[current_polygon], rgb); 150 } 151 } 152 153 function step(timestamp) { 154 if (start_time === undefined) { 155 start_time = timestamp; 156 } 157 158 elapsed_time = timestamp - start_time; 159 160 delta_time = timestamp - previous_timestamp; 161 162 let canvas = document.getElementById('canvas'); 163 let ctx = canvas.getContext('2d'); 164 165 ctx.clearRect(0, 0, canvas.width, canvas.height); 166 167 step_polygons(); 168 step_hsv_values(); 169 170 draw_polygons(ctx, polygons_vertices, hsv_values); 171 172 previous_timestamp = timestamp; 173 174 window.requestAnimationFrame(step) 175 } 176 177 function resize_window() { 178 // https://stackoverflow.com/a/32119392 179 180 let canvas = document.getElementById('canvas'); 181 182 canvas.width = window.innerWidth; 183 canvas.style.width = window.innerWidth; 184 canvas.height = window.innerHeight; 185 canvas.style.height = window.innerHeight; 186 } 187 188 function reset_world() { 189 polygons_vertices = [[ 190 [canvas.width / 2, canvas.height / 3], 191 [2 * canvas.width / 3, 2 * canvas.height / 3], 192 [canvas.width / 3, 2 * canvas.height / 3], 193 ]]; 194 195 polygon_velocity = [ 196 uniform_random_direction(), 197 uniform_random_direction(), 198 uniform_random_direction(), 199 ].map(vertex => vertex.map(axis => pixels_per_step * axis)); 200 } 201 202 function main() { 203 let canvas = document.getElementById('canvas'); 204 let ctx = canvas.getContext('2d'); 205 206 resize_window(); 207 reset_world(); 208 209 canvas.addEventListener('click', reset_world); 210 211 window.addEventListener('resize', resize_window) 212 213 window.requestAnimationFrame(step); 214 } 215 216 window.addEventListener('load', main); 217 })();