A Pen by David Aerne on CodePen.
Created
July 2, 2023 17:06
-
-
Save meodai/8b82a15eb55fcc0d2c238f73d23d0e57 to your computer and use it in GitHub Desktop.
vanilla JS color-scale generator (supports p3/rec2020)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<div class="world"></div> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const hardStopsGradient = (arrOfColors) => { | |
const l = arrOfColors.length; | |
return arrOfColors.map( | |
(c, i) => `${c} ${i/l*100}% ${(i+1)/l*100}%` | |
).join(',') | |
} | |
const scaleSpreadArray = ( | |
initial, | |
targetSize, | |
fillFunction = (percent, lastValue, nextValue) => lastValue + percent * (nextValue - lastValue) | |
) => { | |
const valuesToAdd = targetSize - initial.length; | |
const chunkArray = initial.map((value) => [value]); | |
for (let i = 0; i < valuesToAdd; i++) { | |
chunkArray[i % (initial.length - 1)].push(null); | |
} | |
for (let i = 0; i < chunkArray.length - 1; i++) { | |
const currentChunk = chunkArray[i]; | |
const nextChunk = chunkArray[i + 1]; | |
const currentValue = currentChunk[0]; | |
const nextValue = nextChunk[0]; | |
for (let j = 1; j < currentChunk.length; j++) { | |
const percent = j / currentChunk.length; | |
currentChunk[j] = fillFunction(percent, currentValue, nextValue); | |
} | |
} | |
return chunkArray.flat(); | |
}; | |
const hxxToCSSokLCH = ({hue, chroma, lightness} = hxx) => `oklch(${lightness * 100}% ${chroma * .4} ${hue})`; | |
const generateHxxRamp = ( | |
colors = 4, | |
minHueDiffAngle = 60, | |
) => { | |
minHueDiffAngle = Math.min(minHueDiffAngle, 360/colors); | |
const baseHue = Math.random() * 360; | |
const huesToPickFrom = new Array( | |
Math.round(360 / minHueDiffAngle) | |
).fill('').map((_, i) => | |
(baseHue + i * minHueDiffAngle) % 360 | |
); | |
while (huesToPickFrom.length > colors) { | |
const randomIndex = Math.floor(Math.random() * huesToPickFrom.length); | |
huesToPickFrom.splice(randomIndex, 1); | |
} | |
const minLightness = Math.random() * .2; | |
const lightnessRange = .9 - minLightness; | |
const minChroma = .5 + Math.random() * .2; | |
const maxChroma = minChroma + .3; | |
const maxLightness = Math.min(minLightness + .2 + Math.random() * .2, .95); | |
return huesToPickFrom.map((hue, i) => ({ | |
lightness: minLightness + Math.random() * .1 + (lightnessRange/(colors-1)) * i, | |
chroma: minChroma + Math.random() * (maxChroma - minChroma), | |
hue, | |
})); | |
} | |
const generateColors = ( | |
colorsToGenerate, | |
colorsFinal, | |
minHueDiffAngle = 60, | |
minIn = `oklch shorter hue`, | |
) => { | |
const baseColors = generateHxxRamp(colorsToGenerate, minHueDiffAngle); | |
const cssColorStops = baseColors.map(hxxToCSSokLCH); | |
return colorsFinal > colorsToGenerate ? | |
scaleSpreadArray( | |
cssColorStops, | |
colorsFinal, | |
(percent, lastValue, nextValue) => | |
`color-mix(in ${minIn}, ${nextValue} ${percent * 100}%, ${lastValue})` | |
) : cssColorStops; | |
} | |
const $w = document.documentElement; | |
let doit = () => { | |
const gradient = generateColors( | |
Math.random() < .5 ? 2 : 4, | |
7 | |
); | |
$w.style.setProperty('--bg', hardStopsGradient(gradient)); | |
$w.style.setProperty('--bgs', gradient.join()); | |
} | |
document.documentElement.addEventListener('click', doit); | |
doit(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
.world { | |
--angle: 0deg; | |
background: linear-gradient(var(--angle), var(--bg)); | |
height: 80vmin; | |
width: 60vmin; | |
position: fixed; | |
top: 50%; | |
left: 50%; | |
transform: translate(-50%, -50%); | |
&::before { | |
content: ''; | |
position: absolute; | |
inset: 0; | |
background: inherit; | |
filter: blur(6vmin); | |
transform: scale(.95); | |
} | |
} | |
@media (orientation: landscape) { | |
.world { | |
--angle: 90deg; | |
height: 35vmin; | |
width: 80vmin; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment