【前端教程】全网最详!暗黑模式在 Trip.com App 的实践

2023-12-31 02:20

本文主要是介绍【前端教程】全网最详!暗黑模式在 Trip.com App 的实践,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

一、背景

在 2019 年,随着 iOS 13 与 Android Q 的推出,Apple 和 Google 同时推出主打功能暗黑模式,分别为 Dark Mode(iOS)/Dark Theme(Android) ,下文我们统称为 Dark Theme。在前期预研中,我们发现 66% 的 iOS 13 用户选择打开Dark Theme,可见用户对暗黑模式的喜爱和期待。

那么 Dark Theme 能带来哪些好处呢?

  • 更加省电,当代手机大部分都是OLED屏(OLED屏黑色下不发光更省电),配合Dark Theme 能耗更低;

  • 提供一致性的用户体验,当用户从Dark Theme的环境切换到我们的App,仍然能够享受黑色的宁静,避免亮眼的白色带来的刺激感;

  • 提升品牌形象,及时跟进系统新特性,在享受新特性带来美好之外还能获得Apple Store和Google Play推荐位机会,提升整体品牌形象;

  • 为弱视以及对强光敏感的用户提高可视性,让用户在暗环境中轻松使用App。

接下来,我们从视觉设计、实现方案和开发效率三个角度来介绍 Dark Theme 在 Trip.com App的实践。

二、视觉设计

暗黑模式是一套全新的设计风格,非简单的颜色明暗处理。我们将设计理念归结为三大要点,并介绍我们整体的设计思路。

2.1 三大要点

1)元素层级越高,表面颜色越浅

UI视觉层次致力于以一种用户能够快速理解的方式呈现产品内容,那么在 Dark Theme 下如何保证视觉层级依然有效呢?在 Light 模式中,我们使用带投影的白色卡片来模拟现实世界的空间深度感,而切换到 Dark 模式,则需要通过较浅的颜色表面来表示高度。层级越高,越接近于光源,表面的颜色就越浅。

image

2)降低饱和度,提升可读性

设计 Dark Theme 时,尽量避免使用高饱和度的颜色,因为这些颜色会在深色背景上产生视觉抖动,导致人眼产生疲劳。以 Trip.com 的品牌蓝为例,若颜色不做调整,直接展示在深色背景上,不仅信息的清晰度降低了,而且识别的费力度还增高了。这显然不是我们所希望的,所以在 Dark Theme 下我们选择更低饱和的颜色来达到更好的可读性。

image

3)增加对比度,提升可用性

依据 WCAG2.0 AA 设计标准,文本的视觉呈现以及文本图像至少要有4.5:1的对比度。深色表面选取白色文字达不到 AA 标准。

image

2.2 设计方案

遵循上述设计要点,我们制定了 Trip.com 的颜色映射和插画设计方案。

2.2.1 颜色映射方案

为了规范化管理颜色库,保证产品、设计、开发的理解一致性,我们采用最直观的方式来命名颜色。这种方式既统一了 Light 和 Dark 的颜色命名,又降低了各方的沟通难度。具体的映射效果如下:

image

UI中的彩色,统一进行了降饱和处理,这些彩色会应用于不同的场景,可能是背景,行动点,标签,或者是图标等等地方,那么当彩色用于背景时,为了确保文字和背景色有足够对比度,低饱和度的浅色背景就需要配合深色字一起使用。

image

2.2.2 插画系统的设计

开启 Dark Theme,就像是我们把房间的窗帘拉上了,打开了一盏灯,不同层级高度的物体表面会受到不同的光照,表现出不同明暗的颜色。我们插画系统中的物体和人物沿用这种设计,在暗环境中,由于光线不够充足,人物的肤色会跟着变暗,衣服的颜色也会发生微妙的变化。比如白色、鲜亮的衣服,到了暗环境下,就会呈现灰色、低饱和度的暗色。

image

三、实现方案

Trip.com App 使用原生系统与 React Native 混合开发的模式。我们在各系统方案的基础上,结合 Trip.com 自身的特性,制定了一套iOS、Android和React Native三端的Dark Theme适配方案。

3.1 iOS

我们为 iOS 13 以上用户提供了两种主题模式的选择:

  • 自适应模式:跟随系统展示 Light/Dark 主题

  • 强制 Light 模式:App 保持 Light 主题,不随系统主题变化

3.1.1 适配原理

iOS系统为 UIWindow、UIViewController、UIView 提供了overrideUserInterfaceStyle 属性来控制 Light/Dark 主题,所以我们只要控制 KeyWindow 的该属性,就可以控制整个 App 的主题。

3.1.2 适配方案

1)设置开关

image

App主题设置逻辑如图,KeyWindow 只有在App和系统都开启 Dark Theme 时,才会开启 Dark 主题。

