首页 > 其他分享 >网页主题切换的实现方案

网页主题切换的实现方案

时间:2023-06-01 14:15:13浏览次数:33  
标签:网页 color 主题 样式 theme 切换 background --

各位看官大家好,想必大家在工作中一定遇到了网页主题切换或者一键换肤的功能,接下来我为大家整理了以下几种实现方案供大家参考学习。也请大家评论补充。

方案一:通过link标签动态引入

其做法就是提前准备好几套CSS主题样式文件,在需要的时候,创建link标签动态加载到head标签中,或者是动态改变link标签的href属性。

优点:

  • 实现了按需加载,提高了首屏加载时的性能

缺点:

  • 动态加载样式文件,如果文件过大网络情况不佳的情况下可能会有加载延迟,导致样式切换不流畅。

  • 各个主题样式是写死的,后续针对某一主题样式表修改或者新增主题也很麻烦。可维护性不高。

方案二:提前引入主题的样式,做类名的切换

这种方案与第一种比较类似,为了解决反复加载样式文件问题提前将样式全部引入,在需要切换主题的时候将指定的根元素类名更换,相当于直接做了样式覆盖,在该类名下的各个样式就统一地更换了。其基本方法如下:

body.light{
  color: #f90;
  background: #fff;
}
.light .xxx{

}
body.dark{
  color: #eee;
  background: #333;
}
.dark .xxx{

}

优点:

  • 不用重新加载样式文件,在样式切换时不会有卡顿

缺点:

  • 首屏加载时会牺牲一些时间加载样式资源

  • 各个主题样式是写死的,后续针对某一主题样式表修改或者新增主题也很麻烦

方案三:CSS变量名称+类名切换

大体思路跟方案2相似,依然是提前将样式文件载入,切换时将指定的根元素类名更换。不过这里相对灵活的是,默认在根作用域下定义好CSS变量,只需要在不同的主题下更改CSS变量对应的取值即可。
在Vue3官网有一个暗黑模式切换按钮,点击之后就会平滑地过渡,虽然Vue3中也有一个v-bind特性可以实现动态样式绑定,但经过观察以后Vue官网并没有采取这个方案,针对Vue3的v-bind特性在接下来的方案中会细说。
具体实现如下:

