网页深色模式切换:简单实现方法与平滑动画效果

更新日期: 2025-10-12 阅读: 113 标签: 模式

现在很多网站都提供了深色模式功能。一个好的深色模式切换不仅要能改变颜色,还要有流畅的动画效果,并且能记住用户的选择。下面我们来学习如何实现这个功能。


基本实现思路

1. 准备两套颜色方案

首先需要为网站设计浅色和深色两套颜色。建议使用 css 变量来管理这些颜色:

:root {
  --bg-color: #ffffff;
  --text-color: #000000;
  --border-color: #dddddd;
}

[] {
  --bg-color: #121212;
  --text-color: #ffffff;
  --border-color: #333333;
}

2. 在网页中使用这些颜色

定义好颜色变量后,在整个网站中使用它们:

body {
  background-color: var(--bg-color);
  color: var(--text-color);
  transition: background-color 0.3s ease, color 0.3s ease;
}

.card {
  background-color: var(--bg-color);
  border: 1px solid var(--border-color);
}

3. 添加切换按钮

html 中添加一个切换按钮:

<button id="theme-toggle">切换到深色模式</button>


完整的代码示例

HTML 结构

<!DOCTYPE html>
<html lang="zh-CN" >="light">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>深色模式演示</title>
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <header>
    <h1>我的网站</h1>
    <button id="theme-toggle">切换到深色模式</button>
  </header>
  
  <main>
    <div class="card">
      <h2>内容标题</h2>
      <p>这是一段示例文本,用来展示深色模式下的效果。</p>
    </div>
  </main>

  <script src="script.js"></script>
</body>
</html>

CSS 样式

/* 定义颜色变量 */
:root {
  --bg-color: #ffffff;
  --text-color: #000000;
  --card-bg: #f8f9fa;
  --border-color: #dee2e6;
  --primary-color: #007bff;
}

[] {
  --bg-color: #121212;
  --text-color: #e9ecef;
  --card-bg: #1e1e1e;
  --border-color: #495057;
  --primary-color: #4dabf7;
}

/* 应用样式 */
body {
  margin: 0;
  font-family: system-ui, -apple-system, sans-serif;
  background-color: var(--bg-color);
  color: var(--text-color);
  transition: background-color 0.3s ease, color 0.3s ease;
  line-height: 1.6;
}

header {
  padding: 1rem 2rem;
  border-bottom: 1px solid var(--border-color);
  display: flex;
  justify-content: space-between;
  align-items: center;
}

main {
  padding: 2rem;
  max-width: 800px;
  margin: 0 auto;
}

.card {
  background-color: var(--card-bg);
  padding: 1.5rem;
  border-radius: 8px;
  border: 1px solid var(--border-color);
  transition: all 0.3s ease;
}

button {
  background-color: var(--primary-color);
  color: white;
  border: none;
  padding: 0.5rem 1rem;
  border-radius: 4px;
  cursor: pointer;
  font-size: 14px;
  transition: background-color 0.2s ease;
}

button:hover {
  opacity: 0.9;
}

JavaScript 逻辑

class ThemeManager {
  constructor() {
    this.toggleButton = document.getElementById('theme-toggle');
    this.htmlElement = document.documentElement;
    this.init();
  }