跟随系统切换主题需要考虑到 App 运行时,系统主题被切换的情况:

  • 前往系统设置页手动切换

  • 开启自动切换后,系统会自动更新主题

这两种情况都需 App 进入后台,所以只需要添加 App 进入前台的监听,重复1的逻辑即可完成跟随系统变换主题的功能。

2)颜色适配

系统提供了 colorWithDynamicProvider 方法来适配 Light/Dark 模式下的颜色,我们依照视觉颜色映射方案封装颜色,覆盖绝大多数场景。部分无法通过动态色适配的场景,如 CGColor、RGB 颜色,可以通过 resolvedColorWithTraitCollection 方法解析出当前上下文所需要的颜色进行使用。

3)图片适配

系统早在 iOS12 就为 UITraitCollection 增加了 userInterface 属性,我们只要向 ImageAssets 注册 Light/Dark 下两种主题的图片,而后 UIImageView 根据 traitCollectionDidChange 变化自动获取 Light/Dark 图片。

App 内的静态图片资源可以通过 Images.xcassets 直接配置,通过网络下发或代码动态生成的图片可以通过 registerImage:withTraitCollection: 的方式进行动态注册。

4)注意事项

动态色或 ImageAssets 的原理都是根据容器的 userInterface 取得对应的内容,视图上的动态颜色或 ImageAssets 将根据视图的 userInterface 取值,App 内直接进行颜色计算或者图片处理的将会根据 UITraitCollection.currentColletion 进行取值。

设置 Window 的主题来完成 App 主题适配的工作,会存在 App 主题与系统主题不同步的情况,例如系统主题为 Dark,App 主题为 Light。此时直接对动态颜色或 ImageAssets 进行操作会取得错误的结果。所以对于这种场景,都不使用动态色或 ImageAssets,仅在发生主题切换时机进行视图刷新操作。

3.2 Android

我们不仅在 Android Q 上实现 Dark Theme,在 Android Q 以下的版本也适配了 Dark Theme。在 Android Q 上,用户可以选择跟随系统来展示 Dark Theme 或者强制关闭 Dark 保持 Light 主题。

在 Android Q 以下,我们也支持了 Dark Theme,用户可以选择强制打开或者强制关闭 Dark Theme。

3.2.1 适配原理

Android App 启动时会根据系统的配置加载不同的资源,以加载图片为例,高分辨率系统加载三倍图,低分辨率系统加载二倍图。同样地,系统也会根据 Dark Theme 的打开或者关闭来加载 Dark 或者 Light 资源。

我们会往 App 的 value 和 value-night 文件目录下放置 UED 提供的 Light 和 Dark 两套资源。当 App 打开 Dark Theme,系统选择从 value-night 目录加载资源,展示 Dark 界面;当 App 关闭 Dark Theme,系统选择从 value 目录加载资源,展示 Light 界面。

3.2.2 适配方案

我们通过开关设置、颜色适配、图片适配和其他注意事项四小节来介绍Android的Dark Theme适配方案。

1)开关设置

从上述代码可以看出,只有使用 AppCompat 的代码才具有 Dark Theme 特性,例如继承 AppCompatAcivity 和 AppCompatDialog 才支持 Dark Theme,而普通的 Activity 和 Dialog 不会展示 Dark Theme,同样地 Application 也不支持。

// 打开darkmode 
2)颜色适配

在 value 和 value-night 目录下定义 Light 和 Dark 相同名字的颜色,如下图:

image

在 XML 或者代码中使用

//xml 

注意:Activity 必须是 AppCompatActivity 实例,不能是 ApplicationContext/Activity。另外由于带透明度的颜色必须一个一个在 XML 声明,为了减轻开发工作量,我们提供了一个脚本可以快速生成 Light 和 Dark 下的透明度颜色。

3)图片适配

图片适配工作分资源图片适配和自定义 drawable 适配:

  • drawable/mipmap:在 drawable-xxhdpi 和 drawable-night-xxhdpi 目录下放置Light和Dark相同名字的图片,系统根据Light/Dark加载图片。

  • IconFont/自定义Shape/自定义Selector/SVG:因为绘制使用颜色,所以用法同颜色。

4)注意事项
  • 在非 AppCompatActivity 内展示 Dark Theme ,利用下面的代码可在非 AppCompatActivity 内展示 Dark 颜色。
public class IBUDarkModeDelegate {
  • 颜色名必须全App唯一。

  • 切换手机系统的Dark Theme,会导致Activity重建,业务线按需做好状态保存恢复。

  • 做好全机型测试,防止个别机型出现异常展示问题。

3.3 ReactNative

3.3.1 适配方案

RN 桥接 Native 端,通过直接获取和动态监听两种方式获取 Native 端的主题变化。

1)从 Native 端获取当前的 theme 值

