From 30c2cdfd82ed27600d2a75bc625431168488d07a Mon Sep 17 00:00:00 2001 From: Carlos Bonadeo Date: Sun, 12 May 2024 00:03:54 +0100 Subject: [PATCH 01/12] Create getDayProgress & getDayParameters --- src/utils/utils.test.ts | 54 +++++++++++++++++++++++++++++------------ src/utils/utils.ts | 32 ++++++++++++++++++++++++ 2 files changed, 71 insertions(+), 15 deletions(-) diff --git a/src/utils/utils.test.ts b/src/utils/utils.test.ts index 4388dd5c5..7acc38fa8 100644 --- a/src/utils/utils.test.ts +++ b/src/utils/utils.test.ts @@ -1,23 +1,47 @@ -import { describe, expect, test } from "vitest"; -import { getFaviconFileName } from "./utils"; - -const TIMES = { - "02:03": "02_00", - "14:03": "02_00", - "02:00": "02_00", - "02:30": "02_30", - "00:00": "00_00", - "12:00": "00_00", - "02:49": "02_30", - "14:49": "02_30", - "10:49": "10_30", - "22:49": "10_30", -}; +import { describe, expect, test, vi } from "vitest"; +import { getFaviconFileName, getDayProgress } from "./utils"; describe("getFaviconFileName", () => { test("should return expected favicon file name", () => { + const TIMES = { + "02:03": "02_00", + "14:03": "02_00", + "02:00": "02_00", + "02:30": "02_30", + "00:00": "00_00", + "12:00": "00_00", + "02:49": "02_30", + "14:49": "02_30", + "10:49": "10_30", + "22:49": "10_30", + }; + Object.entries(TIMES).forEach(([time, fileName]) => { expect(getFaviconFileName(time)).toEqual(fileName); }); }); }); + +describe("getDayProgress", () => { + test("should return expected progress of the day", () => { + vi.useFakeTimers(); + + const PROGRESS = { + "0": new Date(2024, 5, 11, 0, 0, 0), + "25": new Date(2024, 5, 11, 6, 0, 0), + "32.78": new Date(2024, 5, 11, 7, 52, 6), + "50": new Date(2024, 5, 11, 12, 0, 0), + "60.83": new Date(2024, 5, 11, 14, 36, 0), + "75": new Date(2024, 5, 11, 18, 0, 0), + "100": new Date(2024, 5, 11, 23, 59, 59), + }; + + Object.entries(PROGRESS).forEach(([progress, date]) => { + vi.setSystemTime(date); + + expect(getDayProgress()).toEqual(parseFloat(progress)); + }); + + vi.useRealTimers(); + }); +}); diff --git a/src/utils/utils.ts b/src/utils/utils.ts index 8547be75c..005945d0e 100644 --- a/src/utils/utils.ts +++ b/src/utils/utils.ts @@ -21,6 +21,38 @@ export function getTime() { ); } +export function getDayProgress() { + const now = new Date(); + const seconds = + now.getHours() * 3600 + now.getMinutes() * 60 + now.getSeconds(); + const progress = (seconds * 100) / 86400; + + return parseFloat(progress.toFixed(2)); +} + +export function getDayParameters() { + const progress = getDayProgress(); + const render = progress >= 25 && progress < 75 ? "sun" : "moon"; + const factor = progress < 50 ? 1 : -1; + let stage; + + if (progress < 25) { + stage = "dawn"; + } else if (progress >= 25 && progress < 50) { + stage = "sunrise"; + } else if (progress >= 50 && progress < 75) { + stage = "sunset"; + } else if (progress >= 75) { + stage = "dusk"; + } + + return { + render, + stage, + factor, + }; +} + export function updateGHLinks(time: string, quote: Quote, locale: Locale) { const quoteRaw = `${quote.quote_first}${quote.quote_time_case}${quote.quote_last}`; From 0d4547b0c8219816a1982785b74733ba675a4d28 Mon Sep 17 00:00:00 2001 From: Carlos Bonadeo Date: Sun, 12 May 2024 20:41:47 +0100 Subject: [PATCH 02/12] getDayParameters --- src/utils/utils.test.ts | 53 +++++++++++++++++++++++++++++++++-------- src/utils/utils.ts | 24 +++++++++---------- 2 files changed, 54 insertions(+), 23 deletions(-) diff --git a/src/utils/utils.test.ts b/src/utils/utils.test.ts index 7acc38fa8..7efc40626 100644 --- a/src/utils/utils.test.ts +++ b/src/utils/utils.test.ts @@ -1,5 +1,15 @@ import { describe, expect, test, vi } from "vitest"; -import { getFaviconFileName, getDayProgress } from "./utils"; +import { getFaviconFileName, getDayProgress, getDayParameters } from "./utils"; + +const PROGRESS = { + "0": new Date(2024, 5, 11, 0, 0, 0), + "25": new Date(2024, 5, 11, 6, 0, 0), + "32.78": new Date(2024, 5, 11, 7, 52, 6), + "50": new Date(2024, 5, 11, 12, 0, 0), + "60.83": new Date(2024, 5, 11, 14, 36, 0), + "75": new Date(2024, 5, 11, 18, 0, 0), + "100": new Date(2024, 5, 11, 23, 59, 59), +}; describe("getFaviconFileName", () => { test("should return expected favicon file name", () => { @@ -26,20 +36,43 @@ describe("getDayProgress", () => { test("should return expected progress of the day", () => { vi.useFakeTimers(); - const PROGRESS = { - "0": new Date(2024, 5, 11, 0, 0, 0), - "25": new Date(2024, 5, 11, 6, 0, 0), - "32.78": new Date(2024, 5, 11, 7, 52, 6), - "50": new Date(2024, 5, 11, 12, 0, 0), - "60.83": new Date(2024, 5, 11, 14, 36, 0), - "75": new Date(2024, 5, 11, 18, 0, 0), - "100": new Date(2024, 5, 11, 23, 59, 59), + Object.entries(PROGRESS).forEach(([progress, date]) => { + vi.setSystemTime(date); + + expect(getDayProgress()).toEqual(parseFloat(progress)); + }); + + vi.useRealTimers(); + }); +}); + +describe("getDayParameters", () => { + test("should return expected parameters", () => { + const dayParameterResponse = ( + scene: string, + actor: string, + factor: number, + progress: number + ) => ({ scene, actor, factor, progress }); + + const PARAMETERS = { + "0": dayParameterResponse("dawn", "moon", 1, 0), + "25": dayParameterResponse("sunrise", "sun", 1, 25), + "32.78": dayParameterResponse("sunrise", "sun", 1, 32.78), + "50": dayParameterResponse("sunset", "sun", -1, 50), + "60.83": dayParameterResponse("sunset", "sun", -1, 60.83), + "75": dayParameterResponse("dusk", "moon", -1, 75), + "100": dayParameterResponse("dusk", "moon", -1, 100), }; + vi.useFakeTimers(); + Object.entries(PROGRESS).forEach(([progress, date]) => { vi.setSystemTime(date); - expect(getDayProgress()).toEqual(parseFloat(progress)); + expect(getDayParameters()).toEqual( + PARAMETERS[progress as keyof typeof PARAMETERS] + ); }); vi.useRealTimers(); diff --git a/src/utils/utils.ts b/src/utils/utils.ts index 005945d0e..180bf9f2b 100644 --- a/src/utils/utils.ts +++ b/src/utils/utils.ts @@ -32,24 +32,23 @@ export function getDayProgress() { export function getDayParameters() { const progress = getDayProgress(); - const render = progress >= 25 && progress < 75 ? "sun" : "moon"; + const actor = progress >= 25 && progress < 75 ? "sun" : "moon"; const factor = progress < 50 ? 1 : -1; - let stage; + let scene = "dawn"; - if (progress < 25) { - stage = "dawn"; - } else if (progress >= 25 && progress < 50) { - stage = "sunrise"; + if (progress >= 25 && progress < 50) { + scene = "sunrise"; } else if (progress >= 50 && progress < 75) { - stage = "sunset"; + scene = "sunset"; } else if (progress >= 75) { - stage = "dusk"; + scene = "dusk"; } return { - render, - stage, + actor, + scene, factor, + progress, }; } @@ -84,9 +83,8 @@ export function updateGHLinks(time: string, quote: Quote, locale: Locale) { reportErrorUrl.searchParams.set("author", quote.author); reportErrorUrl.searchParams.set("quote", quoteRaw.replace(/
/g, " ")); - const reportError = document.getElementById( - "report-error" - ) as HTMLAnchorElement; + const reportError = + document.querySelector("#report-error"); if (reportError) { reportError.href = reportErrorUrl.href; } From e9cddd8ab551b855483addfdebaa4b02171e4683 Mon Sep 17 00:00:00 2001 From: Carlos Bonadeo Date: Sun, 12 May 2024 22:00:39 +0100 Subject: [PATCH 03/12] Improve test --- index.html | 1 + src/utils/utils.test.ts | 86 ++++++++++++++++++++++++----------------- src/utils/utils.ts | 13 ++++++- 3 files changed, 62 insertions(+), 38 deletions(-) diff --git a/index.html b/index.html index 07c452657..5fa570579 100644 --- a/index.html +++ b/index.html @@ -187,6 +187,7 @@ + From 63ae018e395ecfc34014e19d7e9d837b2d02cd03 Mon Sep 17 00:00:00 2001 From: Carlos Bonadeo Date: Mon, 13 May 2024 00:29:17 +0100 Subject: [PATCH 05/12] Add some styles --- index.html | 1 + src/modules/clock.ts | 14 +++++-- src/modules/dynamic.test.ts | 73 ++++++++++++++++++++++++++++++++++ src/modules/dynamic.ts | 48 ++++++++++++++++++++++ src/modules/themes.ts | 5 +++ src/strings/translations.json | 5 +++ src/styles/styles.css | 5 ++- src/styles/themes/dynamic.css | 40 +++++++++++++++++++ src/utils/utils.test.ts | 75 +---------------------------------- src/utils/utils.ts | 40 ------------------- 10 files changed, 187 insertions(+), 119 deletions(-) create mode 100644 src/modules/dynamic.test.ts create mode 100644 src/modules/dynamic.ts create mode 100644 src/styles/themes/dynamic.css diff --git a/index.html b/index.html index 0c2108171..9da06d5a0 100644 --- a/index.html +++ b/index.html @@ -188,6 +188,7 @@ + diff --git a/src/modules/dynamic.test.ts b/src/modules/dynamic.test.ts index a26f6805a..ca550c1f2 100644 --- a/src/modules/dynamic.test.ts +++ b/src/modules/dynamic.test.ts @@ -38,24 +38,24 @@ describe("getDayProgress", () => { describe("getDayParameters", () => { const dayParameterResponse = ( scene: string, - actorLeft: number, - opacity: number, + period: string, + segment: number, progress: number - ) => ({ scene, actorLeft, opacity, progress }); + ) => ({ scene, period, segment, progress }); const PARAMETERS = { - "0": dayParameterResponse("night", 50, 0, 0), - "16.67": dayParameterResponse("night", 83.34, 0.33, 16.67), - "25": dayParameterResponse("morning", 0, 0.5, 25), - "32.78": dayParameterResponse("morning", 15.56, 0.66, 32.78), - "33.33": dayParameterResponse("morning", 16.66, 0.67, 33.33), - "37.5": dayParameterResponse("morning", 25, 0.75, 37.5), - "45.83": dayParameterResponse("morning", 41.66, 0.92, 45.83), - "50": dayParameterResponse("morning", 50, 1, 50), - "60.83": dayParameterResponse("afternoon", 71.66, 0.78, 60.83), - "75": dayParameterResponse("evening", 100, 0.5, 75), - "79.17": dayParameterResponse("evening", 8.34, 0.42, 79.17), - "100": dayParameterResponse("night", 50, 0, 100), + "0": dayParameterResponse("night", "am", 0, 0), + "16.67": dayParameterResponse("night", "am", 1, 16.67), + "25": dayParameterResponse("morning", "am", 2, 25), + "32.78": dayParameterResponse("morning", "am", 3, 32.78), + "33.33": dayParameterResponse("morning", "am", 3, 33.33), + "37.5": dayParameterResponse("morning", "am", 3, 37.5), + "45.83": dayParameterResponse("morning", "am", 4, 45.83), + "50": dayParameterResponse("morning", "pm", 4, 50), + "60.83": dayParameterResponse("afternoon", "pm", 5, 60.83), + "75": dayParameterResponse("evening", "pm", 6, 75), + "79.17": dayParameterResponse("evening", "pm", 6, 79.17), + "100": dayParameterResponse("night", "pm", 8, 100), }; Object.entries(PROGRESS).forEach(([progress, date]) => { diff --git a/src/modules/dynamic.ts b/src/modules/dynamic.ts index 0fb0850f3..b363c1f34 100644 --- a/src/modules/dynamic.ts +++ b/src/modules/dynamic.ts @@ -3,7 +3,7 @@ const MOON_PHASES = [ "waxing-crescent", "first-quarter", "waxing-gibbous", - "full-moon", + "full", "waning-gibbous", "third-quarter", "waning-crescent", @@ -26,7 +26,6 @@ export function getDayParameters() { const progress = getDayProgress(); // 9pm to 5am let scene = "night"; - // 5am to 12pm if (progress >= 20.83 && progress <= 50) { scene = "morning"; @@ -38,41 +37,32 @@ export function getDayParameters() { scene = "evening"; } - const opacity = parseFloat( - (progress <= 50 ? progress / 50 : 1 - (progress / 50 - 1)).toFixed(2) - ); + const segment = Math.round((progress * 8) / 100); - // const actorLeft = - // progress <= 25 ? (progress * 100) / 25 : ((progress - 25) * 100) / 75; - const actorLeft = - progress >= 25 && progress <= 75 - ? ((progress - 25) * 100) / 50 - : progress > 75 - ? ((progress - 75) * 50) / 25 - : ((progress + 25) * 100) / 50; + const period = progress < 50 ? "am" : "pm"; return { scene: testScene || scene, - opacity, progress, - actorLeft: parseFloat(actorLeft.toFixed(2)), + period, + segment, }; } export function setDayParameters() { - const { opacity, progress, scene, actorLeft } = getDayParameters(); + const { progress, scene, segment, period } = getDayParameters(); - if (!document.querySelector(".sun.moon")) { + if (!document.querySelector(".sphere")) { const actor = document.createElement("div"); - actor.classList.add("sun", "moon"); + actor.classList.add("sphere"); actor.classList.toggle(moonPhase, scene === "night"); document.querySelector("main")?.appendChild(actor); } const root = document.querySelector(":root"); - root?.style.setProperty("--day-opacity", opacity.toString()); - root?.style.setProperty("--actor-left", actorLeft.toString()); root?.style.setProperty("--day-progress", progress.toString()); root?.setAttribute("data-progress", progress.toString()); root?.setAttribute("data-scene", scene); + root?.setAttribute("data-period", period); + root?.setAttribute("data-segment", segment.toString()); } diff --git a/src/modules/fonts.ts b/src/modules/fonts.ts index 025824e68..f27503a60 100644 --- a/src/modules/fonts.ts +++ b/src/modules/fonts.ts @@ -19,12 +19,13 @@ export const THEME_FONTS: Record = { subtle: ["Unna"], poster: ["Averia Serif Libre", "Allura"], dynamic: [ - "Sunshiney", + "Caveat", "Yeseva One", "Rye", "Megrim", "Jacquard 12", "Sacramento", + "Indie Flower", ], }; diff --git a/src/strings/translations.json b/src/strings/translations.json index a84ea993c..ce19e5ca7 100644 --- a/src/strings/translations.json +++ b/src/strings/translations.json @@ -52,7 +52,7 @@ "it-IT": "Italian", "random": "Random language", - "themeFont": "Theme font" + "default_font": "Default font" }, "es-ES": { "document_title": "Reloj Literario", @@ -107,7 +107,7 @@ "it-IT": "Italiano", "random": "Lenguaje aleatorio", - "themeFont": "Fuente del tema" + "default_font": "Fuente por defecto" }, "pt-BR": { "document_title": "Relógio da Literatura", @@ -162,7 +162,7 @@ "it-IT": "Italiano", "random": "Idioma aleatório", - "themeFont": "Fonte do tema" + "default_font": "Fonte padrão" }, "fr-FR": { "document_title": "Horloge de la Littérature", @@ -217,7 +217,7 @@ "it-IT": "Italien", "random": "Langue aléatoire", - "themeFont": "Police du thème" + "default_font": "Police par défaut" }, "it-IT": { "document_title": "Orologio della Letteratura", @@ -273,6 +273,6 @@ "it-IT": "Italiano", "random": "Lingua casuale", - "themeFont": "Font del tema " + "default_font": "Font predefinito " } } diff --git a/src/styles/themes/dynamic.css b/src/styles/themes/dynamic.css index 64c9167b5..f73efc671 100644 --- a/src/styles/themes/dynamic.css +++ b/src/styles/themes/dynamic.css @@ -1,8 +1,9 @@ [data-theme|="dynamic"] body { position: relative; + transition: background 120s; } -[data-theme|="dynamic"] .sun.moon { +[data-theme|="dynamic"] .sphere { content: ""; width: 10vw; height: 10vw; @@ -10,69 +11,87 @@ min-height: 50px; border-radius: 50%; position: absolute; - left: calc(var(--actor-left) * 1%); - transition: left 60s; - transform: translateX(-50%); - top: 100px; + left: calc(var(--day-progress) * 1%); + transition: left 60s, top 60s; + transform: translateX(-50%) translateY(-50%); } -[data-theme|="dynamic"][data-scene="evening"] .sun { +[data-theme|="dynamic"][data-scene="evening"] .sphere { background-color: #000000; box-shadow: 0 0 0 0 rgb(189, 189, 189) inset; } -[data-theme|="dynamic"][data-scene="morning"] .sun { - background-color: rgb(242, 242, 184); - box-shadow: 0 0 51px 18px rgb(238, 241, 21), 0 0 71px 58px rgb(241, 114, 21); - filter: blur(2px); - top: calc(100% - var(--day-progress) * 1%); - opacity: 0.5; -} - [data-theme|="dynamic"] blockquote:before, [data-theme|="dynamic"] blockquote:after { content: none; } [data-theme|="dynamic"][data-scene="morning"] { + --background: transparent; + --font-color: #000; --background-image: linear-gradient( to top, rgb(230, 174, 41), rgb(246, 248, 98) 18.49%, #9dc6ff 57.98% ); - --quote-font-family: "Sunshiney", cursive; + --quote-font-family: "Caveat", cursive; --font-color: #333; } -[data-theme|="dynamic"][data-scene="morning"] blockquote { - padding: 1rem 2rem; - border-radius: 0.25rem; +[data-theme|="dynamic"][data-scene="morning"] .sphere { + background-color: rgb(242, 242, 184); + box-shadow: 0 0 51px 18px rgb(238, 241, 21), 0 0 71px 58px rgb(241, 114, 21); + filter: blur(2px); + top: calc(100% - var(--day-progress) * 1%); + opacity: 0.5; } [data-theme|="dynamic"][data-scene="afternoon"] { - --background-image: linear-gradient( - 0deg, - rgba(173, 53, 25, 1) 0%, - rgba(250, 131, 73, 1) 9%, - rgba(170, 125, 131, 1) 21%, - rgba(50, 47, 76, 1) 65%, - rgba(28, 27, 43, 1) 86% - ); - --accent-color: rgba(28, 27, 43, 1); + --background: #c2e9fb; + --font-color: #000; + --background-image: linear-gradient(to top, #c2e9fb, #a1c4fd); + --quote-font-family: "Indie Flower", cursive; +} + +[data-theme|="dynamic"][data-scene="afternoon"] .sphere { + background-color: rgb(242, 242, 184); + box-shadow: 0 0 51px 18px rgb(238, 241, 21), 0 0 71px 58px rgb(241, 114, 21); + filter: blur(2px); + top: calc(100% - var(--day-progress) * 1%); + opacity: 0.7; } [data-theme|="dynamic"][data-scene="evening"] { + --background: rgb(14, 14, 36); + --font-color: #fff; --background-image: linear-gradient( to top, rgba(173, 53, 25, 1), rgba(250, 131, 73, 1) 4.2%, rgba(170, 125, 131, 1) 10.5%, rgba(50, 47, 76, 1) 26.89%, - rgb(7, 7, 21) 40.34% + rgb(14, 14, 36) 60.34% ); --quote-font-family: "Megrim", serif; --font-color: #fafafa; + --accent-color: #ffa700; +} + +[data-theme|="dynamic"][data-scene="evening"] .sphere { + background-color: rgb(242, 242, 184); + box-shadow: 0 0 51px 18px rgb(238, 241, 21), 0 0 71px 58px rgb(241, 114, 21); + filter: blur(2px); + top: calc(var(--day-progress) * 1%); + opacity: 0.6; +} + +[data-theme|="dynamic"][data-scene="evening"] blockquote { + text-shadow: 0 0 2px #05021a, 0 0 4px #000; +} + +[data-theme|="dynamic"][data-scene="evening"] footer a { + color: #000000; } [data-theme|="dynamic"][data-scene="night"] { @@ -220,12 +239,20 @@ text-shadow: 0 0 2px #05021a, 0 0 4px #000; } -[data-theme|="dynamic"][data-scene="night"] .moon { +[data-theme|="dynamic"][data-scene="night"] .sphere { background-color: #f0f0f0; overflow: hidden; animation: glow 2s ease-in-out infinite alternate; } +[data-theme|="dynamic"][data-scene="night"][day-period="pm"] .sphere { + left: calc(100% - var(--day-progress) * 1%); +} + +[data-theme|="dynamic"][data-scene="night"][day-period="am"] .sphere { + left: calc(var(--day-progress) * 1%); +} + @keyframes glow { from { box-shadow: var(--glow-box-shadow); @@ -235,7 +262,7 @@ } } -[data-theme|="dynamic"][data-scene="night"] .moon:before { +[data-theme|="dynamic"][data-scene="night"] .sphere:before { content: ""; height: 2.5vw; width: 2.5vw; @@ -248,17 +275,17 @@ box-shadow: 2vw 2vw 0 -0.3vw #c5cdd4, -0.7vw 2.9vw 0 -0.5vw #c5cdd4; } -[data-theme|="dynamic"][data-scene="night"] .moon.new { +[data-theme|="dynamic"][data-scene="night"] .sphere.new { background: #262626; } -[data-theme|="dynamic"][data-scene="night"] .moon.new:before { +[data-theme|="dynamic"][data-scene="night"] .sphere.new:before { opacity: 0.02; } -[data-theme|="dynamic"][data-scene="night"] .moon.waxing-crescent:after, -[data-theme|="dynamic"][data-scene="night"] .moon.first-quarter:after, -[data-theme|="dynamic"][data-scene="night"] .moon.waning-crescent:after, -[data-theme|="dynamic"][data-scene="night"] .moon.third-quarter:after { +[data-theme|="dynamic"][data-scene="night"] .sphere.waxing-crescent:after, +[data-theme|="dynamic"][data-scene="night"] .sphere.first-quarter:after, +[data-theme|="dynamic"][data-scene="night"] .sphere.waning-crescent:after, +[data-theme|="dynamic"][data-scene="night"] .sphere.third-quarter:after { height: 11vw; content: ""; width: 10vw; @@ -269,30 +296,30 @@ min-width: 50px; } -[data-theme|="dynamic"][data-scene="night"] .moon.waxing-crescent:after { +[data-theme|="dynamic"][data-scene="night"] .sphere.waxing-crescent:after { transform: translateX(-25%) translateY(-5px); border-radius: 50%; } -[data-theme|="dynamic"][data-scene="night"] .moon.first-quarter:after { +[data-theme|="dynamic"][data-scene="night"] .sphere.first-quarter:after { transform: translateX(-50%) translateY(-3px); } -[data-theme|="dynamic"][data-scene="night"] .moon.waning-crescent:after { +[data-theme|="dynamic"][data-scene="night"] .sphere.waning-crescent:after { transform: translateX(25%) translateY(-5px); border-radius: 50%; } -[data-theme|="dynamic"][data-scene="night"] .moon.waning-gibbous { +[data-theme|="dynamic"][data-scene="night"] .sphere.waning-gibbous { box-shadow: var(--glow-box-shadow), -3.5vw 0 1px -1.5vw rgba(0, 0, 0, 0.85) inset; } -[data-theme|="dynamic"][data-scene="night"] .moon.waxing-gibbous { +[data-theme|="dynamic"][data-scene="night"] .sphere.waxing-gibbous { box-shadow: var(--glow-box-shadow), 38px 0 1px -15px rgba(0, 0, 0, 0.85) inset; } -[data-theme|="dynamic"][data-scene="night"] .moon.third-quarter:after { +[data-theme|="dynamic"][data-scene="night"] .sphere.third-quarter:after { transform: translateX(50%) translateY(-3px); }