Skip to content

Commit

Permalink
feat: accent color transition
Browse files Browse the repository at this point in the history
  • Loading branch information
Innei committed Oct 3, 2023
1 parent 212eaea commit fbb4ecc
Show file tree
Hide file tree
Showing 2 changed files with 125 additions and 9 deletions.
45 changes: 45 additions & 0 deletions src/lib/color.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,3 +123,48 @@ export function hexToHsl(hex: string) {
// Return the HSL values as a string
return [h, s, Math.round(l * 100)]
}

export function generateTransitionColors(
startColor: string,
targetColor: string,
step: number,
): string[] {
// Convert startColor and targetColor to RGB values
const startRed = parseInt(startColor.substring(1, 3), 16)
const startGreen = parseInt(startColor.substring(3, 5), 16)
const startBlue = parseInt(startColor.substring(5, 7), 16)

const targetRed = parseInt(targetColor.substring(1, 3), 16)
const targetGreen = parseInt(targetColor.substring(3, 5), 16)
const targetBlue = parseInt(targetColor.substring(5, 7), 16)

// Calculate increments for each color channel
const redIncrement = (targetRed - startRed) / step
const greenIncrement = (targetGreen - startGreen) / step
const blueIncrement = (targetBlue - startBlue) / step

const transitionColors: string[] = []

// Generate transition colors
for (let i = 0; i < step; i++) {
// Calculate transition color values
const transitionRed = Math.round(startRed + redIncrement * i)
const transitionGreen = Math.round(startGreen + greenIncrement * i)
const transitionBlue = Math.round(startBlue + blueIncrement * i)

// Convert RGB values to hex format
const hexColor = `#${(
(1 << 24) |
(transitionRed << 16) |
(transitionGreen << 8) |
transitionBlue
)
.toString(16)
.slice(1)}`

// Add transition color to the result array
transitionColors.push(hexColor)
}

return Array.from(new Set(transitionColors))
}
89 changes: 80 additions & 9 deletions src/providers/root/accent-color-provider.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { useEffect, useRef, useState } from 'react'
import { useServerInsertedHTML } from 'next/navigation'
import type { AccentColor } from '~/app/config'
import type { PropsWithChildren } from 'react'

import { sample } from '~/lib/_'
import { hexToHsl } from '~/lib/color'
import { generateTransitionColors, hexToHsl } from '~/lib/color'
import { noopObj } from '~/lib/noop'

import { useAppConfigSelector } from './aggregation-data-provider'
Expand All @@ -30,20 +30,91 @@ const accentColorDark = [
export const AccentColorProvider = ({ children }: PropsWithChildren) => {
const { light, dark } =
useAppConfigSelector((config) => config.color) || (noopObj as AccentColor)
useServerInsertedHTML(() => {
const accentColorL = sample(light ?? accentColorLight)
const accentColorD = sample(dark ?? accentColorDark)

const lightHsl = hexToHsl(accentColorL)
const darkHsl = hexToHsl(accentColorD)
const Length = Math.max(light?.length ?? 0, dark?.length ?? 0)
const randomSeedRef = useRef((Math.random() * Length) | 0)

const lightColors = light ?? accentColorLight
const darkColors = dark ?? accentColorDark
const currentAccentColorLRef = useRef(lightColors[randomSeedRef.current])
const currentAccentColorDRef = useRef(darkColors[randomSeedRef.current])

const [u, update] = useState(0)
useEffect(() => {
const $style = document.createElement('style')

const $originColor = document.getElementById('accent-color-style')

const nextSeed = (randomSeedRef.current + 1) % Length
const nextColorD = darkColors[nextSeed]
const nextColorL = lightColors[nextSeed]
const STEP = 60
const INTERVAL = 100
const colorsD = generateTransitionColors(
currentAccentColorDRef.current,
nextColorD,
STEP,
)
const colorsL = generateTransitionColors(
currentAccentColorLRef.current,
nextColorL,
STEP,
)

const timer = setTimeout(function updateAccent() {
const colorD = colorsD.shift()
const colorL = colorsL.shift()
if (colorD && colorL) {
currentAccentColorDRef.current = colorD
currentAccentColorLRef.current = colorL
setTimeout(updateAccent, INTERVAL)
} else {
randomSeedRef.current = nextSeed
currentAccentColorDRef.current = nextColorD
currentAccentColorLRef.current = nextColorL
update(u + 1)
}

const lightHsl = hexToHsl(currentAccentColorLRef.current)
const darkHsl = hexToHsl(currentAccentColorDRef.current)

const [hl, sl, ll] = lightHsl
const [hd, sd, ld] = darkHsl

$style.innerHTML = `html[data-theme='light'] {
--a: ${`${hl} ${sl}% ${ll}%`};
--af: ${`${hl} ${sl}% ${ll + 6}%`};
}
html[data-theme='dark'] {
--a: ${`${hd} ${sd}% ${ld}%`};
--af: ${`${hd} ${sd}% ${ld - 6}%`};
}
`
}, INTERVAL)
document.head.appendChild($style)
// FIXME should remove origin color, if not will not override origin color
$originColor?.remove()
return () => {
clearTimeout(timer)

setTimeout(() => {
document.head.removeChild($style)
}, 1000)
}
}, [Length, darkColors, lightColors, u])

useServerInsertedHTML(() => {
const lightHsl = hexToHsl(currentAccentColorLRef.current)
const darkHsl = hexToHsl(currentAccentColorDRef.current)

const [hl, sl, ll] = lightHsl
const [hd, sd, ld] = darkHsl

return (
<style
data-light={accentColorL}
data-dark={accentColorD}
id="accent-color-style"
data-light={currentAccentColorLRef.current}
data-dark={currentAccentColorDRef.current}
dangerouslySetInnerHTML={{
__html: `html[data-theme='light'] {
--a: ${`${hl} ${sl}% ${ll}%`};
Expand Down

1 comment on commit fbb4ecc

@vercel
Copy link

@vercel vercel bot commented on fbb4ecc Oct 3, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

shiro – ./

shiro-innei.vercel.app
shiro-git-main-innei.vercel.app
springtide.vercel.app
innei.in

Please sign in to comment.