使用 Native Modules 的同步方法在 JS 端获取当前 theme 值,JS 端方法调用能直接得到 Native 同步方法的返回值,而非一个 Promise。

同步方法于 2017 年 1 月和 10 月先后被引入 ReactNative 的 Android 端和 iOS 端, 但直到现在,仍然没有被写入文档:

  • iOS: 使用 RCTEXPORTSYNCHRONOUSTYPEDMETHOD() 替换 RCTEXPORTMETHOD()(v0.51.0 及以上版本支持Commit)

  • Android: 在 @ReactMethod annotation 后面添加 (isBlockingSynchronousMethod = true) (v0.42.0 及以上版本支持Commit)

同步方法的缺点是无法在 Debug Remotely 时调用,所以必须在 Debug Remotely 时,提供默认值。我们接入 dark theme 时,选择了 dark 作为默认值。

2)theme 值变化监听

我们使用RN事件监听Theme变化。

  • iOS: RCTEventEmitter

  • Android: RCTDeviceEventEmitter

3)RN业务方调用 theme

我们提供 IBUThemeContext & IBUThemeProvider 两个类供产线获取主题。 Context 提供了一个无需为每层组件手动添加 props,就能在组件树间进行数据传递的方法。IBUThemeContext 是 Context 在 Theme 上的一个应用, IBUThemeProvider 负责同步 Theme 值,并将其传递给 IBUThemeContext.Provider。

// IBUThemeContext

将IBUThemeProvider 嵌入App 的根节点, 组件树便能通过如下两种方法,获取theme值:

通过IBUThemeProvider.theme 读取全局theme。声明了static contextType=IBUThemeContext 的类中使用 this.context,获取theme值。

4)颜色适配

我们提供下列方法供产线使用颜色,方法支持透明度的设置:

export declare class IBUColor{

所有方法均接受 theme 和 alpha 两个可选参数, 方法会先根据 theme 选择对应颜色的 hex 字符串色值,如果 theme 值为空, 则 fallback 到 IBUThemeProvider.theme , 之后再根据 alpha 值计算颜色的的 alpha hex 值,并拼接到 hex 字符串色值之后。如 alpha 为空,则不拼接 hex 色值。最后将对应的 hex 色值字符串返回。

5)图片适配

我们使用 lazy getters 解决 Light/Dark 图片展示的问题。方式如下:

RN端图片之前已经作了统一的静态资源管理:

export const images = {

使用 lazy getters,稍作改造后,即能完美适配:

export const images = {

6)DynamicStyle

ReactNative 导出的 StyleSheet 只会在文件引入时,初始化一次,不会随着 App DarkTheme 的变化而变化这就导致系统主题发生变化时,RN 无法更新 styles,导致 RN 页面与 Native 不一致的问题。为此我们提出 DynamicStyleSheet 来解决该问题。

type IBUNamedStyles<T> = { [P in keyof T]: ViewStyle | TextStyle | ImageStyle };

IBUDynamicStyleSheet 是一个 Function,它接受一个返回值是 style 的 Function 作为参数,并且返回一个 Function。这种 Function 也被称High Order Function。

StyleSheet 创建 style 的代码被包在参数的 Function 中,这样可以保证每次取值都会取到当前的 theme 对应的 style。每次 render 前, 将返回的 Function 执行一次,并将这个 Function 的返回值作为真正的 style 使用。

IBUDynamicStyleSheet 内部对 light 和 dark 下的 style 作了缓存,这样大部分情况下 style 仍然只会被创建一次, theme 发生变化时 style 被创建两次, theme 发生多次变化时,style 最多只被创建两次。

采用DynamicStyleSheet这种方式,代码改动量不仅小, 而且性能损失少, 达到实时切换Theme的目的。

7)Examples

App 开启dark theme

