diff --git a/README.md b/README.md index ef1123d..49d39fa 100644 --- a/README.md +++ b/README.md @@ -4,10 +4,7 @@ ## ▶️Demo -### Desktop -![](res/demo_horizontal.png) -### Mobile -![](res/demo_vertical.png) +![](res/demo.png) ## ℹUsage You can [go to the website](https://Rudxain.github.io/RGB-digital-rain), or [⬇download](https://github.com/Rudxain/RGB-digital-rain/archive/refs/heads/main.zip) this branch as a zip. If you downloaded the zip, then extract it, then open `src/index.html` in your 🌐browser of choice. diff --git a/res/demo.png b/res/demo.png new file mode 100644 index 0000000..5ab210d Binary files /dev/null and b/res/demo.png differ diff --git a/src/index.html b/src/index.html index f7a4f2b..fc19625 100644 --- a/src/index.html +++ b/src/index.html @@ -24,10 +24,6 @@ - - - - @@ -35,8 +31,8 @@ - Update browser or enable JS + Enable JS Canvas - \ No newline at end of file + diff --git a/src/m.webmanifest b/src/m.webmanifest index e20b04d..68d8e27 100644 --- a/src/m.webmanifest +++ b/src/m.webmanifest @@ -18,20 +18,11 @@ "background_color": "#000", "theme_color": "#0f0", "display": "fullscreen", -"screenshots": [ - { - "src": "/res/demo_horizontal.png", +"screenshots": [{ + "src": "/res/demo.png", "type": "image/png", "sizes": "1920x1080", "platform": "wide", - "label": "Desktop demo of RGB digital rain" - }, - { - "src": "/res/demo_vertical.png", - "type": "image/png", - "sizes": "720x1280", - "platform": "narrow", - "label": "Mobile demo of RGB digital rain" - } -] -} \ No newline at end of file + "label": "Demo of RGB-DR" + }] +} diff --git a/src/main.js b/src/main.js index 7b145bc..d0b069a 100644 --- a/src/main.js +++ b/src/main.js @@ -2,6 +2,35 @@ 'use strict' // global/public, for debugging/testing purposes const RGBDR_anim = (() => { + const rng = Math.random + + /** Returns a pseudorandom 32-bit unsigned integer between `min` and `max`. */ + const randomUint32 = (min = 0, max = 2 ** 32) => (rng() * (max - min) + min) >>> 0 + + /** + Get a pseudo-random UTF-16 code-unit from a `string`. + @param {string} s + */ + const rand_CU_pick = s => s[rng() * s.length >>> 0] + // To simplify optimization by the engine, + // I call `rng`, rather than `randomUint32`. + // `trunc`/`floor` are misleading since index is always a u32, + // that's I use `>>>0`. + + /** + Convert Hertz to corresponding mili-seconds + @param {number} f frequency + @return interval + */ + const Hz_to_ms = f => 1000 / f + + /** + coerce `x` to u8, then hex-encode it with padding + @param {number} x + */ + const hex_byte = x => (x & 0xff).toString(16).padStart(2, '0') + + const DOC = document, RAF = requestAnimationFrame const canv = /**@type {HTMLCanvasElement}*/(DOC.getElementById('c')) @@ -30,15 +59,10 @@ const RGBDR_anim = (() => { playing = b if (!prev && b) { - /** - Convert Hertz to corresponding mili-seconds - @param {number} f frequency - @return interval - */ - const Hz_to_ms = f => 1000 / f + // order matters + RAF(full_dimmer) // the interval ensures `drawChars` is independent of FPS it_ID = setInterval(draw_chars, Hz_to_ms(a.settings.char_speed_Hz)) - RAF(full_dimmer) } if (prev && !b) clearInterval(it_ID) @@ -49,7 +73,7 @@ const RGBDR_anim = (() => { it's defined here, instead of outside the IIFE's closure. */ settings: { - /** `Array` of CSS hex colors */ + /** hex */ colors: ['f00', 'ff0', '0f0', '0ff', '00f', 'f0f'],//🌈RYGCBM /** character-set/alphabet */ charset: @@ -65,7 +89,7 @@ const RGBDR_anim = (() => { /** dimming coefficient */ dim_factor: 1 * (is_dark ? 1 : -1), /** miliseconds to debounce until `resize` is called */ - resize_delay_ms: 1500 + resize_delay_ms: 750 } } return a @@ -85,7 +109,7 @@ const RGBDR_anim = (() => { /** Set `canv` dimensions to fill the full viewport. - Resize `height_ls` accordingly, padding with 0. + Resize `height_ls` accordingly, padding with `0`. Resize `color_i_ls` accordingly, padding with `index mod colors.length`. */ @@ -95,8 +119,9 @@ const RGBDR_anim = (() => { // calculate how many columns in the grid are necessary to fill the whole canvas const columns = Math.ceil(canv.width / anim.settings.grid_px) + // prevent memory/CPU leak caused by race condition const prev = anim.playing - anim.playing = false // prevent memory/CPU leak caused by race condition + anim.playing = false //const sleep = (/**@type {number|undefined}*/ ms) => new Promise(_ => setTimeout(_, ms)) /* @@ -114,24 +139,15 @@ const RGBDR_anim = (() => { color_i_ls.push(color_i_ls.length % anim.settings.colors.length) color_i_ls.length = columns - anim.playing = prev // revert to previous play-state + anim.playing = prev } const draw_chars = () => { const - rng = Math.random, { colors, grid_px, charset } = anim.settings ctx.font = `bold ${grid_px}px monospace` - /** Returns a pseudorandom 32-bit unsigned integer between `min` and `max`. */ - const randomUint32 = (min = 0, max = 2 ** 32) => (rng() * (max - min) + min) >>> 0 - - /** - Get a pseudo-random UTF-16 code-unit from a `string`. - @param {string} s - */ - const rand_CU_pick = s => s[rng() * s.length >>> 0] // according to MDN docs, `forEach` seems to be thread-safe here (I guess) height_ls.forEach((y, i) => { @@ -166,30 +182,8 @@ const RGBDR_anim = (() => { /** u8 that specifies how much to dim the canvas */ const dim = Math.round(Math.min((now - t) * Math.abs(df), 0xff)) - { - /** - Check if `x` is `-0`, without `Object.is`, for purity. - @param {*} x - */ - const is_neg_zero = x => x === 0 && 1 / x == -Infinity - - /** - Check if `x` is an unsigned 8bit int. Returns `false` for `BigInt`s. - @param {*} x - */ - const is_u8 = x => typeof x == 'number' && x == (x & 0xff) && !is_neg_zero(x) - console.assert(is_u8(dim)) - } - // performance... if (dim) { - const HEX_TABLE = '0123456789abcdef' - - /** - coerce `x` to u8, then hex-encode it with padding - @param {number} x - */ - const hex_byte = x => HEX_TABLE[(x & 0xff) >> 4] + HEX_TABLE[x & 0xf] // does hex really has better performance here? // should I change it to `rgb()` or `hsl()`?