From 551afe04a5b8b2ba373dba934755591009facb23 Mon Sep 17 00:00:00 2001 From: Neale Pickett Date: Fri, 8 Sep 2023 18:05:51 -0600 Subject: [PATCH] Puzzle start using new lib +bg animation --- theme/background.mjs | 126 +++++++++++++++++++++++++++++++++++++++++++ theme/basic.css | 107 ++++++++++++++++++------------------ theme/moth.mjs | 14 +++++ theme/puzzle.html | 44 +++++++-------- theme/puzzle.mjs | 60 +++++++++++++++++++++ 5 files changed, 275 insertions(+), 76 deletions(-) create mode 100644 theme/background.mjs diff --git a/theme/background.mjs b/theme/background.mjs new file mode 100644 index 0000000..f2d1509 --- /dev/null +++ b/theme/background.mjs @@ -0,0 +1,126 @@ +function randint(max) { + return Math.floor(Math.random() * max) +} + +const MILLISECOND = 1 +const SECOND = MILLISECOND * 1000 + +class Line { + /** + * @param {CanvasRenderingContext2D} ctx canvas context + * @param {Number} hue Hue, in % of one circle [0,tau) + * @param {Number} a First point of line + * @param {Number} b Second point of line + */ + constructor(ctx, hue, a, b) { + this.ctx = ctx + this.hue = hue + this.a = a + this.b = b + } + + bounce(point, v) { + let ret = [ + point[0] + v[0], + point[1] + v[1], + ] + if ((ret[0] > this.ctx.canvas.width) || (ret[0] < 0)) { + v[0] *= -1 + ret[0] += v[0] * 2 + } + if ((ret[1] > this.ctx.canvas.height) || (ret[1] < 0)) { + v[1] *= -1 + ret[1] += v[1] * 2 + } + return ret + } + + Add(hue, a, b) { + return new Line( + this.ctx, + (this.hue + hue) % 1.0, + this.bounce(this.a, a), + this.bounce(this.b, b), + ) + } + + Draw() { + this.ctx.save() + this.ctx.strokeStyle = `hwb(${this.hue}turn 0% 50%)` + this.ctx.beginPath() + this.ctx.moveTo(this.a[0], this.a[1]) + this.ctx.lineTo(this.b[0], this.b[1]) + this.ctx.stroke() + this.ctx.restore() + } +} + +class LengoBackground { + constructor() { + this.canvas = document.createElement("canvas") + document.body.insertBefore(this.canvas, document.body.firstChild) + this.canvas.style.position = "fixed" + this.canvas.style.zIndex = -1000 + this.canvas.style.opacity = 0.3 + this.canvas.style.top = 0 + this.canvas.style.left = 0 + this.canvas.style.width = "99vw" + this.canvas.style.height = "99vh" + this.canvas.width = 2000 + this.canvas.height = 2000 + this.ctx = this.canvas.getContext("2d") + this.ctx.lineWidth = 1 + + this.lines = [] + for (let i = 0; i < 18; i++) { + this.lines.push( + new Line(this.ctx, 0, [0, 0], [0, 0]) + ) + } + this.velocities = { + hue: 0.001, + a: [20 + randint(10), 20 + randint(10)], + b: [5 + randint(10), 5 + randint(10)], + } + this.nextFrame = performance.now()-1 + this.frameInterval = 100 * MILLISECOND + + //addEventListener("resize", e => this.resizeEvent()) + //this.resizeEvent() + //this.animate(this.nextFrame) + setInterval(() => this.animate(this.nextFrame+1), SECOND/6) + } + + + /** + * Animate one frame + * + * @param {DOMHighResTimeStamp} timestamp + */ + animate(timestamp) { + if (timestamp >= this.nextFrame) { + this.lines.shift() + let lastLine = this.lines.pop() + let nextLine = lastLine.Add(this.velocities.hue, this.velocities.a, this.velocities.b) + this.lines.push(lastLine) + this.lines.push(nextLine) + + this.ctx.clearRect(0, 0, this.ctx.canvas.width, this.ctx.canvas.height) + for (let line of this.lines) { + line.Draw() + } + this.nextFrame += this.frameInterval + } + //requestAnimationFrame((ts) => this.animate(ts)) + } +} + +function init() { + new LengoBackground() +} + +if (document.readyState === "loading") { + document.addEventListener("DOMContentLoaded", init) +} else { + init() +} diff --git a/theme/basic.css b/theme/basic.css index 14a5a1e..42d1b5c 100644 --- a/theme/basic.css +++ b/theme/basic.css @@ -1,29 +1,58 @@ -/* http://paletton.com/#uid=63T0u0k7O9o3ouT6LjHih7ltq4c */ +/* + * Colors + * + * This uses the alpha channel to apply hue tinting to elements, to get a + * similar effect in light or dark mode. + * + * http://paletton.com/#uid=33x0u0klrl-4ON9dhtKtAdqMQ4T + */ body { - font-family: sans-serif; - max-width: 40em; - background: #282a33; - color: #f6efdc; + background: #010e19; + color: #edd488; } -body.wide { - max-width: 100%; +main { + background: #000d; } -a:any-link { - color: #8b969a; +h1, h2, h3, h4, h5, h6 { + color: #cb2408cc; } h1 { - background: #5e576b; - color: #9e98a8; + background: #cb240844; } -.Fail, .Error, #messages { - background: #3a3119; - color: #ffcc98; +a:any-link { + color: #b9cbd8; } -.Fail:before { - content: "Fail: "; +.notification { + background: #ac8f3944; } -.Error:before { - content: "Error: "; +.error { + background: red; + color: white; +} +@media (prefers-color-scheme: light) { + body { + background: #b9cbd8; + color: black; + } + main { + background: #fffd; + } + a:any-link { + color: #092b45; + } +} + +body { + font-family: sans-serif; +} +main { + max-width: 40em; + margin: auto; + padding: 1px 3px; + border-radius: 5px; +} +h1 { + padding: 3px; } p { margin: 1em 0em; @@ -36,9 +65,11 @@ input, select { margin: 0.2em; max-width: 30em; } -nav { - border: solid black 2px; +.notification, .error { + padding: 0 1em; + border-radius: 8px; } + nav ul, .category ul { padding: 1em; } @@ -58,7 +89,6 @@ input:invalid { } #messages { min-height: 3em; - border: solid black 2px; } #rankings { width: 100%; @@ -91,40 +121,7 @@ input:invalid { #devel { - background-color: #eee; - color: black; - overflow: scroll; -} -#devel .string { - color: #9c27b0; -} -#devel .body { - background-color: #ffc107; -} -.kvpair { - border: solid black 2px; -} - -.spinner { - display: inline-block; - width: 64px; - height: 64px; - display: block; - width: 46px; - height: 46px; - margin: 1px; - border-radius: 50%; - border: 5px solid #fff; - border-color: #fff transparent #fff transparent; - animation: rotate 1.2s linear infinite; -} -@keyframes rotate { - 0% { - transform: rotate(0deg); - } - 100% { - transform: rotate(360deg); - } + overflow: auto; } li[draggable]::before { diff --git a/theme/moth.mjs b/theme/moth.mjs index 3a31b18..f76f5e0 100644 --- a/theme/moth.mjs +++ b/theme/moth.mjs @@ -326,6 +326,20 @@ class Server { GetContent(category, points, filename) { return this.fetch(`/content/${category}/${points}/${filename}`) } + + /** + * Return a Puzzle object. + * + * New Puzzle objects only know their category and point value. + * See docstrings on the Puzzle object for more information. + * + * @param {String} category + * @param {Number} points + * @returns {Puzzle} + */ + GetPuzzle(category, points) { + return new Puzzle(this, category, points) + } } export { diff --git a/theme/puzzle.html b/theme/puzzle.html index 37206f6..46c1ad9 100644 --- a/theme/puzzle.html +++ b/theme/puzzle.html @@ -1,32 +1,34 @@ - + Puzzle - - + + -

Puzzle

-
-
- -

Puzzle by

-
-
-
- - - - Team ID:
- Answer:
- -
-
+
+

[loading]

+
+
+

+ Starting script... +

+
+
    +

    Puzzle by [loading]

    +
    +
    + + + Team ID:
    + Answer:
    + +
    +
    +