移动端H5暗黑模式

记录一次暗黑模式实现

移动端H5暗黑模式体验直通车

核心点

css 变量

1body{
2  --bg:#fff;
3  --color:#333;
4}

css变量前需加两根连词线(--),变量名大小写敏感,--header-color--Header-Color是两个不同变量。

css 变量的使用

1a {
2   --font-size:14px;
3   font-size:var(--font-size)
4}

var()函数用来读取变量

1p {
2   colorvar(--foo, #7F583F);
3}

var()函数还可以使用第二个参数,表示变量的默认值。如果该变量不存在,就会使用这个默认值。

css 变量的作用域

1a {
2   --font-size:14px;
3   font-size:var(----font-size)
4}

--font-size 变量 只在 这个a { }里面有效,变量的作用域就是它所在的选择器的有效范围。全局的变量通常放在根元素:root里面,确保任何选择器都可以读取它们

1:root {
2  --bg#fff;
3  --color:#333;
4}

js 如何动态控制 css 变量

设置css变量:document.documentElement.style.setProperty(css变量名,css变量值)

移除css变量:document.documentElement.style.removeProperty(css变量名)

暗黑模式实现思路

以下是我的实现暗黑模式的思路:

  1<!DOCTYPE html>
2<html lang="en">
3<head>
4    <meta charset="UTF-8">
5    <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, user-scalable=no">
6    <title>darkMode</title>
7</head>
8<style>
9
10*{
11    margin: 0;
12    padding: 0;
13}
14@media (prefers-color-scheme: dark) {
15    :root{
16        --bg:#191919; /* 主体背景色*/
17        --bg-search:#232323;/* 搜索框背景色*/
18        --bg-card:#333333;/*卡片背景色*/
19
20        --color:#848484;/*主体字体颜色*/
21        --color-search:#696969;/*搜索框字体颜色*/
22        --color-tip:#536DF7;/*提示作用字体颜色 */
23        --color-white:#FFFFFF;
24        --color-details:#F1F1F1;
25        --color5:#2B3F68;
26        --color7:#888888;
27
28        --border-color:#393939;/*边框颜色*/
29    }
30}
31.container{
32    width: 100vw;
33    height: 100vh;
34    background-color: var(--bg,#F7F6FA);
35}
36ul{
37    list-style: none;
38    background-color: var(--bg,transparent);
39}
40ul li{
41    width: calc(100% - 20px);
42    height: 130px;
43    margin: 10px auto;
44    /* border-style:solid ;
45    border-width: 1px;
46    border-color:var(--border-color,#fff); */
47    background-color: var(--bg-card,#FFFFFF);
48    border-radius: 8px;
49    display: flex;
50    flex-direction: column;
51}
52.item-top{
53    box-sizing: border-box;
54    padding: 10px;
55    flex: 1;
56    display: flex;
57    flex-direction: column;
58    justify-content: space-around;
59    border-bottom-style:solid ;
60    border-bottom-width:1px ;
61    border-bottom-color:var(--border-color,#EAEAEA);
62}
63.title{
64    font-size: 16px;
65    font-weight: bold;
66    color:var(--color,#212121);
67}
68.type,.type-name{
69    font-size: 14px;
70}
71.type{
72    font-size: 13px;
73    color:var(--color-tip,#878787);
74
75}
76.type-name{
77    color:var(--color-details,#212121);
78    font-size: 12px;
79}
80.time{
81    height: 42px;
82    box-sizing: border-box;
83    padding: 10px;
84    font-size: 14px;
85    color:var(--color,#333);
86}
87.search-container{
88    height: 40px;
89    /* background-color: var(--bg-search,#F7F6FA); */
90    box-sizing: border-box;
91    padding: 3px 10px;
92}
93.search-input{
94    outline: none;
95    border: none;
96    border-radius: 3px;
97    height: 34px;
98    width: 100%;
99    background-color: var(--bg-search,#ffffff);
100    color: var(--color-search,#808080);
101    text-align: center;
102}
103
</style>
104<body>
105    <div class="container">
106        <button id="btn">改变</button>
107        <div class="search-container">
108            <input type="text" class="search-input" placeholder="搜索">
109        </div>
110        <ul>
111            <li>
112                <div class="item-top">
113                    <p class="title">携入工单批量发布异常情况说明</p>
114                    <p><span class="type">类型:</span><span class="type-name">未分类</span></p>
115                </div>
116                <p class="time">2019/02/09  18:00:56</p>
117            </li>
118            <li>
119                <div class="item-top">
120                    <p class="title">携入工单批量发布异常情况说明</p>
121                    <p><span class="type">类型:</span><span class="type-name">未分类</span></p>
122                </div>
123                <p class="time">2019/02/09  18:00:56</p>
124            </li>
125            <li>
126                <div class="item-top">
127                    <p class="title">携入工单批量发布异常情况说明</p>
128                    <p><span class="type">类型:</span><span class="type-name">未分类</span></p>
129                </div>
130                <p class="time">2019/02/09  18:00:56</p>
131            </li>
132            <li>
133                <div class="item-top">
134                    <p class="title">携入工单批量发布异常情况说明</p>
135                    <p><span class="type">类型:</span><span class="type-name">未分类</span></p>
136                </div>
137                <p class="time">2019/02/09  18:00:56</p>
138            </li>
139        </ul>
140
141    </div>
142
143</body>
144
145<script>
146
147(function (name, definition{
148    if (typeof define === 'function') {
149    // AMD环境或CMD环境
150    define(definition);
151    } else if (typeof module !== 'undefined' && module.exports) {
152    // 定义为普通Node模块
153    module.exports = definition();
154    } else {
155    // 将模块的执行结果挂在window变量中,在浏览器中this指向window对象
156    this[name] = definition();
157    }
158})('RookieFlyDarkMode'function ({
159
160    function RookieFlyDarkMode(opt){
161        this.init(opt)
162    }
163    RookieFlyDarkMode.prototype.init = function(opt){
164        this.opt = RookieFlyDarkMode.extend({},opt)
165    }
166    RookieFlyDarkMode.extend = function(defaultOptions,userOptions){
167        if(!userOptions) return defaultOptions
168        for (let i in userOptions) {
169            defaultOptions[i] = defaultOptions[i] && defaultOptions[i].toString() === "[object Object]" ? RookieFlyDarkMode.extend(defaultOptions[i], userOptions[i]) : defaultOptions[i] = userOptions[i]
170        }
171        return defaultOptions
172    }
173    RookieFlyDarkMode.prototype.setCssVar = function(name){
174        let s = document.documentElement.style
175        let css = this.opt[name]
176        for(let k in css){
177            if(typeof css[k] === 'object'){
178                for(let key in css[k]){
179                    s.setProperty(key,css[k][key]);
180                }
181            }else{
182                s.setProperty(k,css[k]);
183            } 
184        }
185    }
186    RookieFlyDarkMode.prototype.removeCssVar = function(name){
187        let s = document.documentElement.style
188        let css = this.opt[name]
189        for(let k in css){
190            if(typeof css[k] === 'object'){
191                for(let key in css[k]){
192                    s.removeProperty(key);
193                }
194            }else{
195                s.removeProperty(k);
196            }
197        }
198    }
199    return RookieFlyDarkMode
200})
201
202
203    //暗黑模式
204    var  darkMode = {
205        //定义暗黑模式 背景色
206        bg:{
207
208            "--bg":"#191919"// 主体背景色
209            "--bg-search":"#232323",// 搜索框背景色
210            "--bg-card":"#333333",// 卡片背景色
211        },
212        //定义暗黑模式 字体颜色
213        color:{
214            "--color":"#848484",//主体字体颜色
215            "--color-search":"#696969",//搜索框字体颜色
216            "--color-tip":"#536DF7",//提示作用字体颜色 
217            "--color-title":"#536DF7",
218            "--color-details":"#F1F1F1",
219            "--color5":"#2B3F68",
220            "--color-white":"#FFFFFF",
221            "--color7":"#888888",
222        },
223        border:{
224            "--border-color":"#393939"
225        }
226    }
227
228    let RookieFlyDarker = new RookieFlyDarkMode({dark:darkMode})
229
230    let falg = true
231    document.querySelector('#btn').addEventListener('click',function(){
232        RookieFlyDarker[falg?'setCssVar':'removeCssVar']('dark')
233        falg = !falg
234    })
235
236    //检测用户设备是否支持 媒体查询的暗黑模式  如果不支持
237    if(!window.matchMedia("(prefers-color-scheme:dark)").matches){
238
239        //检测用户是否选择了模式  我们可以放在个人中心的一个开关 是否开启暗黑模式  默认关闭
240
241        //第一步:首先检测 本地缓存 里 是否有 darkMode 字段,
242        //没有则说明是 用户首次进入应用,我们 给个弹出层提示,“检测你的设备不支持暗黑模式,是否手动开启”,
243        //点 开启 直接执行js开启暗黑,并储存一个 darkMode 字段在本地localStorage,值为  “dark”
244        //点 不开启 则也在本地缓存里存储一个字段 darkMode,值为 “light”
245
246
247
248        //第二步:用户不是第一次进入应用时,我们也会检测本地是否存储有 darkMode 字段,
249        //有该 字段后,我们就不会给用户弹出提示 “检测你的设备不支持暗黑模式,是否手动开启”,
250        //然后我们取得其值,如果 值为 “light”,个人中心 是否开启暗黑模式 开启为 关闭状态
251        //如果 值为 “cark”,个人中心 是否开启暗黑模式 开启为 开启状态,并且我们js执行手动开启暗黑模式
252
253        // 第三步:当然 是否开启也可以由接口控制
254    }
255
</script>
256</html>

为此,我们需要考虑的几个点有:
1. 正常情况下,媒体查询prefers-color-scheme:dark生效,我们不需要做任何操作
2. 如果当前设备不支持媒体查询prefers-color-scheme:dark时,我们就需要用js代码去检测出当前设备是否不支持
2.1 检测用户是否选择了模式 我们可以放在个人中心的一个开关 是否开启暗黑模式 默认关闭,先检测 本地缓存 里 是否有 darkMode 字段,没有则说明是 用户首次进入应用,我们 给个弹出层提示,“检测你的设备不支持暗黑模式,是否手动开启”,点 开启 直接执行js开启暗黑,并储存一个 darkMode 字段在本地localStorage,值为 “dark”,点 不开启 则也在本地缓存里存储一个字段 darkMode,值为 “light”;
2.2 用户不是第一次进入应用时,我们也会检测本地是否存储有 darkMode 字段,有该 字段后,我们就不会给用户弹出提示 “检测你的设备不支持暗黑模式,是否手动开启”,然后我们取得其值,如果 值为 “light”,个人中心 是否开启暗黑模式 开启为 关闭状态,如果 值为 “cark”,个人中心 是否开启暗黑模式 开启为 开启状态,并且我们js执行手动开启暗黑模式