diff --git a/_includes/layouts/home.njk b/_includes/layouts/home.njk index e83483d..4a3a107 100644 --- a/_includes/layouts/home.njk +++ b/_includes/layouts/home.njk @@ -10,9 +10,9 @@ title: runtimefee.lol - Homepage
This blog is dedicated to game development. You can find hints, tutorials diff --git a/eleventy.config.js b/eleventy.config.js index 4fde384..9b91bbf 100644 --- a/eleventy.config.js +++ b/eleventy.config.js @@ -1,9 +1,4 @@ -import { createNoise2D } from "simplex-noise"; -import { createCanvas, ImageData } from "canvas"; -import alea from "alea"; - -const SEED = "runtimefee.lol"; -var log_once = false; +import { generateThumbnail } from "./scripts/thumbnails.js"; /** @param {import("@11ty/eleventy").UserConfig} eleventyConfig */ export default function (eleventyConfig) { @@ -28,135 +23,27 @@ export default function (eleventyConfig) { }).format(dateObj); }); + eleventyConfig.addFilter("listTags", (tagsList) => { + let listHTML = ""; + for (let i in tagsList) { + listHTML += `${tagsList[i]}`; + } + return listHTML; + }); + eleventyConfig.addFilter("capitalize", function (value) { return value.toUpperCase(); }); // generate a nice preview image for articles that don't have one eleventyConfig.addShortcode("generateThumbnail", function (hash) { - return getThumbnailImage(hash); + return generateThumbnail(hash); }); // Collections eleventyConfig.addCollection("posts", (collectionApi) => { - return collectionApi.getFilteredByTag("posts"); + return collectionApi.getFilteredByTag("Posts"); }); eleventyConfig.addPassthroughCopy({ "public/": "/" }); } - -function getRandomFloat(min, max) { - return parseFloat((Math.random() * (max - min) + min).toFixed(2)); -} - -function getRandomPaletteValues() { - // Generate random arrays for a, b, c, and d - const a = [ - getRandomFloat(0.5, 1.0), - getRandomFloat(0.5, 1.0), - getRandomFloat(0.5, 1.0), - ]; - const b = [ - getRandomFloat(0.2, 0.8), - getRandomFloat(0.2, 0.8), - getRandomFloat(0.2, 0.8), - ]; - const c = [ - getRandomFloat(0.0, 2.0), - getRandomFloat(0.0, 2.0), - getRandomFloat(0.0, 2.0), - ]; - const d = [ - getRandomFloat(0.0, 1.0), - getRandomFloat(0.0, 1.0), - getRandomFloat(0.0, 1.0), - ]; - - if (log_once == false) { - log_once = true; - //console.log({ a, b, c, d }); - } - - return { a, b, c, d }; -} - -function getPalette(t, a, b, c, d) { - const result = []; - - for (let i = 0; i < 3; i++) { - // Apply the formula element-wise to each component of the vectors - result[i] = - 255 * (a[i] + b[i] * Math.cos(2.0 * Math.PI * (c[i] * t + d[i]))); - } - - if (log_once == false) { - log_once = true; - //console.log(result); - } - - return result; -} - -function getThumbnailImage(hash) { - var width = 800; - var height = 300; - var rng = alea(SEED + hash); - - var noise2D = createNoise2D(rng); - - const canvas = createCanvas(width, height); - const ctx = canvas.getContext("2d"); - const imageData = ctx.createImageData(width, height); - const paletteSettings = getRandomPaletteValues(); - - var dist = 0; - for (let x = 0; x < width; x++) { - for (let y = 0; y < height; y++) { - const value = noise2D(x / 50, y / 50); - var threshold = Math.floor((value + 1) * 128); - threshold = value > 0.2 ? 128 : 0; - - const index = (x + y * width) * 4; - - const dx = x - width / 2; - const dy = y - height / 2; - const distance = Math.sqrt(dx * dx + dy * dy); // Euclidean distance - - // Normalize the distance to be between 0 and 1 (for a radial gradient effect) - const maxDistance = Math.sqrt( - ((width / 2) * width) / 2 + ((height / 2) * height) / 2 - ); - - const t = (distance / maxDistance) * 3.0 * Math.sin(dist); - dist += 0.00001; - - const a = [0.5, 0.5, 0.5]; - const b = [0.5, 0.5, 0.5]; - const c = [1.0, 1.0, 1.0]; - const d = [0.0, 0.1, 0.2]; - - const palette = getPalette( - t, - paletteSettings.a, - paletteSettings.b, - paletteSettings.c, - paletteSettings.d - ); - - if (threshold == 0) { - imageData.data[index] = palette[0] * 0.05; // Red - imageData.data[index + 1] = palette[1] * 0.09; // Green - imageData.data[index + 2] = palette[2] * 0.3; // Blue - imageData.data[index + 3] = 255; // Alpha - } else { - imageData.data[index] = palette[0]; // Red - imageData.data[index + 1] = palette[1]; // Green - imageData.data[index + 2] = palette[2]; // Blue - imageData.data[index + 3] = 255; // Alpha - } - } - } - - ctx.putImageData(imageData, 0, 0); - return canvas.toDataURL("image/jpeg"); -} diff --git a/public/css/bundle.css b/public/css/bundle.css index 095f804..3e5799c 100644 --- a/public/css/bundle.css +++ b/public/css/bundle.css @@ -1,30 +1,30 @@ /* See https://codeberg.org/lucajunge/css-reset for details */ @import url(./reset.css); -/* Custom fonts */ @import url(./fonts.css); -@import url(./margins.css); - @import url(./icons.css); +@import url(./util.css); + +@import url(./post.css); + :root { --font-family: "Source Sans 3", -apple-system, system-ui, sans-serif; --font-family-monospace: Consolas, Menlo, Monaco, Andale Mono WT, Andale Mono, Lucida Console, Lucida Sans Typewriter, DejaVu Sans Mono, Bitstream Vera Sans Mono, Liberation Mono, Nimbus Mono L, Courier New, Courier, monospace; -} - -:root { - --background-color: #121212; - --background-color-dark: #080808; + --background-color: #242424; + --background-color-strong: #080808; + --text-color: #f3f3f3; --accent-color: #daa520; } html, body { font-family: var(--font-family); + color: var(--text-color); background-color: var(--background-color); } @@ -40,6 +40,18 @@ h6 { color: var(--accent-color); } +h1 { + font-size: 1.6em; +} + +h2, +h3, +h4, +h5, +h6 { + margin-block: 0.5em 0.7em; +} + .header { padding: 1rem; display: flex; @@ -61,9 +73,18 @@ h6 { } .footer div { - background-color: #080808; + background-color: var(--background-color-strong); border-radius: 4px; - padding: 2px 4px 1px 2px; + padding: 2px 4px 1px 4px; +} + +.tag { + padding: 3px 6px; + background-color: var(--background-color-strong); + margin-left: 4px; + display: inline-block; + border-radius: 4px; + font-size: 0.8em; } .recent-posts-container { @@ -120,5 +141,24 @@ h6 { font-weight: bold; border-radius: 4px; padding: 2px 7px 5px 7px; - background-color: var(--background-color-dark); + background-color: var(--background-color-strong); +} + +/* Light theme */ +@media (prefers-color-scheme: light) { + :root { + --text-color: #242424; + --background-color: #f3f3f3; + --background-color-strong: #ababab; + } +} + +.home main { + display: flex; + flex-direction: column; + max-width: 786px; +} + +.home main * { + max-width: 786px; } diff --git a/public/css/icons.css b/public/css/icons.css index 438a6ba..d435ef5 100644 --- a/public/css/icons.css +++ b/public/css/icons.css @@ -5,6 +5,15 @@ font-size: 24px; } +.ti-font-size { + font-size: 1em; +} + +.ti-xsmall, +.ti-xtra-small { + font-size: 14px; +} + .ti.small { font-size: 16px; } diff --git a/public/css/margins.css b/public/css/margins.css deleted file mode 100644 index f27ae96..0000000 --- a/public/css/margins.css +++ /dev/null @@ -1,3 +0,0 @@ -.margin-small { - margin: 12px; -} diff --git a/public/css/post.css b/public/css/post.css new file mode 100644 index 0000000..7edb1c1 --- /dev/null +++ b/public/css/post.css @@ -0,0 +1,41 @@ +.post main { + display: flex; + flex-direction: column; + max-width: 786px; +} + +.post main * { + max-width: 786px; +} + +.post nav a { + background-color: var(--background-color-strong); + border-radius: 8px; + padding: 8px 12px; + display: inline-block; + text-decoration: none; + color: inherit; + font-weight: bold; + margin-block: 0px 8px; +} + +.post #post-info { + display: flex; + max-width: 786px; + flex-direction: column; + justify-content: center; + padding-block-end: 0px; +} + +.post h2 { + font-size: 1.6em; + font-weight: bold; +} + +.post p ~ h2 { + margin-block: 2em 0.5em; +} + +.post p ~ h3 { + margin-block: 1.5em 0.5em; +} diff --git a/public/css/util.css b/public/css/util.css new file mode 100644 index 0000000..8236ed1 --- /dev/null +++ b/public/css/util.css @@ -0,0 +1,23 @@ +.center { + justify-self: center; +} + +.margin-small { + margin: 12px; +} + +.margin-right-small { + margin-right: 4px; +} + +.margin-left-small { + margin-left: 4px; +} + +.padding-small { + padding: 12px; +} + +.margin-auto { + margin: 0 auto; +} diff --git a/scripts/thumbnails.js b/scripts/thumbnails.js new file mode 100644 index 0000000..700c452 --- /dev/null +++ b/scripts/thumbnails.js @@ -0,0 +1,111 @@ +import { createNoise2D } from "simplex-noise"; +import { createCanvas, ImageData } from "canvas"; +import alea from "alea"; + +// The hash is the name of the article e.g. "Recommended Godot Editor Settings" +export function generateThumbnail(hash) { + var rng = alea(hash); + console.log(`Generating thumbnail for \"${hash}\" with ${rng()}`); + var width = 800; + var height = 300; + + var noise2D = createNoise2D(rng); + + const canvas = createCanvas(width, height); + const ctx = canvas.getContext("2d"); + const imageData = ctx.createImageData(width, height); + const paletteSettings = getRandomPaletteValues(rng()); + + var dist = 0; + for (let x = 0; x < width; x++) { + for (let y = 0; y < height; y++) { + const value = noise2D(x / 50, y / 50); + var threshold = Math.floor((value + 1) * 128); + threshold = value > 0.2 ? 128 : 0; + + const index = (x + y * width) * 4; + + const dx = x - width / 2; + const dy = y - height / 2; + const distance = Math.sqrt(dx * dx + dy * dy); // Euclidean distance + + // Normalize the distance to be between 0 and 1 (for a radial gradient effect) + const maxDistance = Math.sqrt( + ((width / 2) * width) / 2 + ((height / 2) * height) / 2 + ); + + const t = (distance / maxDistance) * 3.0 * Math.sin(dist); + dist += 0.00001; + + const a = [0.5, 0.5, 0.5]; + const b = [0.5, 0.5, 0.5]; + const c = [1.0, 1.0, 1.0]; + const d = [0.0, 0.1, 0.2]; + + const palette = getPalette( + t, + paletteSettings.a, + paletteSettings.b, + paletteSettings.c, + paletteSettings.d + ); + + if (threshold == 0) { + imageData.data[index] = palette[0] * 0.05; // Red + imageData.data[index + 1] = palette[1] * 0.09; // Green + imageData.data[index + 2] = palette[2] * 0.3; // Blue + imageData.data[index + 3] = 255; // Alpha + } else { + imageData.data[index] = palette[0]; // Red + imageData.data[index + 1] = palette[1]; // Green + imageData.data[index + 2] = palette[2]; // Blue + imageData.data[index + 3] = 255; // Alpha + } + } + } + + ctx.putImageData(imageData, 0, 0); + return canvas.toDataURL("image/jpeg"); +} + +function getRandomFloat(seed, min, max) { + return parseFloat((seed * (max - min) + min).toFixed(2)); +} + +function getRandomPaletteValues(seed) { + // Generate random arrays for a, b, c, and d + const a = [ + getRandomFloat(seed, 0.5, 1.0), + getRandomFloat(seed, 0.5, 1.0), + getRandomFloat(seed, 0.5, 1.0), + ]; + const b = [ + getRandomFloat(seed, 0.2, 0.8), + getRandomFloat(seed, 0.2, 0.8), + getRandomFloat(seed, 0.2, 0.8), + ]; + const c = [ + getRandomFloat(seed, 0.0, 2.0), + getRandomFloat(seed, 0.0, 2.0), + getRandomFloat(seed, 0.0, 2.0), + ]; + const d = [ + getRandomFloat(seed, 0.0, 1.0), + getRandomFloat(seed, 0.0, 1.0), + getRandomFloat(seed, 0.0, 1.0), + ]; + + return { a, b, c, d }; +} + +function getPalette(t, a, b, c, d) { + const result = []; + + for (let i = 0; i < 3; i++) { + // Apply the formula element-wise to each component of the vectors + result[i] = + 255 * (a[i] + b[i] * Math.cos(2.0 * Math.PI * (c[i] * t + d[i]))); + } + + return result; +}