如何在界面上实现主题效果,主要是网页白天与黑夜两种配色方案之间切换
我记录了几种方式
CSS 变量
可以使用,但是给我的体验并不太好,值会呈现在标签 style
属性当中
body {
--size: 100px;
--time: 500ms;
--color1: #fff;
--color2: #000;
margin: 0;
width: 100vw;
height: 100vh;
background: var(--color1);
transition: background var(--time);
}
div {
width: var(--size);
height: var(--size);
color: var(--color1);
background: var(--color2);
transition: background var(--time);
}
<div>
文本
</div>
<script>
const div = document.querySelector("div")
const style = document.body.style
div.onclick = (() => {
let isDark = false
return () => {
if (isDark) {
style.setProperty("--color1", "#fff");
style.setProperty("--color2", "#000");
} else {
style.setProperty("--color1", "#000");
style.setProperty("--color2", "#fff");
}
isDark = !isDark
}
})()
</script>
动态写入
在写这一段时,我在寻找使用 JavaScript 动态修改
@keyframes
的方法,得到的一种方案
获取标签,直接修改当中的内容
<style id="root">
:root {
--size: 300px;
--color1: #000;
--color2: #990;
--color3: #090;
--color4: #099;
}
</style>
<style>
div {
width: var(--size);
height: var(--size);
background: var(--color1);
animation: play 5s infinite;
}
@keyframes play {
0% {
background: var(--color1);
transform: translate(0, 0);
}
25% {
background: var(--color2);
transform: translate(var(--size), 0);
}
50% {
background: var(--color3);
transform: translate(var(--size), var(--size));
}
75% {
background: var(--color4);
transform: translate(0, var(--size));
}
100% {
background: var(--color1);
transform: translate(0, 0);
}
}
</style>
<input type="number">
<button>
修改 size
</button>
<div></div>
<script>
const btn = document.querySelector("button")
const input = document.querySelector("input")
const style = document.querySelector("#root")
btn.onclick = () => {
style.innerText = `
:root {
--size: ${
input.value}px;
--color1: #000;
--color2: #990;
--color3: #090;
--color4: #099;
}
`
}
</script>
当中使用了 CSS 变量,发现了一个问题:--size
改变后,div
大小发随之变化,但是 @keyframes
节奏没有变,依旧按照 300 距离移动
这也是说,@keyframes
在定义的那一刻就固定了,并不会追踪变量变化而随之变动
当然,响应式动画是没有问题的。当触发时,拿到的会是最新的变量值
@media (min-width: 500px) {
:root {
--size: 100px;
--color1: #000;
--color2: #990;
--color3: #090;
--color4: #099;
}
@keyframes play {
0% {
background: var(--color1);
transform: translate(0, 0);
}
25% {
background: var(--color2);
transform: translate(calc(var(--size) / 3), 0);
}
50% {
background: var(--color3);
transform: translate(calc(var(--size) / 3), calc(var(--size) / 3));
}
75% {
background: var(--color4);
transform: translate(0, calc(var(--size) / 3));
}
100% {
background: var(--color1);
transform: translate(0, 0);
}
}
}
@media (min-width: 800px) {
:root {
--size: 300px;
--color1: #000;
--color2: #990;
--color3: #090;
--color4: #099;
}
@keyframes play {
0% {
background: var(--color1);
transform: translate(0, 0);
}
25% {
background: var(--color2);
transform: translate(var(--size), 0);
}
50% {
background: var(--color3);
transform: translate(var(--size), var(--size));
}
75% {
background: var(--color4);
transform: translate(0, var(--size));
}
100% {
background: var(--color1);
transform: translate(0, 0);
}
}
}
不过这并不能用于动态插入,需要手动触发响应式进行刷新,想要一个良好的体验,就需要将变量与 @keyframes
一起重写
JavaScript 部分省略,只在后段加入了固定的
@keyframes
:root {
--size: 300px;
--color1: #000;
--color2: #990;
--color3: #090;
--color4: #099;
}
@keyframes play {
0% {
background: var(--color1);
transform: translate(0, 0);
}
25% {
background: var(--color2);
transform: translate(var(--size), 0);
}
50% {
background: var(--color3);
transform: translate(var(--size), var(--size));
}
75% {
background: var(--color4);
transform: translate(0, var(--size));
}
100% {
background: var(--color1);
transform: translate(0, 0);
}
}
动态标签
提前写好 CSS 文件,触发时将其插入至 HTML 当中,利用覆盖来实现该效果
:root {
--bg-color: #fff;
--text-color: #000;
}
body {
margin: 0;
width: 100vw;
height: 100vh;
font-size: 5em;
text-align: center;
color: var(--text-color);
background: var(--bg-color);
}
body::before {
content: 'light';
}
/* dark.css */
:root {
--bg-color: #000;
--text-color: #fff;
}
body::before {
content: 'dark';
}
document.body.onclick = (() => {
let isDark = false
return () => {
if (isDark) {
const link = document.querySelector("#darkTheme")
link.remove()
} else {
const link = document.createElement('link')
link.id = "darkTheme"
link.rel = "stylesheet"
link.href = "./css/dark.css"
document.head.append(link)
}
isDark = !isDark
}
})()
该方法的简便性很好,并不暴力,也是推荐做法
小结
可见 CSS 变量的存在,省略掉了很多事情,在没有变量之前,想要实现第三种方案,就需要对整体重写
使用 CSS-in-JS 方案可以做到动态 @keyframes
,不过当前还未研究过,是如何实现的。是否存在更好的解决方案,而非暴力写入