  init() {
    // 从本地存储获取主题设置,如果没有就使用系统偏好
    const savedTheme = localStorage.getItem('theme');
    const systemPrefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
    
    const initialTheme = savedTheme || (systemPrefersDark ? 'dark' : 'light');
    this.setTheme(initialTheme);
    
    // 监听按钮点击
    this.toggleButton.addEventListener('click', () => this.toggleTheme());
    
    // 监听系统主题变化
    window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', (e) => {
      if (!localStorage.getItem('theme')) {
        this.setTheme(e.matches ? 'dark' : 'light');
      }
    });
  }

  setTheme(theme) {
    this.htmlElement.setAttribute('>, theme);
    localStorage.setItem('theme', theme);
    this.updateButtonText(theme);
  }

  toggleTheme() {
    const currentTheme = this.htmlElement.getAttribute('>);
    const newTheme = currentTheme === 'dark' ? 'light' : 'dark';
    this.setTheme(newTheme);
  }

  updateButtonText(theme) {
    this.toggleButton.textContent = theme === 'dark' ? '切换到浅色模式' : '切换到深色模式';
  }
}

// 初始化主题管理器
new ThemeManager();


高级动画效果

如果想要更炫酷的切换效果,可以使用 View Transitions api

class AdvancedThemeManager extends ThemeManager {
  async toggleTheme() {
    // 检查浏览器是否支持 View Transitions API
    if (!document.startViewTransition) {
      return super.toggleTheme();
    }

    const currentTheme = this.htmlElement.getAttribute('>);
    const newTheme = currentTheme === 'dark' ? 'light' : 'dark';

    // 使用 View Transitions API 创建平滑过渡
    const transition = document.startViewTransition(() => {
      this.setTheme(newTheme);
    });

    try {
      await transition.finished;
    } catch (error) {
      console.log('主题切换动画完成');
    }
  }
}

// 使用高级主题管理器
new AdvancedThemeManager();


在流行框架中的实现

react 版本

import { useState, useEffect } from 'react';

function useTheme() {
  const [theme, setTheme] = useState('light');

  useEffect(() => {
    const saved = localStorage.getItem('theme');
    const systemDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
    setTheme(saved || (systemDark ? 'dark' : 'light'));
  }, []);

  const toggleTheme = () => {
    const newTheme = theme === 'dark' ? 'light' : 'dark';
    setTheme(newTheme);
    localStorage.setItem('theme', newTheme);
    document.documentElement.setAttribute('>, newTheme);
  };

  return [theme, toggleTheme];
}

function ThemeToggle() {
  const [theme, toggleTheme] = useTheme();
  
  return (
    <button onClick={toggleTheme}>
      {theme === 'dark' ? '浅色模式' : '深色模式'}
    </button>
  );
}

vue 版本

<template>
  <button @click="toggleTheme">
    {{ theme === 'dark' ? '浅色模式' : '深色模式' }}
  </button>
</template>

<script>
import { ref, onMounted } from 'vue'

export default {
  setup() {
    const theme = ref('light')
    
    const toggleTheme = () => {
      theme.value = theme.value === 'dark' ? 'light' : 'dark'
      localStorage.setItem('theme', theme.value)
      document.documentElement.setAttribute('>, theme.value)
    }
    
    onMounted(() => {
      const saved = localStorage.getItem('theme')
      const systemDark = window.matchMedia('(prefers-color-scheme: dark)').matches
      theme.value = saved || (systemDark ? 'dark' : 'light')
      document.documentElement.setAttribute('>, theme.value)
    })
    
    return { theme, toggleTheme }
  }
}
</script>


重要注意事项

  1. 性能优化

    • 只对颜色相关的属性添加过渡动画

    • 避免对大量元素同时进行复杂动画

    • 使用 will-change: opacity, color, background-color 提示浏览器优化

  2. 可访问性

    • 确保颜色对比度符合 WCAG 标准

    • 为色盲用户提供足够的视觉区分

    • 不要完全依赖颜色来传达信息

  3. 测试要点

    • 在不同浏览器中测试效果

    • 检查移动设备上的表现

    • 验证打印样式是否正常


常见问题解决

页面加载时闪烁怎么办?
在 HTML 的 head 部分添加内联脚本快速设置主题:

<script>
  const savedTheme = localStorage.getItem('theme');
  const systemDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
  document.documentElement.setAttribute('>, 
    savedTheme || (systemDark ? 'dark' : 'light')
  );
</script>

如何支持更多主题?
可以扩展颜色变量系统:

[] {
  --bg-color: #f0f8ff;
  --text-color: #003366;
  --primary-color: #007bff;
}

[] {
  --bg-color: #f8fff0;
  --text-color: #1a331a;
  --primary-color: #28a745;
}


总结

实现一个完整的深色模式功能需要综合考虑颜色设计、动画效果、用户偏好和性能优化。通过 CSS 变量、JavaScript 控制和适当的动画,可以创建出既美观又实用的主题切换功能。

关键是要记住用户体验:切换要流畅,选择要持久,效果要一致。按照本文介绍的方法,你可以在任何网站上实现高质量的深色模式功能。

本文内容仅供个人学习/研究/参考使用,不构成任何决策建议或专业指导。分享/转载时请标明原文来源,同时请勿将内容用于商业售卖、虚假宣传等非学习用途哦~感谢您的理解与支持!

链接: https://fly63.com/article/detial/13000

js设计模式之单例模式,javascript如何将一个对象设计成单例

单例模式是我们开发中一个非常典型的设计模式,js单例模式要保证全局只生成唯一实例,提供一个单一的访问入口,单例的对象不同于静态类,我们可以延迟单例对象的初始化,通常这种情况发生在我们需要等待加载创建单例的依赖。

前端设计模式:从js原始模式开始,去理解Js工厂模式和构造函数模式

工厂模式下的对象我们不能识别它的类型,由于typeof返回的都是object类型,不知道它是那个对象的实例。另外每次造人时都要创建一个独立的person的对象,会造成代码臃肿的情况。

JavaScript设计模式_js实现建造者模式

建造者模式:是将一个复杂的对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。工厂类模式提供的是创建单个类的模式,而建造者模式则是将各种产品集中起来进行管理,用来创建复合对象

html和xhtml,DOCTYPE和DTD,标准模式和兼容模式

主要涉及知识点: HTML与XHTML,HTML与XHTML的区别,DOCTYPE与DTD的概念,DTD的分类以及DOCTYPE的声明方式,标准模式(Standard Mode)和兼容模式(Quircks Mode),标准模式(Standard Mode)和兼容模式(Quircks Mode)的区别

前端四种设计模式_JS常见的4种模式

JavaScript中常见的四种设计模式:工厂模式、单例模式、沙箱模式、发布者订阅模式

javascript 策略模式_理解js中的策略模式

javascript 策略模式的定义是:定义一系列的算法,把它们一个个封装起来,并且使它们可以相互替换。 策略模式利用组合,委托等技术和思想,有效的避免很多if条件语句,策略模式提供了开放-封闭原则,使代码更容易理解和扩展, 策略模式中的代码可以复用。

javascript观察者模式_深入理解js中的观察者模式

javascript观察者模式又叫发布订阅模式,观察者模式的好处:js观察者模式支持简单的广播通信,自动通知所有已经订阅过的对象。存在一种动态关联,增加了灵活性。目标对象与观察者之间的抽象耦合关系能够单独扩展以及重用。

Vue中如何使用方法、计算属性或观察者

熟悉 Vue 的都知道 方法methods、计算属性computed、观察者watcher 在 Vue 中有着非常重要的作用,有些时候我们实现一个功能的时候可以使用它们中任何一个都是可以的

我最喜欢的 JavaScript 设计模式

我觉得聊一下我爱用的 JavaScript 设计模式应该很有意思。我是一步一步才定下来的,经过一段时间从各种来源吸收和适应直到达到一个能提供我所需的灵活性的模式。让我给你看看概览,然后再来看它是怎么形成的

Flutter 设计模式 - 简单工厂

在围绕设计模式的话题中,工厂这个词频繁出现,从 简单工厂 模式到 工厂方法 模式,再到 抽象工厂 模式。工厂名称含义是制造产品的工业场所,应用在面向对象中,顺理成章地成为了比较典型的创建型模式

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!