<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          深色模式適配指南

          共 8424字,需瀏覽 17分鐘

           ·

          2020-08-18 20:26

          本文首發(fā)于政采云前端團(tuán)隊(duì)博客:深色模式適配指南

          https://www.zoo.team/article/dark-theme

          背景

          隨著 iOS 13 的發(fā)布,深色模式(Dark Mode)越來越多地出現(xiàn)在大眾的視野中,支持深色模式已經(jīng)成為現(xiàn)代移動(dòng)應(yīng)用和網(wǎng)站的一個(gè)潮流,前段時(shí)間更是因?yàn)槲⑿诺倪m配再度引起熱議。深色模式不僅可以大幅減少電量的消耗,減弱強(qiáng)光對(duì)比,還能提供更好的可視性和沉浸感。

          那針對(duì)一款 App 應(yīng)用(原生 + H5)怎么進(jìn)行深色模式的適配呢?今天就讓我們一起來探究吧!

          系統(tǒng)兼容

          想要實(shí)現(xiàn)深色模式的效果,前提條件是要系統(tǒng)支持,目前常見系統(tǒng)支持情況如下:

          H5 深色適配

          隨著深色模式的流行,越來越多的操作系統(tǒng)、瀏覽器開始支持深色模式,現(xiàn)在可以利用 CSS 的媒體查詢方法:prefers-color-scheme (https://developer.mozilla.org/zh-CN/docs/Web/CSS/@media/prefers-color-scheme) 以及 CSS 變量 (https://developer.mozilla.org/zh-CN/docs/Web/CSS/Using_CSS_custom_properties)(CSS variables、CSS custom properties)就可以實(shí)現(xiàn)頁面主題跟隨系統(tǒng)自動(dòng)切換深淺模式。CSS 變量除了 IE,其余各大瀏覽器都支持的比較好,但 prefers-color-scheme 方法還處于 W3C 草案規(guī)范,需要對(duì)不兼容瀏覽器做向下兼容,具體瀏覽器兼容性可以查詢 ?Can I Use (https://caniuse.com/#search=prefers-color-scheme),綜合來說,高版本的主流瀏覽器都已經(jīng)支持,IE 不支持。

          可以通過以下兩種方式來實(shí)現(xiàn) Web 端的深色適配:

          一、CSS 的媒體查詢

          prefers-color-scheme (https://developer.mozilla.org/zh-CN/docs/Web/CSS/@media/prefers-color-scheme) 是一種用于檢測(cè)用戶是否有將系統(tǒng)的主題色設(shè)置為亮色或者暗色的 CSS 媒體特性。利用其設(shè)置不同主題模式下的 CSS 樣式,瀏覽器會(huì)自動(dòng)根據(jù)當(dāng)前系統(tǒng)主題加載對(duì)應(yīng)的 CSS 樣式。light 適配淺色主題,dark 適配深色主題,no-preference 表示獲取不到主題時(shí)的適配方案。

          • CSS

          @media?(prefers-color-scheme:?light)?{?
          ??.article?{??
          ????background:#fff;?
          ????color:?#000;??
          ??}?
          }?
          @media?(prefers-color-scheme:?dark)?{?
          ??.article?{??
          ????background:#000;??
          ????color:?white;??
          ??}?
          }?
          @media?(prefers-color-scheme:?no-preference)?{?
          ??.article?{??
          ????background:#fff;?
          ????color:?#000;??
          ??}?
          }?
          • link 標(biāo)簽
          "./common.css"?rel="stylesheet"?type="text/css"?/>?
          ./light-mode-theme.css"?rel="stylesheet"?type="text/css"?/>?
          ./dark-mode-theme.css"?rel="stylesheet"?type="text/css"?media="(prefers-color-scheme:?dark)"?/>?

          來看一下效果,將系統(tǒng)設(shè)置為淺色外觀:

          然后將系統(tǒng)設(shè)置為深色外觀:

          頁面已經(jīng)加載了對(duì)應(yīng)深色主題的樣式:

          二、CSS 變量 + 媒體查詢

          window.matchMedia (https://developer.mozilla.org/zh-CN/docs/Web/API/Window/matchMedia) 方法可以用來查詢指定的媒體查詢字符串解析后的結(jié)果。結(jié)合 CSS 變量和 matchMedia 的查詢結(jié)果,設(shè)置對(duì)應(yīng)的 CSS 主題顏色。該方法更靈活,可以單獨(dú)抽離主題色進(jìn)行適配。

          CSS 變量的作用域與 CSS 的"層疊"規(guī)則一致,優(yōu)先級(jí)最高的聲明生效。所以當(dāng) body 上存在 "dark" 類名時(shí),:root .dark 會(huì)生效,否則 :root 生效。

          .article?{?
          ??color:?var(--text-color,?#eee);?
          ??background:?var(--text-background,?#fff);?
          }?
          :root?{?
          ??--text-color:?#000;?
          ??--text-background:?#fff;?
          }?
          :root?.dark?{?
          ??--text-color:?#fff;?
          ??--text-background:?#000;?
          }?

          使用 matchMedia 匹配主題媒體,深色模式匹配 (prefers-color-scheme: dark) ,淺色模式匹配 (prefers-color-scheme: light)

          監(jiān)聽主題模式,深色模式時(shí)為 body 添加類名 dark,根據(jù) CSS 變量的響應(yīng)式布局特點(diǎn),自動(dòng)生效 dark 類名下的 CSS。

          const?darkMode?=?window.matchMedia?&&?window.matchMedia('(prefers-color-scheme:?dark)');?
          //?判斷是否匹配深色模式?
          if?(darkMode?&&?darkMode.matches)?{?
          ??document.body.classList.add('dark');?
          }?
          //?監(jiān)聽主題切換事件?
          darkMode?&&?darkMode.addEventListener('change',?e?=>?{?
          ??if?(e.matches)?{?
          ????document.body.classList.add('dark');?
          ??}?else?{?
          ????document.body.classList.remove('dark');??
          ??}?
          });??

          那么,針對(duì)不支持 CSS 變量的 IE 瀏覽器怎么辦呢?不做兼容性處理的話那頁面可能就是一團(tuán)糟了。所以我們需要針對(duì)不兼容的瀏覽器做一些兜底處理,這里我們可以在 webpack 等構(gòu)建工具中借助 post-css 的 postcss-css-variables (https://www.npmjs.com/package/postcss-css-variables) 插件來自動(dòng)解析 CSS 變量對(duì)應(yīng)的色值,并在原始 CSS 定義之上添加一條新的 CSS 樣式,做到對(duì)不支持 CSS 變量瀏覽器的兼容。

          用法如下:

          //?根目錄?postcss.config.js?
          module.exports?=?{?
          ??plugins:?{?
          ????"postcss-css-variables":?{?
          ??????preserve:?true,?//?保留?var()?定義?
          ??????preserveInjectedVariables:?false,?//?去除其他模塊的重復(fù)變量?
          ??????variables:?require("./page.json"),?//?CSS?變量,可以支持多個(gè)?
          ????}?
          ??}?
          };?

          項(xiàng)目實(shí)踐

          現(xiàn)在的 Web、App 項(xiàng)目大都引用第三方開源組件庫,組件庫一般會(huì)使用 Sass、Less 等 CSS 預(yù)處理器定義顏色變量作為組件的基礎(chǔ)色值,并單獨(dú)抽離為配置文件。所以,項(xiàng)目使用組件庫時(shí)可以根據(jù)修改基礎(chǔ)色值來自定義主題。那么針對(duì)項(xiàng)目的深色模式適配方案也一樣,主要分為三步:一、組件庫深淺色主題 適配;二、項(xiàng)目中深淺色的顏色適配;三、 完成 CSS 變量到頁面的注入。

          組件庫樣式、自定義樣式適配

          如果第三方組件本身支持多主題或者深色模式,可以直接按說明給組件設(shè)置對(duì)應(yīng)主題模式;如果第三方組件庫不支持的話,只能用覆蓋的方式。這里以 Less 為例進(jìn)行簡(jiǎn)單實(shí)例說明:

          修改前:

          // index.less
          @white: #fff; // 顏色預(yù)定義
          @background-color: @white;
          // 組件樣式 panel.less
          .panel-background-color {
          background-color: @background-color; // 組件中使用 less 變量定義顏色樣式
          }

          新增兩個(gè) js 或者 JSON 文件,分別定義深淺模式下的 CSS 變量,并命名為 light-theme1.js、dark-theme1.js 他們并不會(huì)影響組件的樣式,只是便于后期注入到全局 style 中。

          修改后:
          //?淺色主題文件?light-theme1.js?
          const?bgColor?=?'#fff';//?顏色預(yù)定義?
          module.exports?=?{?
          ??"--background-color":?bgColor;?
          }?
          //?深色主題文件?dark-theme1.js?
          const?bgColor?=?'#000';//?顏色預(yù)定義?
          module.exports?=?{?
          ??"--background-color":?bgColor;?
          }?
          //?組件樣式?panel.less?
          .panel-background-color?{?
          ??background-color:?var(--background-color);?//組件中顏色樣式?
          }?

          CSS 變量支持第二參數(shù),當(dāng)變量不存在或者未注冊(cè)成功時(shí),可以為其設(shè)置默認(rèn)值,優(yōu)化如下:

          //?組件樣式?panel.less?
          .panel-background-color?{?
          ??background-color:?var(--background-color, @background-color);?//?組件中顏色樣式,其中?@background-color 代表修改前組件的背景顏色變量,這里設(shè)其為默認(rèn)值,在適配不成功情況下,可以保持適配前的樣式。?
          }?

          項(xiàng)目才是真正使用組件的地方,并且項(xiàng)目本身也有很多自定義 CSS 的顏色樣式,需要做與組件庫類似的處理,結(jié)果也會(huì)得到兩個(gè) js/json 文件,分別命名為 light-theme2.js、dark-theme2.js。

          CSS 注入

          在頁面渲染前,需要把定義深淺樣式的 CSS 變量注入到頁面。

          以上兩步得到了四個(gè)文件,合并淺色樣式文件 light-theme1.js 和 light-theme2.js ?得到 light-theme.js,合并深色樣式文件dark-theme1.js 和 dark-theme2.js 得到 dark-theme.js,最后把 light-theme.js、dark-theme.js 兩個(gè)文件注入到頁面中,注入腳本如下:

          import?lightTheme?from?'./light-theme';?
          import?darkTheme?from?'./dark-theme';?
          //?創(chuàng)建一個(gè)?style?元素,用于插入?css?定義?
          const?createStyle?=?(content)?=>?{?
          ??const?style?=?document.createElement('style');??
          ??style.type?=?'text/css';?
          ??style.innerHTML?=?content;??
          ??document.getElementsByTagName("script")[0].parentNode.appendChild(style);?
          //?在?body?標(biāo)簽中定義?css?變量?
          const?createCssStyle?=?()?=>?{?
          ??const?lightThemeStr?=?Object.keys(lightTheme).map(key?=>?key?+?':'?+???????lightTheme[key]).join(';');?
          ??const?darkThemeStr?=?Object.keys(darkTheme).map(key?=>?key?+?':'?+?darkTheme[key]).join(';');?
          ??const?lightContent?=?`body{${lightThemeStr}}`;?//?淺色模式?CSS?變量定義?
          ??const?darkContent?=?`body.dark{${darkThemeStr}}`;?//?深色模式?CSS?變量定義?
          ??createStyle(lightContent);?
          ??createStyle(darkContent);?
          ??isDarkSchemePreference();?
          };?

          注入完成后,項(xiàng)目頁面中就有了 CSS 變量定義,包括淺色模式 CSS 變量定義和深色模式 CSS 變量定義,具體哪一個(gè)生效,就可以根據(jù)上面提到的兩種適配方案給 body 添加 class 來控制。默認(rèn)時(shí)淺色模式生效,添加 dark 類名時(shí),深色模式會(huì)生效。至此就實(shí)現(xiàn)了一套完整的深色模式適配方案。

          native 深色適配

          iOS

          在 iOS 系統(tǒng)中,開發(fā)者從顏色和圖片兩個(gè)方面來進(jìn)行適配,我們不需要關(guān)心切換模式后該怎么操作,因?yàn)檫@些都由系統(tǒng)幫我們實(shí)現(xiàn)。顏色的適配,需要使用系統(tǒng)提供的 API,在回調(diào)用中不同的模式下分別設(shè)置顏色,而圖片的適配,需要在 XCode 的 工具欄中 Appearances 下選擇 Any,Dark,在同一名稱資源的配置下分別添加圖片資源。當(dāng)切換深色模式時(shí),系統(tǒng)會(huì)根據(jù)適配的顏色和圖片資源進(jìn)行查找和自動(dòng)切換對(duì)應(yīng)模式下的顏色和資源文件。

          Android

          安卓在 Android 10(API 級(jí)別 29)及更高版本中提供深色主題背景,可以通過以下三種方法啟用深色主題背景:

          • 使用系統(tǒng)設(shè)置(Settings -> Display -> Theme)啟用深色主題背景
          • 使用"快捷設(shè)置"圖塊,從通知托盤中切換主題背景(啟用后)
          • 在 Pixel 設(shè)備上,選擇"省電模式"將同時(shí)啟用深色主題背景,其他原始設(shè)備制造商 (OEM) 不一定支持這種行為

          在應(yīng)用中支持深色主題背景

          如要支持深色主題背景,必須將應(yīng)用的主題背景(通常可在 res/values/styles.xml 中找到)設(shè)置為繼承 DayNight 主題背景:

          <style?name="AppTheme"?parent="Theme.AppCompat.DayNight">?

          還可以使用 MaterialComponent (https://material.io/develop/android/theming/dark) 的深色主題背景:

          <style?name="AppTheme"?parent="Theme.MaterialComponents.DayNight">?

          這會(huì)將應(yīng)用的主要主題背景與系統(tǒng)控制的夜間模式標(biāo)記相關(guān)聯(lián),并將應(yīng)用的默認(rèn)主題背景設(shè)置為深色主題背景(如果已啟用)。

          主題背景和樣式

          主題背景和樣式應(yīng)避免使用旨在于淺色主題背景下使用的硬編碼顏色或圖標(biāo),您應(yīng)改用主題背景屬性(首選)或適合在夜間使用的資源,以下是需要了解的兩個(gè)最重要的主題背景屬性:

          • ?android:attr/textColorPrimary 這是一種通用型文本顏色,它在淺色主題背景下接近于黑色,在深色主題背景下接近于白色,該顏色包含一個(gè)停用狀態(tài)。
          • ?attr/colorControlNormal 一種通用圖標(biāo)顏色,該顏色包含一個(gè)停用狀態(tài)。

          Flutter

          這里以 Flutter 為例,簡(jiǎn)單介紹下跨平臺(tái)開發(fā)框架如何適配深色模式。Flutter 定義主題有兩種方式:全局主題或使用 Theme 來定義應(yīng)用程序局部的顏色和字體樣式。

          全局主題

          全局主題就是由應(yīng)用程序根 MaterialAPP 創(chuàng)建的 Theme。為了在整個(gè)應(yīng)用程序中共享包含顏色和字體樣式的主題,我們可以提供 ThemeData 給 Material 的構(gòu)造函數(shù)。Theme 指定的是淺色模式,darkTheme 指定的是深色模式,程序會(huì)根據(jù)系統(tǒng)設(shè)定的暗黑模式自動(dòng)匹配模式。

          new?MaterialApp(?
          ??title:?title,?
          ??theme:?new?ThemeData(?
          ?????brightness:?Brightness.light,?
          ?????primaryColor:?Colors.lightBlue[800],?
          ?????accentColor:?Colors.cyan[600]?,?
          ??),?
          ??darkTheme:?new?ThemeData(?
          ?????brightness:?Brightness.dark,?
          ?????primaryColor:?Colors.lightGreen[800]?,?
          ?????accentColor:?Colors.cyan[200],?
          ??),?
          );?

          局部主題

          如果我們想在應(yīng)用程序的一部分中覆蓋應(yīng)用程序的全局的主題,我們可以將要覆蓋的部分封裝在一個(gè) Theme 的 Widget 中,有 2 種方法可解決:創(chuàng)建特有的 ThemeData 或擴(kuò)展父主題。

          創(chuàng)建特有的ThemeData

          如果我們不想繼承任何應(yīng)用程序的顏色或字體樣式,我們可以通過 new ThemeData() 創(chuàng)建一個(gè)實(shí)例并將其傳遞給 Theme Widget。

          //?Create?a?unique?theme?with?"new?ThemeData"?
          new?Theme(?
          ??data:?new?ThemeData(?
          ????accentColor:?Colors.yellow,?
          ??),?
          ??child:?new?FloatingActionButton(?
          ????onPressed:?()?{},?
          ????child:?new?Icon(Icons.add),?
          ??),?
          );?

          擴(kuò)展父主題

          擴(kuò)展父主題時(shí)無需覆蓋所有的主題屬性,我們可以通過使用 copyWith 方法來實(shí)現(xiàn)。

          //?Find?and?Extend?the?parent?theme?using?"copyWith".?Please?see?the?next?section?for?more?info?on?`Theme.of`.?
          new?Theme(?
          ??data:?Theme.of(context).copyWith(accentColor:?Colors.yellow),?
          ??child:?new?FloatingActionButton(?
          ????onPressed:?null,?
          ????child:?new?Icon(Icons.add),?
          ??),?
          );?

          使用主題

          我們可以在 Widget 的 build 方法中通過 Theme.of(context) 函數(shù)使用自定義的主題。

          new?Container(?
          ??color:?Theme.of(context).accentColor,?
          ??child:?new?Text(?
          ????'Text?with?a?background?color',?
          ????style:?Theme.of(context).textTheme.title,?
          ??),?
          );?

          渲染效果 如下 :

          總結(jié)

          以上分別介紹了在 App 應(yīng)用中對(duì) H5 頁面和客戶端的深色模式適配方案,當(dāng)然其中 H5 的方案頁同樣適應(yīng)于 PC 端。使用前一定要確保你的系統(tǒng)和瀏覽器是兼容深色模式的,不然就沒有效果了呢。本篇只簡(jiǎn)單介紹了幾種方案,歡迎有更好想法的小伙伴一起討論~

          參考資料

          • https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme
          • https://juejin.im/post/5eca7cbf518825430c3ab223
          • https://developer.android.com/guide/topics/ui/look-and-feel/darktheme
          • https://flutterchina.club/cookbook/design/themes/

          ??看完三件事

          如果你覺得這篇內(nèi)容對(duì)你挺有啟發(fā),我想邀請(qǐng)你幫我三個(gè)小忙:

          1. 點(diǎn)贊,讓更多的人也能看到介紹內(nèi)容(收藏不點(diǎn)贊,都是耍流氓-_-)
          2. 關(guān)注公眾號(hào)“前端勸退師”,不定期分享原創(chuàng)知識(shí)。
          3. 也看看其他文章

          勸退師個(gè)人微信:huab119

          也可以來我的GitHub博客里拿所有文章的源文件:

          前端勸退指南:https://github.com/roger-hiro/BlogFN一起玩耍呀

          瀏覽 135
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  国产91 白丝在一线播放 | 日韩高清在线 | 无码视频免费观看 | 99操逼网 | 台湾无码中文字幕 |