如何实现自定义主题切换?

本文章是基于ElementUI theme-chalk-preview方法上的改进

效果展示

切换前:

截屏2021-11-20 21.16.38.png

切换后:

截屏2021-11-20 21.16.28.png

先来说一下为什么选择该方案吧,为什么有CSS变量替换等其他更优方法不用呢?

因为IE !!!2021了,居然还要支持IE(我不李姐😐 )

言归正传,先来了解一下原方法的思路吧😆


以下步骤代码摘抄至源码

1、使用XMLHttpRequest请求服务端的CSS样式文件(经步骤 2 处理后,将替换后的全部样式保存在 originalStyle 中)

getFile (url, isBlob = false) {
    return new Promise((resolve, reject) => {
        const client = new XMLHttpRequest()
        client.responseType = isBlob ? 'blob' : ''
        client.onreadystatechange = () => {
            if (client.readyState !== 4) {
                return 
            }
            if (client.status === 200) {
                const urlArr = client.responseURL.split('/')
                resolve({
                    data: client.response,
                    url: urlArr[urlArr.length - 1]
                })
            } else {
              reject(new Error(client.statusText))
            }
       }
       client.open('GET', url)
       client.send()
       })
},
getIndexStyle () { 
    this.getFile('//unpkg.com/element-ui/lib/theme-chalk/index.css') 
        .then(({ data }) => { 
            this.originalStyle = this.getStyleTemplate(data) 
        }) 
}

2、将获取到的样式中涉及到的颜色值替换成关键词

getStyleTemplate (data) {
    const colorMap = { 
        '#3a8ee6': 'shade-1', 
        '#409eff': 'primary', 
        '#53a8ff': 'light-1', 
        '#66b1ff': 'light-2', 
        '#79bbff': 'light-3', 
        '#8cc5ff': 'light-4', 
        '#a0cfff': 'light-5', 
        '#b3d8ff': 'light-6', 
        '#c6e2ff': 'light-7', 
        '#d9ecff': 'light-8', 
        '#ecf5ff': 'light-9' 
    } 
    Object.keys(colorMap).forEach(key => { 
        const value = colorMap[key] 
        data = data.replace(new RegExp(key, 'ig'), value) 
    }) 
  return data 
}

替换前:

h {
    color: #3a8ee6;
}

替换后

h {
    color: shade-1;
}

3、根据用户选择的颜色,自动生成每个关键词对应的颜色值

4、将新生成的颜色值替换回对应的关键词处

5、新增style标签,将样式写入

if (this.originalStylesheetCount === document.styleSheets.length) { 
    const style = document.createElement('style') 
    style.innerText = cssText document.head.appendChild(style) 
} else { 
    document.head.lastChild.innerText = cssText 
}

新增style标签前,先判别是否曾添加过,若无,则新建style,将其添加至末尾。若有,则直接覆盖末尾子节点的内容。

至此我们就完成了自定义主题切换的全部功能

现在我们来思考如何优化他

1、开发模式下支持主题色替换

一般我们在做开发时,都是在本地运行代码,此时上述方式便失效了(ElementUI项目运行时无法以上述请求方式拿到样式文件),一旦项目大起来,编译打包,再映射,查看该功能是否生效,一来费时,二来出现问题也不好定位,因此我们能不能在开发模式下,也支持该功能呢?

问题的关键就在于,如何拿到页面上的CSS样式,此时我们就要借助他的帮忙 styleSheets

截屏2021-11-19 22.16.08.png

关注其中的 cssRules 属性(如下图所示)

截屏2021-11-19 22.18.38.png

诶,cssText 属性中不就存着我们所需的样式嘛?由此我们可以遍历页面上所有的 styleSheets ,然后将cssRules.cssText一行一行的拼凑起来,不就是一个完整的样式了吗?

for (let j = 0; j < document.styleSheets.length; j++) {
    try {
        for (let i = 0; i < document.styleSheets[j].cssRules.length; i++) {
            try {
               this.originalStyle = this.originalStyle.concat(document.styleSheets[j].cssRules[i].cssText);
            } catch (error) {
                continue;
            };
        }
    } catch (error) {
        continue;
    };
}
2、支持 rgb 色值的更改

我们都知道色值有许多种表示方式,在 CSS 中最为常见的莫过于rgb 和 hex ,可上述的例子中仅支持了 hex 的修改,若是某些颜色需带有透明度(或者同种颜色)用了 rgb 的方式表示,就不能修改了。( hex 也可以表示透明度,只可惜 IE 依然不支持)