* 定义根作用域下的变量 */
:root {  --theme-color: #333;  --theme-background: #eee;}
/* 更改dark类名下变量的取值 */
.dark{  --theme-color: #eee;  --theme-background: #333;}
/* 更改pink类名下变量的取值 */
.pink{  --theme-color: #fff;  --theme-background: pink;}
.box {transition: all .2s;width: 100px;height: 100px;border: 1px solid #000;
/* 使用变量 */
color: var(--theme-color);
background: var(--theme-background);
}

优点:

  • 不用重新加载样式文件,在样式切换时不会有卡顿
  • 在需要切换主题的地方利用var()绑定变量即可,不存在优先级问题
  • 新增或修改主题方便灵活,仅需新增或修改CSS变量即可,在var()绑定样式变量的地方就会自动更换

缺点:

  • IE兼容性(忽略不计)
  • 首屏加载时会牺牲一些时间加载样式资源

方案四:使用VUE3的v-bind特性

虽然这种方式存在局限性只能在Vue开发中使用,但是为Vue项目开发者做动态样式更改提供了又一个不错的方案。

<script setup>
// 这里可以是原始对象值,也可以是ref()或reactive()包裹的值,根据具体需求而定
const theme = {    color: 'red'  }
</script>
<template>
<p>hello</p>
</template>
<style scoped>
p {
  color: v-bind('theme.color');
}
</style>

Vue3中在style样式通过v-bind()绑定变量的原理其实就是给元素绑定CSS变量,在绑定的数据更新时调用CSSStyleDeclaration.setProperty更新CSS变量值。
前面方案3基于CSS变量绑定样式是在:root上定义变量,然后在各个地方都可以获取到根元素上定义的变量。现在的方案我们需要考虑的问题是,如果是基于JS层面如何在各个组件上优雅地使用统一的样式变量?
我们可以利用Vuex或Pinia对全局样式变量做统一管理,如果不想使用类似的插件也可以自行封装一个hook,大致如下:

// 定义暗黑主题变量
exportdefault { 
 fontSize: '16px', 
 fontColor: '#eee',
 background: '#333',};
// 定义白天主题变量
exportdefault {  
fontSize: '20px',  
fontColor: '#f90',  
background: '#eee',
};
import { shallowRef } from'vue';
// 引入主题
import theme_day from'./theme_day';
import theme_dark from'./theme_dark';
// 定义在全局的样式变量
const theme = shallowRef({});
exportfunctionuseTheme() {
// 尝试从本地读取
const localTheme = localStorage.getItem('theme'); 
theme.value = localTheme ? JSON.parse(localTheme) : theme_day;constsetDayTheme = () => {    
theme.value = theme_day;  
};
constsetDarkTheme = () => {   
 theme.value = theme_dark;
  };
return {    
theme,    
setDayTheme,    
setDarkTheme,
  };
}

使用自己封装的主题hook

<script setuplang="ts">
import { useTheme } from'./useTheme.ts';
importMyButtonfrom'./components/MyButton.vue';
const { theme } = useTheme();
</script>
<template>
<div class="box">
<span>Hello</span>
</div>
<my-button />
</template>
<style lang="scss">
.box {
width: 100px;height: 100px;
background: v-bind('theme.background');
color: v-bind('theme.fontColor');
font-size: v-bind('theme.fontSize');
}
</style>
<script setuplang="ts">
import { useTheme } from'../useTheme.ts';
const { theme, setDarkTheme, setDayTheme } = useTheme();constchange1 = () => {setDarkTheme();
};
constchange2 = () => {setDayTheme();};
</script>
<template>
<button class="my-btn" @click="change1">dark</button>
<button class="my-btn" @click="change2">day</button>
</template>
<style scopedlang="scss">
.my-btn {
color: v-bind('theme.fontColor');
background: v-bind('theme.background');
}
</style>

其实从这里可以看到,跟Vue的响应式原理一样,只要数据发生改变,Vue就会把绑定了变量的地方通通更新。

优点:

  • 不用重新加载样式文件,在样式切换时不会有卡顿

  • 在需要切换主题的地方利用v-bind绑定变量即可,不存在优先级问题

  • 新增或修改主题方便灵活,仅需新增或修改JS变量即可,在v-bind()绑定样式变量的地方就会自动更换

缺点:

  • IE兼容性(忽略不计)

  • 首屏加载时会牺牲一些时间加载样式资源

这种方式只要是在组件上绑定了动态样式的地方都会有对应的编译成哈希化的CSS变量,而不像方案3统一地就在:root上设置(不确定在达到一定量级以后的性能),也可能正是如此,Vue官方也并未采用此方式做全站的主题切换

方案五:mixin+scss+类名切换

主要是运用SCSS的混合+CSS类名切换,其原理主要是将使用到mixin混合的地方编译为固定的CSS以后,再通过类名切换去做样式的覆盖,实现方案如下:

定义SCSS变量:

/* 字体定义规范 */
$font_samll:12Px;$font_medium_s:14Px;$font_medium:16Px;$font_large:18Px;
/* 背景颜色规范(主要) */
$background-color-theme: #d43c33;
//背景主题颜色默认(网易红)
$background-color-theme1: #42b983;
//背景主题颜色1(QQ绿)
$background-color-theme2: #333;
//背景主题颜色2(夜间模式)
/* 背景颜色规范(次要) */
$background-color-sub-theme: #f5f5f5;
//背景主题颜色默认(网易红)
$background-color-sub-theme1: #f5f5f5;
//背景主题颜色1(QQ绿)
$background-color-sub-theme2: #444;
//背景主题颜色2(夜间模式)
/* 字体颜色规范(默认) */
$font-color-theme : #666;
//字体主题颜色默认(网易)
$font-color-theme1 : #666;
//字体主题颜色1(QQ)
$font-color-theme2 : #ddd;
//字体主题颜色2(夜间模式)
/* 字体颜色规范(激活) */
$font-active-color-theme : #d43c33;
//字体主题颜色默认(网易红)
$font-active-color-theme1 : #42b983;
//字体主题颜色1(QQ绿)
$font-active-color-theme2 : #ffcc33;
//字体主题颜色2(夜间模式)
/* 边框颜色 */
$border-color-theme : #d43c33;
//边框主题颜色默认(网易)
$border-color-theme1 : #42b983;
//边框主题颜色1(QQ)
$border-color-theme2 : #ffcc33;
//边框主题颜色2(夜间模式)
/* 字体图标颜色 */
$icon-color-theme : #ffffff;
//边框主题颜色默认(网易)
$icon-color-theme1 : #ffffff;
//边框主题颜色1(QQ)
$icon-color-theme2 : #ffcc2f;
//边框主题颜色2(夜间模式)
$icon-theme : #d43c33;
//边框主题颜色默认(网易)
$icon-theme1 : #42b983;
//边框主题颜色1(QQ)
$icon-theme2 : #ffcc2f;
//边框主题颜色2(夜间模式)


定义混合mixin:
```vue
@import"./variable.scss";
@mixin bg_color(){
background: $background-color-theme;  
[data-theme=theme1] & {
background: $background-color-theme1;  
}  
[data-theme=theme2] & {
background: $background-color-theme2;
  }}
@mixin bg_sub_color(){
background: $background-color-sub-theme;  
[data-theme=theme1] & {background: $background-color-sub-theme1;  
}  
[data-theme=theme2] & {
background: $background-color-sub-theme2;  
}}
@mixin font_color(){color: $font-color-theme;  
[data-theme=theme1] & {color: $font-color-theme1;  
}  
[data-theme=theme2] & {
color: $font-color-theme2;  
}}
@mixin font_active_color(){
color: $font-active-color-theme;  
[data-theme=theme1] & {
color: $font-active-color-theme1;  
}  
[data-theme=theme2] & {
color: $font-active-color-theme2;  
}}
@mixin icon_color(){
color: $icon-color-theme;    
[data-theme=theme1] & {
color: $icon-color-theme1;    
}    
[data-theme=theme2] & {
color: $icon-color-theme2;    
}}
@mixin border_color(){
border-color: $border-color-theme;  
[data-theme=theme1] & {
border-color: $border-color-theme1;  
}  
[data-theme=theme2] & {border-color: $border-color-theme2;  
}}

<template>
<div class="header" @click="changeTheme">
<div class="header-left">
<slot name="left">左边</slot>
</div>
<slot name="center"class="">中间</slot>
<div class="header-right">
<slot name="right">右边</slot>
</div>
</div>
</template>
<script>
exportdefault {    
name: 'Header',    
methods: {      
changeTheme () {
document.documentElement.setAttribute('data-theme', 'theme1')      
}    
}  
}</script>
<style scopedlang="scss">
@import"../assets/css/variable";
@import"../assets/css/mixin";
.header{width: 100%;height: 100px;
font-size: $font_medium;
@include bg_color();
}
</style>

这种方案最后得到的结果与方案2类似,只是在定义主题时由于是直接操作的SCSS变量,会更加灵活。

优点:

  • 不用重新加载样式文件,在样式切换时不会有卡顿

  • 在需要切换主题的地方利用mixin混合绑定变量即可,不存在优先级问题

  • 新增或修改主题方便灵活,仅需新增或修改SCSS变量即可,经过编译后会将所有主题全部编译出来

缺点:

  • 首屏加载时会牺牲一些时间加载样式资源

方案六:CSS变量+动态setProperty

此方案较于前几种会更加灵活,不过视情况而定,这个方案适用于由用户根据颜色面板自行设定各种颜色主题,这种是主题颜色不确定的情况,而前几种方案更适用于定义预设的几种主题。
方案参考:vue-element-plus-admin
主要实现思路如下:
只需在全局中设置好预设的全局CSS变量样式,无需单独为每一个主题类名下重新设定CSS变量值,因为主题是由用户动态决定。

:root {  --theme-color: #333;  --theme-background: #eee;}

定义一个工具类方法,用于修改指定的CSS变量值,调用的是CSSStyleDeclaration.setProperty

exportconstsetCssVar = (prop: string, val: any, dom = document.documentElement) => {  dom.style.setProperty(prop, val)}

在样式发生改变时调用此方法即可

setCssVar('--theme-color', color)

优点:

  • 不用重新加载样式文件,在样式切换时不会有卡顿

  • 仔细琢磨可以发现其原理跟方案4利用Vue3的新特性v-bind是一致的,只不过此方案只在:root上动态更改CSS变量而Vue3中会将CSS变量绑定到任何依赖该变量的节点上。

  • 需要切换主题的地方只用在:root上动态更改CSS变量值即可,不存在优先级问题

  • 新增或修改主题方便灵活

缺点:

  • IE兼容性(忽略不计)

  • 首屏加载时会牺牲一些时间加载样式资源(相对于前几种预设好的主题,这种方式的样式定义在首屏加载基本可以忽略不计)

方案七:利用prefers-color-schemes特性

prefers-color-scheme是CSS 媒体特性【@media】用于检测用户是否有将操作系统的主题色设置为亮色【light】或者暗色【dark】,这俩个也是prefers-color-scheme重要属性。W3C 在 2020 年 7 月 31日首次提到css的prefers-color-scheme新特性。
当前prefers-color-scheme新特性支持各大主流电脑【window和IOS系统,Linux系统可以用第三方工具】端浏览器谷歌、火狐等,包括手机端的安卓和苹果,足以说明prefers-color-scheme属性已经稳定成熟,可以用于生产环境了。
如何正确使用prefers-color-scheme属性呢?答:需要在全局css文件内部写入下面代码即可,用于监听系统主题变化结果:

:root {
    --color-background: #1b1b1b;
    --white-color-background: #fff;
    color-scheme: light dark;
}
/* 监听操作系统主题模式 */
@media (prefers-color-scheme: dark) {
    body {
        background-color: var(--color-background);
    }
}

@media (prefers-color-scheme: light) {
    body {
        background-color: var(--white-color-background);
    }
} 

当操作系统电脑端或者手机操作系统主题颜色变化时就能监听到主题,然后就会按照你预期设置的颜色进行渲染主题色。比如这里的body暗色主题是background-color: var(--color-background),亮色主题是background-color: var(--white-color-background);
提示::root表示根元素,拥有更高的优先级,这里可以设置全局样式变量,通过css的var方法来获取对应变量且获得相应的样式。
那么除了媒体监听主题变化,能不能自定义主题呢?答:能,可以通过js来操作操作,实现自由切换主题颜色,具体细节请往下看。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>系统的主题色设置为亮色或者暗色</title>
    <style lang="css">
        /* 整个页面配置为使用用户的配色方案首选项 */
        /* 根元素,优先级最高,与html选择器相同 */
        :root {
            --color-background: #1b1b1b;
            --color-border: #cacfd5;
            --color-text-default: #0b1016;
            --color-base: #f4f5f6;
            --color-accent: #ba0d37;
            --white-color-background: #fff;
            color-scheme: light dark;
        }
        * {
            margin: 0;
            padding: 0;
        }
        body {
            text-align: center;
            height: 100vh;
        }
        .light-scheme {
            background: var(--white-color-background);
            color: var(--color-text-default);
        }
        .dark-scheme {
            background: var(--color-background);
            color: white;
        }
        h2 {
            margin: 50px auto;
            color: var(--color-accent)
        }

        /* 监听操作系统主题模式 */
        @media (prefers-color-scheme: dark) {
            body {
                background-color: var(--color-background);
            }
        }
        @media (prefers-color-scheme: light) {
            body {
                background-color: var(--white-color-background);
            }
        }
        .tab-type {
            display: flex;
            justify-content: center;
            padding-top: 30px;
        }
        .tab-type > li {
            cursor: pointer;
            color: #fff;
            background-color: darkred;
            border-radius: 12px;
            padding: 5px;
            margin: 0px 20px;
            max-width: 300px;
        }
</style>
</head>

<body>
    <div class="content" id="content">
        <ul class="tab-type">
            <li id="light">浅色主题</li>
            <li id="dark">暗色主题</li>
        </ul>
        <h2>微信公众号:[布依前端],布衣前端,专注于前端知识分享的up</h2>
        <div class="scheme-tip"></div>
    </div>
    <script>
        // 手动修改主题颜色
        const light = document.getElementById('light')
        const dark = document.getElementById('dark')
        const content = document.getElementsByTagName('body')[0]
        const tipText = document.getElementsByClassName('scheme-tip')[0]
        let lightTip = '当前自定义主题:light亮色', darkTip = '当前自定义主题:dark暗色'

        light.onclick = function (params) {
            content.setAttribute('class', 'light-scheme')
            tipText.innerHTML = lightTip
        }
        dark.onclick = function (params) {
            content.setAttribute('class', 'dark-scheme')
            tipText.innerHTML = darkTip
        }

        // js 监听系统主题模式
        const scheme = window.matchMedia('(prefers-color-scheme: dark)')
        if (scheme.matches) {
            // 深色模式业务处理代码
            console.log('深色模式');
            tipText.innerHTML = darkTip
        } else {
            // 浅色模式业务处理代码
            console.log('浅色模式');
            tipText.innerHTML = lightTip
        }
</script>
</body>
</html> 

当网站不满足系统默认主题,还想提供用户自己切换主题的功能,代码中有两个按钮,【浅色主题】和【暗色主题】就是干这件事情的。用户可以随意切换,具体切换代码可以到js部分查看。

除了css媒体能监听操作系统主题变化,js也能监听的,当监听到后可以增强js业务逻辑,比如设置某个主题下布局变化、语音播报当前模式等等和其他业务处理逻辑,需要设置页面加载后添加如下代码即可:

// js 监听系统主题模式
const scheme = window.matchMedia('(prefers-color-scheme: dark)')
if (scheme.matches) {
    // 深色模式业务处理代码
    console.log('深色模式');
    tipText.innerHTML = darkTip
} else {
    // 浅色模式业务处理代码
    console.log('浅色模式');
    tipText.innerHTML = lightTip
} 

总结prefers-color-scheme监听方式:

css里通过@media监听

js里面通过matchMedia监听

两种方式都能监听到操作系统主题变化后的值.

标签:网页,color,主题,样式,theme,切换,background,--
From: https://www.cnblogs.com/Cxymds/p/17447993.html

相关文章

  • linux 性能自我学习 ———— cpu 切换带来的性能损耗 [二]
    前言我们知道现在操作系统,都是多进程和多线程,那么会有一个操作系统帮助我们去切换进程和线程,这个是要消耗cpu资源的,那么就来了解一下cpu资源消耗情况。正文一般是下面几个场景切换:进程上下文切换线程上下文切换中断上下文切换在了解进程切换的时候,需要了解另外一个......
  • 一个有趣的问题调查,网页上的代码块全部变成 [object Object]
    问题如图,网页上的代码全部显示成了[objectObject],而且与特定网站无关,大部分网站都会有问题。调查无痕模式打开,换个浏览器打开,没有问题,看来是有插件或者油猴脚本捣乱了。调试跟踪HTML元素变更,前几次都是网站自己的js变更,后面出现了一个可疑的插件拿到插件ID之后,看......
  • 网页接入硬件交互通信方案
    我们都知道,网页直接接入硬件并与硬件交互并不那么方便,如果硬件由网络功能还可以通过网络通信实现(一般是socket),但大多数情况下硬件还是直接通过usb/串口等硬接线接入。很早以前有一种解决方案是Active-X,但只支持IE浏览器,尤其现在2023年IE已经并入Edge浏览器,这一方案已经过时。那么......
  • 界面控件DevExpress ASP.NET新主题——Office 365暗黑主题的应用
    DevExpressASP.NET WebFormsControls拥有针对Web表单(包括报表)的110+种UI控件,DevExpressASP.NETMVCExtensions是服务器端MVC扩展或客户端控件,由轻量级JavaScript小部件提供支持的70+个高性能DevExpressASP.NETCoreControls,包含功能完善的报表平台。在之前发布的v22.1版......
  • 高性能 Go 的 6 个技巧 — Go 高级主题
    本文旨在讨论6个提示,这些提示可以帮助诊断和修复Go应用程序中的性能问题。基准测试:在Go中编写有效的基准测试对于了解代码性能至关重要。可以通过将文件命名为“_test.go”,并使用testing包的Benchmark函数来创建基准测试。以下是一个示例:funcfibonacci(nint)int{ ifn<=......
  • 网页常见的渗透方法以及解决方法
    方式一:弱口令弱口令(weakpassword)没有严格和准确的定义,通常认为容易被别人(他们有可能对你很了解)猜测到或被破解工具破解的口令均为弱口令。弱口令指的是仅包含简单数字和字母的口令,例如“123456”、“abc”等,因为这样的口令很容易被别人破解,从而使用户的计算机面临风险,因此不......
  • MobTech MobLink|小程序、网页跳转App的原理
    从不同的渠道,如小程序、二维码、网页等,直接跳转到App内对应的页面,并传递相关的参数信息,已经由移动还原解决方案MobLink实现了。本文将详细介绍多样化跳转技术的原理。MobLink的功能架构MobLink的功能架构如下图所示:从图中可以看出,MobLink主要涉及三个部分:客户端、服务器和网页。客......
  • 2023-05-30 taro如何切换到其他已发布的小程序
    taro可以把一套小程序代码发布成多个小程序,那么要如何维护这些小程序呢,咱也不懂,咱也是刚开始学习怎么操作。开始前先感谢chatGpt这个工具,它真的很棒,几乎没有什么是它不会的,我的很多一些问题都是问chatGpt,就比如这篇文章都是chatGpt给我的。要将Taro项目切换到已经发布的小程序,需......
  • Typora - typora主题样式
      主题名称:github_harley.css(随便命名)效果:Mac风格的代码块、更舒适的引用块风格。css查看代码/*代码块主题*//*顶部*/.md-fences{/*color:#c5c8c6;*/background-color:#21252b;border-radius:5px;/*box-shadow:010px30px0rgb(0......
  • el-cascader 切换数据源时报错
    问题根据属性类型加载el-cascader不同的数据源选项,会报错如下:尝试猜测是:el-cascader上次的绑定值没有清空,而切换属性类型导致数据源选项不同,以致于无法在新的选项中找到绑定值,从而报错。于是,设置了切换属性类型时,把el-cascader的绑定值重置为[]。但仍然存在这个报错。......