diff --git a/Makefile b/Makefile index 112913d..a0a28c0 100644 --- a/Makefile +++ b/Makefile @@ -5,7 +5,8 @@ MEDIA = \ $(wildcard media/*.png) $(wildcard media/*.gif) $(wildcard media/*.webp) \ $(wildcard media/flags/*) $(wildcard media/buttons/*) \ $(wildcard media/icons/*) $(wildcard media/bg/*) 8831.png 8831-quox.png \ - $(wildcard rainbow-quox/front/*) $(wildcard rainbow-quox/back/*) + $(wildcard rainbow-quox/front/*) $(wildcard rainbow-quox/back/*) \ + rainbow-quox/palette.svg SCRIPTS = $(patsubst %.ts,%.js,$(wildcard script/*.ts rainbow-quox/*.ts)) MISC = $(shell find .well-known -type f) ALL = $(CSS) $(PAGES) $(MEDIA) $(SCRIPTS) $(MISC) @@ -59,6 +60,9 @@ $(BUILDDIR)/%.js: %.ts --lib dom,es2023 --target es2015 \ --outDir $(dir $@) $^ +$(BUILDDIR)/rainbow-quox/palette.svg: rainbow-quox/palette.svg.raku + raku $^ $@ + clean: $(RM) -r $(TMPDIR) $(BUILDDIR) diff --git a/rainbow-quox/back/pos.json b/rainbow-quox/back/pos.json index f93e41b..21db123 100644 --- a/rainbow-quox/back/pos.json +++ b/rainbow-quox/back/pos.json @@ -15,7 +15,7 @@ "static": [219, 41], "stripes": [219, 221], "vitiligo1": [4, 22], - "vitiligo2": [46, 48], - "vitiligo3": [101, 134], + "vitiligo2": [102, 134], + "vitiligo3": [46, 129], "vitiligo4": [221, 56] } diff --git a/rainbow-quox/back/vitiligo2.webp b/rainbow-quox/back/vitiligo2.webp index d17c37b..0899891 100644 --- a/rainbow-quox/back/vitiligo2.webp +++ b/rainbow-quox/back/vitiligo2.webp @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1db28390d6407495f8c0a0125db81f8d5e15d363463c04dc77748808fbd97eb7 -size 2632 +oid sha256:195c7aaf80305f17cbee871b59bf3f797628aafb09701567b22549ae68626f27 +size 2578 diff --git a/rainbow-quox/back/vitiligo3.webp b/rainbow-quox/back/vitiligo3.webp index 0899891..a891ca5 100644 --- a/rainbow-quox/back/vitiligo3.webp +++ b/rainbow-quox/back/vitiligo3.webp @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:195c7aaf80305f17cbee871b59bf3f797628aafb09701567b22549ae68626f27 -size 2578 +oid sha256:d299e372195d499b57c8f804d51ccba438f73c6aaa2342bec863e1ff0f0e1ea8 +size 1556 diff --git a/rainbow-quox/canvas.ts b/rainbow-quox/canvas.ts index 86d90d3..5ffe952 100644 --- a/rainbow-quox/canvas.ts +++ b/rainbow-quox/canvas.ts @@ -38,7 +38,6 @@ type Rgbs = Record; let rgbBuf = new OffscreenCanvas(1, 1).getContext('2d')!; function toRgb(col: Color.Oklch): Rgb { - // :) rgbBuf.fillStyle = col.css(); rgbBuf.fillRect(0, 0, 1, 1); const rgb = rgbBuf.getImageData(0, 0, 1, 1).data; @@ -49,6 +48,14 @@ function toRgbs(col: Color.Colors): Rgbs { return Color.makeColorInfo(l => toRgb(col[l])); } +function toHex([r, g, b]: Rgb): string { + function chan(n: number) { + let a = Math.floor(n).toString(16); + return a.length == 1 ? `0${a}` : a; + } + return `#${chan(r)}${chan(g)}${chan(b)}`; +} + function setImageDataRgb({ data }: ImageData, [r, g, b]: Rgb, a: number = -1) { for (let i = 0; i < data.length; i += 4) { @@ -84,7 +91,6 @@ async function loadSide(buf: OffscreenCanvasRenderingContext2D, await Promise.all( allLayers.map(l => load(buf, side, l).then(res => [l, res]))) ) as Record; - console.log(images); for (const l of allLayers) { layers[l] = { data: images[l], pos: pos[l] }; } @@ -131,7 +137,7 @@ function message(msg: string, ctx: CanvasRenderingContext2D) { ctx.clearRect(0, 0, WIDTH, HEIGHT); ctx.font = 'bold 100px Muller, sans-serif'; ctx.textAlign = 'center'; - ctx.fillText(msg, WIDTH/2, HEIGHT/2); + ctx.fillText(msg, WIDTH/2, HEIGHT/2, WIDTH-10); ctx.restore(); } @@ -167,13 +173,14 @@ class Side { layers() { return this.cur == 'front' ? this.fronts : this.backs; } async recolorOn(ctx: CanvasRenderingContext2D, - buf: OffscreenCanvasRenderingContext2D) { + buf: OffscreenCanvasRenderingContext2D): + Promise<[Color.Colors, Rgbs]> { const cols = Color.colors(); const rgbs = toRgbs(cols); setColors(rgbs, this.fronts); setColors(rgbs, this.backs); await redraw(ctx, buf, this.layers()); - return cols.outer.h; + return [cols, rgbs]; } async ensureComposed(buf: OffscreenCanvasRenderingContext2D) { @@ -187,15 +194,53 @@ function setBg(hue: number) { } +function getPalette(): XMLDocument | null { + const palette = document.getElementById('palette') as HTMLObjectElement; + return palette.contentDocument; +} + +function setPalette(oklch: Color.Colors, rgb: Rgbs) { + const palette = getPalette(); + + if (!palette) { + setTimeout(() => setPalette(oklch, rgb), 500); + return; + } + + palette.documentElement.style.setProperty('--hue', `${oklch.outer.h}`); + console.log(palette); + + for (const l of Color.allLayers) { + let col = toHex(rgb[l]); + + const swatch = palette.getElementById(`s-${l}`); + if (swatch) swatch.style.fill = col; + + const text = palette.getElementById(`c-${l}`); + if (text) text.innerHTML = col; + + const bg = palette.getElementById(`p-${l}`); + if (bg) bg.style.fill = col; + + const item = palette.getElementById(`i-${l}`); + if (item) { + const textCol = oklch[l].l < 0.6 ? 'white' : 'black'; + item.style.setProperty('--text', textCol); + } + + } +} + + async function animateReroll(ctx1: CanvasRenderingContext2D, ctx2: CanvasRenderingContext2D, buf: OffscreenCanvasRenderingContext2D, - side: Side, - done: () => void) { + side: Side, done: () => void) { const duration = 200; - const hue = await side.recolorOn(ctx2, buf); + const [oklch, rgb] = await side.recolorOn(ctx2, buf); ctx2.canvas.style.animation = `${duration}ms ease fade-in`; - setBg(hue); + setBg(oklch.outer.h); + setPalette(oklch, rgb); setTimeout(finish, duration); async function finish() { @@ -255,8 +300,9 @@ document.addEventListener('DOMContentLoaded', async function() { throw err; }); - let hue = await side.recolorOn(picCtx, buf); - setBg(hue); + let [oklch, rgb] = await side.recolorOn(picCtx, buf); + setBg(oklch.outer.h); + setPalette(oklch, rgb); await redraw(picCtx, buf, side.layers()); const reroll = document.getElementById('reroll')!; @@ -264,6 +310,19 @@ document.addEventListener('DOMContentLoaded', async function() { addListeners(); + type Done = (finish: () => void) => void; + + function handleReroll(e: Event) { + wrap(reroll, + done => animateReroll(picCtx, pic2Ctx, buf, side, done), + 'reroll', e); + } + + function handleSwap(e: Event) { + wrap(swap, done => animateSwap(picCtx, pic2Ctx, side, buf, done), + 'swap', e); + } + function addListeners() { reroll.addEventListener('click', handleReroll); swap.addEventListener('click', handleSwap); @@ -274,19 +333,7 @@ document.addEventListener('DOMContentLoaded', async function() { swap.removeEventListener('click', handleSwap); } - function handleReroll(e: Event) { - wrap(reroll, done => animateReroll(picCtx, pic2Ctx, buf, side, done), - 'reroll', e); - } - - function handleSwap(e: Event) { - wrap(swap, done => animateSwap(picCtx, pic2Ctx, side, buf, done), - 'swap', e); - } - - type Handler = (finish: () => void) => void; - - function wrap(elem: Element, f: Handler, name: string, e: Event) { + function wrap(elem: Element, f: Done, name: string, e: Event) { if (elem != e.target) return; e.stopPropagation(); if (isRunning()) return; diff --git a/rainbow-quox/color.ts b/rainbow-quox/color.ts index 9de056b..b712e0d 100644 --- a/rainbow-quox/color.ts +++ b/rainbow-quox/color.ts @@ -108,7 +108,11 @@ export class Oklch { } css(alpha: number = 1): string { - return `oklch(${this.l} ${this.c} ${this.h} / ${alpha})`; + const l = (this.l * 100).toFixed(0); + const c = (this.c * 250).toFixed(0); + const h = this.h.toFixed(0); + if (alpha != 1) { return `oklch(${l}% ${c}% ${h} / ${alpha})`; } + else { return `oklch(${l}% ${c}% ${h})`; } } with(lch: Partial>): Oklch { diff --git a/rainbow-quox/index.html b/rainbow-quox/index.html index 67e269f..45885bf 100644 --- a/rainbow-quox/index.html +++ b/rainbow-quox/index.html @@ -8,6 +8,11 @@ +
+ + +
+
if the canvas isn't working, @@ -17,11 +22,13 @@
-
- - +
+ + there should be a palette here but it failed to load for some reason +
-