我们可以把 colorMap(不知道colorMap是什么的,赶紧往上翻!!!不认真!点名批评💢 )变为以下格式

const colorMap = { 
    '64,158,255': 'primary',   // 64,158,255 是 #409eff 对应 rgb 值
    '64, 158, 255': 'primary', 
    '#409eff': 'rgb(primary)'
}

ElementUI 颜色选择器,选择后传出的色值是 hex 格式的,因此我们的色值生成函数,需要进行 hex 到 rgb 的转换,具体函数这里就不贴了,网上很多。

3、删除指定的style标签

原方法是删除最后一个 style 标签,若最后一个 style 标签不是我们新增的呢?此时就会造成误删的情况。

if (document.querySelector("style[title='replaceStyle']")) {
    try {
        document.querySelector("style[title='replaceStyle']").remove();
    } catch (error) {
      document.querySelector("style[title='replaceStyle']").removeNode();
    }
}

新增 style标签 时,附加一个 title ,每次删除时,只删除指定 title 的 style标签。

1637414267267.jpg

4、提升IE浏览器下的替换速度

当新增过 style 标签后,后续的每次替换,我们就将样式覆盖进去,但这速度在 IE 上,不敢恭维,速度慢得令人崩溃😩 ,那有没有什么方法可以提速呢?

实测发现,直接删除原新增的 style 标签,然后再重新添加 style 标签,速度居然比我们直接覆盖更快!!!但这还是远远不够,IE表示,还是很吃力🙉 。此时我们可以利用 Fragment ,让我们先简单了解一下他。

DocumentFragment 是 DOM 节点。不是主 DOM 树的一部分。常用于创建文档片段,将元素附加到文档片段,然后将文档片段附加到 DOM 树。在 DOM 树中,文档片段被其所有的子元素所代替。因为文档片段存在于内存中,并不在 DOM 树中,所以将子元素插入到文档片段时不会引起页面回流(对元素位置和几何上的计算)。因此,使用文档片段通常会带来更好的性能。

const dq = document.createDocumentFragment();  //新建fragment片段
const style = document.createElement('style');  //创建元素
style.title = 'replaceStyle';  //添加标题
style.innerHTML = cssText;
dq.appendChild(style);  //元素插入至fragment片段中
document.head.appendChild(dq);  //插入片段的所有子元素

处理过后,现在 IE 上的主题切换,也变得非常丝滑啦~

最后

本文到此结束,希望对你有所帮助。若有什么好的建议,可以多多交流呀。文中若有不正之处,欢迎指正。

前端萌新向各位大佬鞠躬了,谢谢啦 😘

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
LVGL 7 提供了一种简单的方式来实现界面主题切换,即使用 LV_THEME_DEF 宏定义一个主题数组,然后在运行时使用 lv_theme_set_current() 函数来切换当前主题。以下是一个示例代码: ```c static lv_theme_t *th; /* 定义主题数组 */ LV_THEME_DEF(theme_default, NULL); LV_THEME_DEF(theme_dark, NULL); void theme_init(void) { /* 初始化默认主题 */ th = lv_theme_default_init(lv_disp_get_default()); /* 初始化自定义主题 */ lv_theme_set_current(theme_default, false); } void switch_theme(void) { static bool theme_is_dark = false; /* 切换主题 */ if (theme_is_dark) { lv_theme_set_current(theme_default, false); } else { lv_theme_set_current(theme_dark, false); } /* 更新状态 */ theme_is_dark = !theme_is_dark; } ``` 在这个示例中,我们定义了两个主题数组:`theme_default` 和 `theme_dark`。在 `theme_init` 函数中,我们初始化了默认主题,并将当前主题设置为 `theme_default`。在 `switch_theme` 函数中,我们切换当前主题,并更新主题状态。 需要注意的是,在创建主题数组时,需要使用 LV_THEME_DEF 宏,并将数组名作为参数传递。这样 LVGL 才能正确识别主题数组。同时,每个主题数组都需要包含一个名为 `theme` 的结构体变量,用于存储主题配置参数。在上述示例中,由于我们没有定义自定义主题参数,因此传递了 NULL。 希望这个示例能够帮助你实现 LVGL 7 界面主题切换

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值