export default class App extends Component{

Class Component 接入

class MyClass extends React.Component {

Functional Component接入

export const MyComponent = () => {

注意:Component必须声明contextType, 否则不能在theme发生变化时触发render重绘。

四、工具&效率

在建立颜色规范到方案落地的过程中,我们发现新的颜色命名虽然容易理解,由于对使用的名字命名,开发在使用时需要对照视觉稿查找对应的颜色命名,造成开发效率上的浪费。

例如视觉稿上显示 #287DFA,开发根据色值查找此颜色的映射名称 brandingBlue,再将颜色设置成 brandingBlue。

为了解决此问题,我们扩展了 Sketch Measure 插件,颜色一栏不再展示颜色的色值,取而代之的是颜色的命名。这样开发能依照视觉稿直接获取颜色名,大大减少工作量。

插件效果如下 :

image

至此完美解决了开发适配 Dark Theme 的效率问题。

五、结语

Dark Theme适配是一项涉及多职能部门合作的项目。在规范的设计指导、完善的落地方案和便捷的效率工具加持下,我们的适配成本和资源大大降低。在各端仅投入一位研发人员的情况下,在两周内完成了从方案制定到方案落地,并推进产线接入。

Trip.com一直致力于追随前沿新特性,带给用户最好的体验,让用户更舒适,旅行从此简单。

image

服务推荐

  • 蜻蜓代理
  • ip代理服务器
  • 企业级代理ip
  • 微信域名检测
  • 微信域名拦截检测

这篇关于【前端教程】全网最详!暗黑模式在 Trip.com App 的实践的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/554688

相关文章

Vue3 的 shallowRef 和 shallowReactive:优化性能

大家对 Vue3 的 ref 和 reactive 都很熟悉,那么对 shallowRef 和 shallowReactive 是否了解呢? 在编程和数据结构中,“shallow”(浅层)通常指对数据结构的最外层进行操作,而不递归地处理其内部或嵌套的数据。这种处理方式关注的是数据结构的第一层属性或元素,而忽略更深层次的嵌套内容。 1. 浅层与深层的对比 1.1 浅层(Shallow) 定义

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

基于MySQL Binlog的Elasticsearch数据同步实践

一、为什么要做 随着马蜂窝的逐渐发展,我们的业务数据越来越多,单纯使用 MySQL 已经不能满足我们的数据查询需求,例如对于商品、订单等数据的多维度检索。 使用 Elasticsearch 存储业务数据可以很好的解决我们业务中的搜索需求。而数据进行异构存储后,随之而来的就是数据同步的问题。 二、现有方法及问题 对于数据同步,我们目前的解决方案是建立数据中间表。把需要检索的业务数据,统一放到一张M

这15个Vue指令,让你的项目开发爽到爆

1. V-Hotkey 仓库地址: github.com/Dafrok/v-ho… Demo: 戳这里 https://dafrok.github.io/v-hotkey 安装: npm install --save v-hotkey 这个指令可以给组件绑定一个或多个快捷键。你想要通过按下 Escape 键后隐藏某个组件,按住 Control 和回车键再显示它吗?小菜一碟: <template

【 html+css 绚丽Loading 】000046 三才归元阵

前言:哈喽,大家好,今天给大家分享html+css 绚丽Loading!并提供具体代码帮助大家深入理解,彻底掌握!创作不易,如果能帮助到大家或者给大家一些灵感和启发,欢迎收藏+关注哦 💕 目录 📚一、效果📚二、信息💡1.简介:💡2.外观描述:💡3.使用方式:💡4.战斗方式:💡5.提升:💡6.传说: 📚三、源代码,上代码,可以直接复制使用🎥效果🗂️目录✍️

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

Makefile简明使用教程

文章目录 规则makefile文件的基本语法:加在命令前的特殊符号:.PHONY伪目标: Makefilev1 直观写法v2 加上中间过程v3 伪目标v4 变量 make 选项-f-n-C Make 是一种流行的构建工具,常用于将源代码转换成可执行文件或者其他形式的输出文件(如库文件、文档等)。Make 可以自动化地执行编译、链接等一系列操作。 规则 makefile文件

在JS中的设计模式的单例模式、策略模式、代理模式、原型模式浅讲

1. 单例模式(Singleton Pattern) 确保一个类只有一个实例,并提供一个全局访问点。 示例代码: class Singleton {constructor() {if (Singleton.instance) {return Singleton.instance;}Singleton.instance = this;this.data = [];}addData(value)

系统架构师考试学习笔记第三篇——架构设计高级知识(20)通信系统架构设计理论与实践

本章知识考点:         第20课时主要学习通信系统架构设计的理论和工作中的实践。根据新版考试大纲,本课时知识点会涉及案例分析题(25分),而在历年考试中,案例题对该部分内容的考查并不多,虽在综合知识选择题目中经常考查,但分值也不高。本课时内容侧重于对知识点的记忆和理解,按照以往的出题规律,通信系统架构设计基础知识点多来源于教材内的基础网络设备、网络架构和教材外最新时事热点技术。本课时知识

计算机毕业设计 大学志愿填报系统 Java+SpringBoot+Vue 前后端分离 文档报告 代码讲解 安装调试

🍊作者:计算机编程-吉哥 🍊简介:专业从事JavaWeb程序开发,微信小程序开发,定制化项目、 源码、代码讲解、文档撰写、ppt制作。做自己喜欢的事,生活就是快乐的。 🍊心愿:点赞 👍 收藏 ⭐评论 📝 🍅 文末获取源码联系 👇🏻 精彩专栏推荐订阅 👇🏻 不然下次找不到哟~Java毕业设计项目~热门选题推荐《1000套》 目录 1.技术选型 2.开发工具 3.功能