如何实现网页主题切换

如何在界面上实现主题效果,主要是网页白天与黑夜两种配色方案之间切换

我记录了几种方式

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,不过当前还未研究过,是如何实现的。是否存在更好的解决方案,而非暴力写入

猜你喜欢

转载自blog.csdn.net/qq_49661519/article/details/121766316