开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第15天,点击查看活动详情
主题和模式的定义
想要实现网站换肤的功能,有很多的方案可以选择,但其核心逻辑是不变的,我觉得在实现之前先明确主题和模式概念比较好:
模式一般情况下有两种,高亮模式和暗黑模式,主要受影响的页面属性是背景颜色、字体颜色; 主题则是网站的主题色、辅助色这类的配色方案,主要用在突出显示的内容,如激活的菜单、按钮颜色等。
线上预览
页面设计
demo的页面设计以常见的官网为原型,页面效果如下:
分为三个部分:
- 通栏菜单,包含模式切换
- 大图banner
- 页面主体,也分两部分:第一块是主题色背景内,嵌套标题以及跟随跟随背景色的小块;第二部分是左文右图;这样的页面结构非常常见,比如掘金插件宣传页:
图片备注
页面代码如下:
<template>
<!-- 导航菜单 -->
<header>
<a href="javascript:;" :class="{'theme-bg': active === index}" v-for="item, index in menu" :key="item" @click="active = index">{{item}}</a>
高亮模式<input type="radio" value="light" name="mode" />
暗黑模式<input type="radio" value="dart" name="mode"/>
</header>
<!-- 网站主题内容案例 -->
<main>
<img class="banner" src="https://vkceyugu.cdn.bspapp.com/VKCEYUGU-0511edfa-4fd3-481d-8712-a5c2b826268d/ce9a993a-40bb-4dd2-94a7-68173cc1e7e0.jpeg"/>
<section>
<div class="theme-bg content-1">
<h3>标题标题标题标题</h3>
<div class="content-1-list">
<div class="mode-bg">内容块</div>
<div class="mode-bg">内容块</div>
<div class="mode-bg">内容块</div>
</div>
</div>
<div class="content-2">
<h3 class="theme-text">标题标题标题标题</h3>
<div class="content-2-block theme-bg">内容块</div>
</div>
</section>
</main>
</template>
<script setup lang="ts">
const menu = ['首页', '文章', '代码', '资源']
const active = ref(0)
</script>
<style lang="scss">
// 页面布局
header {
text-align: right;
height: 50px;
line-height: 50px;
padding: 0 15px;
a {
padding: 0 10px;
display: inline-block;
height: 100%;
text-decoration: none;
}
}
.banner {
width: 100%;
height: 40vh;
object-fit: cover;
margin-bottom: 10px;
}
.theme-block {
display: inline-block;
width: 30px;
height: 30px;
line-height: 30px;
text-align: center;
border-radius: 5px;
margin: 10px;
position: relative;
&.active::before {
content: '√';
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
}
}
h2 {
padding: 0 15px;
}
section {
padding: 0 10px;
.content-1 {
padding: 15px;
text-align: center;
.content-1-list {
display: flex;
justify-content: space-between;
padding-top: 15px;
&>div {
height: 100px;
width: 25%;
border-radius: 4px;
text-align: center;
line-height: 100px;
}
}
}
.content-2 {
padding: 30px;
display: flex;
justify-content: center;
align-items: cneter;
text-align: center;
h3 {
flex: 1;
}
.content-2-block {
margin-left: 50px;
height: 100px;
flex: 1;
display: flex;
justify-content: center;
align-items: cneter;
line-height: 100px;
}
}
}
</style>
变量预设
我们需要先确定模式和主题的颜色,我这里选用颜色值为
const light = '#fefefe' // 高亮模式背景色
const dart = '#302D42' // 暗黑模式背景色
// 不同模式下,字体颜色和背景颜色相反
// 页面主题色
const themeList = ['#90E39A','#7D5BA6','#A49966','#CE4760']
为了后续的开发更方便兼容不同的模式和主题,我们要做两件事:
- 定义主题和模式相关的css变量 如果模式和主题还涉及到边框、阴影等,继续加变量即可
:root {
--theme-color: #3497FC; // 主题颜色
--mode-bg: #fefefe; // 模式背景颜色
--mode-color: #302D42; // 模式的字体颜色
}
-
修改初始化css样式 开发项目时为了不同浏览器中样式统一,通常会重写网页的预设样式,我们在此基础上去增加模式和主题相关的预设样式
其中最重要的预设是设置body的背景颜色和字体颜色,后续开发尽量不要单独设置元素的这两个属性,让元素继承body的颜色。
body {
margin: 0;
padding: 0;
background-color: var(--mode-bg);
color: var(--mode-color);
// a标签模式是丑陋的蓝色,这里将它设置为跟随模式的字体颜色,也就是黑/白
a {
color: var(--mode-color);
}
}
- 定义通用的主题、模式class
// 定义一系列关于主题和模式的样式,用到背景颜色和主题色的统一使用这几个类
.theme-text {
color: var(--theme-color);
}
.theme-bg {
background-color: var(--theme-color);
}
.mode-bg {
background-color: var(--mode-bg);
}
.mode-text {
color: var(--mode-color);
}
切换模式
切换模式要做的事情有两件:
- 修改对应的css变量
- 把模式的值存储到本地
// 设置模式
function setMode(_mode) {
switch (_mode) {
case "dart":
document.body.style.setProperty("--mode-bg", dart);
document.body.style.setProperty("--mode-color", light);
break;
case "light":
document.body.style.setProperty("--mode-bg", light);
document.body.style.setProperty("--mode-color", dart);
break;
}
// 保存到本地以及赋值给变量
localStorage.setItem('pages-mode', _mode)
mode.value = _mode
}
// 获取模式值
function getMode() {
return localStorage.getItem('pages-mode') || 'light'
}
// 进入页面先获取本地存储的模式,并进行修改
setMode(getMode())
切换主题
切换主题和切换模式类似,只是修改的css变量不同。
// 设置主题色
function setTheme(_theme) {
document.body.style.setProperty("--theme-color", _theme);
theme.value = _theme
localStorage.setItem('pages-theme', _theme)
}
// 设置主题色
function getTheme(_theme) {
return localStorage.getItem('pages-theme') || themeList[0]
}
// 进入页面先获取本地存储的主题色,并进行修改
setTheme(getTheme())
模式跟随系统
模式的切换我们可以做到更智能一点:跟随用户系统的模式设置。
通过 window.matchMedia 的api可以判断用户当前系统是否设置了暗黑模式,并且监听到系统模式的改变:
const media = window.matchMedia('(prefers-color-scheme: dark)')
function changeMedia() {
setMode(media.matches ? 'dark' : 'light')
}
media.addEventListener('change', changeMedia)
// 进入页面时跟随系统模式
// changeMedia()
当然,手动修改模式和自动跟随系统模式是冲突的,一般还要增加一个开关设置:是否跟随系统模式。