From 230f98aa491be2d30e0162022ae7380948df4bbf Mon Sep 17 00:00:00 2001 From: Logan Hunt Date: Thu, 17 Mar 2022 10:32:22 -0600 Subject: [PATCH] Updates with dvd logo animation; add cool predictions; update index page with description --- .DS_Store | Bin 6148 -> 0 bytes dvd-logo/index.html | 32 ++++++-- dvd-logo/js/script.js | 169 ++++++++++++++++++++++++++++++++---------- index.html | 6 +- 4 files changed, 161 insertions(+), 46 deletions(-) delete mode 100644 .DS_Store diff --git a/.DS_Store b/.DS_Store deleted file mode 100644 index 7226a5624dbb2fee54663d7181affd8f060b4642..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeHKO>fgM7=FE$&25ELq#+@}q)1%1vPz&0aS5dyxDo^hK%;3&n~}yUGiHJ2uL7k{dL>xt8v5aboNWClz zvSvoE0GW&-+I}Q_^kg_ImI2Gazs`VscMH^}gpx;5=KcK&&5xf-)FzBo)UA7M(I3oh z%xL*RatMPCOkO+-<3NlXViX67^7XWWmv{+2lgaIE=k9ddne5fk z+P||motF63o44*gK6n%LWAQ;V&G3a)a@pV+d_*I&_azz1=;!lvy!rly$y3heJX@=5 zmgFoG@{YL;8&wVjC6Be`Sl^l1s3x}@yZ3>gI&61z%uY>7?9rw z2SuW5aHdgxI#8)A0I-N*C1|Ukf1o1=fUd!rMvOq1b_HrzX08~_+#Q&9hu$^zGmYAv zn3@@J%*@Q(P?))TFh`jab2Zx9GGH0VGEmUnsyzQ6uD<_goovrCU>W$Y7+}Sw*KA-* z=4@TqoIGnClvgNHq}@!TazSN|W7UvH@ii1B&^fXJbPdilVgzD;1QZRnvJCuH27Uo@ CW!%yL diff --git a/dvd-logo/index.html b/dvd-logo/index.html index fc07484..d10f527 100644 --- a/dvd-logo/index.html +++ b/dvd-logo/index.html @@ -6,20 +6,42 @@ - +
+ + +
+ +
+ +
+
diff --git a/dvd-logo/js/script.js b/dvd-logo/js/script.js index 9703f88..870ec11 100644 --- a/dvd-logo/js/script.js +++ b/dvd-logo/js/script.js @@ -1,42 +1,135 @@ -const randUpTo = (max) => Math.random() * max - -const randomHue = (element) => { - element.style.filter = `brightness(0.5) sepia(1) saturate(10000%) hue-rotate(${randUpTo(360)}deg)`; -} - -const clamp = (val, max) => val > max ? max : val; - - -const updateLogo = (logo, element) => { - const screen_height = window.innerHeight; - const screen_width = window.innerWidth; - - const collide_y = logo.y <= 0 || (logo.y + element.clientHeight) >= screen_height; - const collide_x = logo.x <= 0 || (logo.x + element.clientWidth) >= screen_width; - - logo.dx *= (collide_x ? -1 : 1); - logo.dy *= (collide_y ? -1 : 1); - - if (collide_y || collide_x) { - randomHue(element); - } - - // Clamp in case user changes screen size - logo.x = clamp(logo.x + logo.dx, screen_width - element.clientWidth); - logo.y = clamp(logo.y + logo.dy, screen_height - element.clientHeight); - - element.style.left = `${logo.x}px`; - element.style.top = `${logo.y}px`; +const global_state = { + iterations: 0 }; -window.onload = () => { - let logo = { - x: randUpTo(window.innerWidth), - y: randUpTo(window.innerHeight), - dx: 2, - dy: 2 - }; - const dvdLogo = document.getElementById("dvd-logo"); +const ALLOWED_ERROR = 0.01; - setInterval(() => updateLogo(logo, dvdLogo), 15); // ~ 67 hz +const randUpTo = (max) => Math.random() * max + +const withinError = (a, b, error=ALLOWED_ERROR) => Math.abs(a - b) < error; + +const normalize = (vec) => { + const magnitude = Math.sqrt(vec.x * vec.x + vec.y * vec.y); + return { + x: vec.x / magnitude, + y: vec.y / magnitude + }; } + +const findNextIntersection = ({x, y, dx, dy}, max_width, max_height) => { + const slope = dy / dx; + const y_intercept = y - (slope * x); + const intersections = [ + {x: 0, y: y_intercept}, + {x: max_width, y: slope * max_width + y_intercept}, + {x: -y_intercept / slope, y: 0}, + {x: (max_height - y_intercept) / slope, y: max_height} + ]; + return intersections.find((cross) => { + const cross_dir_hat = normalize({x: cross.x - x, y: cross.y - y}); + const logo_dir_hat = normalize({x: dx, y: dy}); + const in_bounds = cross.x >= 0 && cross.x <= max_width && cross.y >= 0 && cross.y <= max_height; + return in_bounds && withinError(cross_dir_hat.x, logo_dir_hat.x) && withinError(cross_dir_hat.y, logo_dir_hat.y); + }); +}; + +const handleCollision = (obj, max_width, max_height) => { + const collide_y = obj.y <= 0 || obj.y >= max_height; + const collide_x = obj.x <= 0 || obj.x >= max_width; + + obj.dx *= (collide_x ? -1 : 1); + obj.dy *= (collide_y ? -1 : 1); + + return {collide_y, collide_x}; +} + +const updateLogo = (logo, max_width, max_height) => { + const {collide_x, collide_y} = handleCollision(logo, max_width, max_height); + if (collide_y || collide_x) { + logo.dColorWheel = randUpTo(2*Math.PI) + logo.onIntersection(logo, max_width, max_height); + } + + logo.x = Math.max(0, Math.min(logo.x + logo.dx, max_width)); + logo.y = Math.max(0, Math.min(logo.y + logo.dy, max_height)); +}; + +const drawLogo = (logo, element) => { + element.style.left = `${logo.x}px`; + element.style.top = `${logo.y}px`; + element.style.filter = `brightness(0.5) sepia(1) saturate(10000%) hue-rotate(${logo.dColorWheel}rad)`; +} + +const makeSvg = (coords) => { + const makeLine = (a, b, color='white') => ``; + + return ` + + + ${coords.map((_,i) => { + if (i == 0) { + return; + } + return makeLine(coords[i-1], coords[i], 'red'); + }).join('')} + + `; +} + +const setBackgroundToSvg = (svg) => { + document.getElementById("container").style.background = 'url(data:image/svg+xml;base64,'+btoa(svg)+')'; +} + +const makeListOfFutureCollisionsAndDraw = (logo, max_width, max_height) => { + let next_int = {x: logo.x, y: logo.y, dx: logo.dx, dy: logo.dy}; + let intersections = [{...next_int}]; + + for (let i = 0; i < global_state.iterations; i++) { + const next = findNextIntersection(next_int, max_width, max_height); + next_int.x = next.x; + next_int.y = next.y; + handleCollision(next_int, max_width, max_height); + intersections.push(next); + } + + // Transform each intersection to the center of the logo + intersections.map((x) => { + x.x += logo.width/2; + x.y += logo.height/2; + }); + + setBackgroundToSvg(makeSvg(intersections)); +}; + +let drawNewPaths; +window.onload = () => { + const dvdLogo = document.getElementById("dvd-logo"); + let logo = { + x: Math.floor(randUpTo(window.innerWidth-dvdLogo.clientWidth)), + y: Math.floor(randUpTo(window.innerHeight-dvdLogo.clientHeight)), + dx: 2, + dy: 2, + width: dvdLogo.clientWidth, + height: dvdLogo.clientHeight, + onIntersection: makeListOfFutureCollisionsAndDraw, + dColorWheel: 0 + }; + setInterval(() => { + updateLogo(logo, window.innerWidth-logo.width, window.innerHeight-logo.height); + drawLogo(logo, dvdLogo); + }, 22); + + drawNewPaths = () => makeListOfFutureCollisionsAndDraw(logo, window.innerWidth-logo.width, window.innerHeight-logo.height); + drawNewPaths(); + window.onresize = drawNewPaths; + + document.getElementById("iteration-slider").value = global_state.iterations; + document.getElementById("iteration-count").innerHTML = `${global_state.iterations} iterations`; +} + +document.getElementById("iteration-slider").oninput = function() { + global_state.iterations = parseInt(this.value, 10); + document.getElementById("iteration-count").innerHTML = `${global_state.iterations} iterations`; + drawNewPaths(); +} + diff --git a/index.html b/index.html index 160c57a..5107355 100644 --- a/index.html +++ b/index.html @@ -21,9 +21,9 @@
🤓 I'm Simponic.
- 📖 This pages hosts strictly static content. + 📖 This page hosts strictly static content.
- 🔔 Visit simponic.xyz to view my "real" website. + 🔔 My "real website" is at simponic.xyz.

@@ -33,7 +33,7 @@

DVD Logo Bouncing Animation

-

Brings back the nostalgia of old-school DVD players, as discussed in The Office.

+

Brings back the nostalgia of old-school DVD players with an intersection predictor. The twist: no Canvas API! Only svg's and absolute positioned images!