基于vue的换肤功能的示例代码


Posted in Javascript onOctober 10, 2017

最近在做的一个几月vue的移动端小demo,其中有一块是实现各个页面的统一换肤功能的。想着写一篇文章,来写一写实现过程中遇到的一些问题。

项目github地址

项目github地址

一 先看一下实现效果吧

基于vue的换肤功能的示例代码 

设置主题颜色

讲道理这么一个功能,我觉得这么几点可以说下,分步实现:

1. 色值的选取

2. scss 的一些小众用法(多变量CSS值的批量设置)

3. 全局事件巴士的应用

1 色值的选取和原则

推荐大家看下蚂蚁金服的设计指引,里面对常见的交互和界面设计有一套不错的指引和建议,喜欢看书的也可以看看《写给大家看的设计书》。

对于界面中的色彩元素,我们一般要保持视觉的连续性,即同一套色彩,尽量采取同一个色环上的色值

基于vue的换肤功能的示例代码 

同一个圆环上的色值作为一套颜色会显得更协调

所以这里采取ant design 的建议,取某一列色值作为我们的系列主题颜色(具体色值参照它的官网吧~)

基于vue的换肤功能的示例代码 

而在某些特殊场合,需要表现出颜色的差异,如抛硬币页面的两个颜色,

基于vue的换肤功能的示例代码 

2 将格式色值转换成十六进制颜色值

这里我们通过设置主题颜色的透明度来实现区分不同颜色, 然后我们是通过存储一个诸如 #123456 的16进制颜色全局变量作为我们主题,这里就需要我们把这样一个格式的色值转化成 rgba 表示的颜色值啦,代码如下,备用

hexToRgba (hex, opacity = 0.3) {
  let color = []
  let rgb = []
  hex = hex.replace(/#/, '')
  for (let i = 0; i < 3; i++) {
   color[i] = '0x' + hex.substr(i * 2, 2)
   rgb.push(parseInt(Number(color[i])))
  }
  return `rgba(${rgb.join(',')},${opacity})`
 }

3 scss 的一些小众用法

我们最终拿到这么一串我们想要的主题颜色

$colors: #f04134, #00a854, #108ee9, #f5317f, #f56a00, #7265e6, #ffbf00, #00a2ae, #2e3238;

一个很直接的思路,我们需要在各个view页面里面,去定义我们需要设置主题的元素的颜色,比如文字和icon的color, 以及头部的background 等。 于是我们在app 里面定义一个color变量,派发到各个view组件里面去,通过这个全局的变量来控制所有路由页面的颜色,以实现不同的主题效果。

派发的实现在下一个部分说,这里我们先来完成我们的第一步,我们可以容易提取出我们的需求:

4 设置并保存一个全局颜色

界面的小事:

我在首页直接实现这个功能,项目中我引入了mint-ui 框架(饿了么团队的移动端框架,稍微遗憾使用感觉没有element.ui 的舒服), 设置的交互就用弹层 mt-popup 的形式好了,然后直接点击色块便设置对应颜色值

<!-- ?置?色 -->
  <mt-popup v-model="changColor" position="bottom" class="color-panel">
   <div class="color-items">
     <span class="color-item" v-for="(item, $index) in colors" :key="$index" @click="chooseColor(item)">
      <span class="color-cycle" :class="'bg-color' + ($index + 1)"></span>
      </span>
   </div>
  </mt-popup>

接着就是色块div的呈现,从上面代码发现,我会很容易出现类似这样的css样式表

.bg-color1 {background: #f04134}
.bg-color2 {background: #f04134}
.bg-color3 {background: #f04134}
.bg-color4 {background: #f04134}
···

写代码时候如果我们一般发现,一件类似的东西重复出现了,就总隐隐觉得可以开始表演了,然后可预见的是,这样的情况意味着在项目增长后,还可能出现许多单一设置字体颜色或border颜色的样式表,诸如color1, borderColor1···,这样每种形式的表现我们都需要根据我们主题颜色的数组去逐条书写,修改成本也会变高 。于是我的书写风格是这样的,

// mixin.scss:
$colors: #f04134, #00a854, #108ee9, #f5317f, #f56a00, #7265e6, #ffbf00, #00a2ae, #2e3238;

// setColor.vue:
@import '~@/assets/mixin.scss';
···
@for $i from 1 to 10 {
    .bg-color#{$i} {
     background-color: nth($colors, $i)
    }
   }

scss 除了常用的类名嵌套书写外,还有许多···低调奢华的语法, 对于这类需要重复书写的样式类型,我的约定是添加一个scss变量在mixin 文件中, 在需要书写重复循环样式时候作为变量引入,并在书写样式时候,利用sass的循环,引用其中对应的值,这样无论设置颜色的样式怎么拓展和变化,变成颜色背景边框都好,我都只需要维护一份mixin的的文件里的色值就行了, 同样的实践也可以应用于项目里面字体大小和间距值的统一之类,总之我们多尝试体验下吧

5 逻辑的小事

这个项目里面localstorage 基本被当成数据库使用了,所以点击色块设置主题时候,我们假装发出请求,在localstorage存储我们改变的颜色就好了( ./static/api.json 是一个返回helloword 的json, 为了写实在这里这么用,$bus 事件巴士下面说, 作用就是设置全局的主题颜色变量,localStorage 模拟我们把设置存储到后台,每次重新打开页面就去获取这些设置值), 目前为止,我们的设置页面就大致完成了

// 假装调用接口设置颜色
  chooseColor (color) {
   this.$axios.get('./static/api.json')
    .then((data) => {
     this.$bus.$emit('set-theme', color)
     this.changColor = false
     localStorage.setItem('themeColor', color)
    })
    .catch((data) => {
     console.log(data)
    })
  }

6 事件巴士的运用

在上一步最后我们有个关键的东西没完成, this.$bus.$emit('set-theme', color) ,将选取的颜色设置到全局,我的代码结构是这样的

基于vue的换肤功能的示例代码 

子组件

<setColor> 是 home 页面 的一个子组件,而在一开始我们已经说了,我们想在我们在app.vue (home.vue和其他view的父组件) 里面定义一个color变量,派发到各个view组件里面去。 于是这其实就是个,从 setColor 触发 app.vue 的设置颜色事件, 子组件向父组件通信的问题。

我们可以很直接地用绑定事件配合 emit() 的做法,在 app.vue 定义一个 setglobalColor 方法, 并绑定到router-view(包含了home.vue),接着在home组件继续定义一个 setglobalColor 方法, 实现的功能就是 emit('setglobalColor') 去触发app.vue的方法, 并把 home.vue 的这个 setglobalColor 继续绑定到组件, 组件里面点选颜色时候,直接emit这个方法就行了。

为什么我想用事件巴士.vue 的事件巴士和 vuex, 在一些有追求的程序员手里总是小心翼翼的,我也一样,因为作为涉及全局的东西,一般觉得能不用就不用,代码能精简就精简,我们经常用一个词,不提倡。

可是有朝一日我经常在想,代码的可读性可维护性,和性能以及“风险”相对比,到底哪个更重要。对于事件巴士和vuex 这类全局性质的方案的主要担忧大部分在于, 他们是全局的,可能因为一个事件名变量名一致就造成冲突,在小型项目还会造成冗余和额外开销。 但事实上,事件和变量的命名我们都可以通过约定去规范,而在表现上,使用了事件巴士和vuex的项目,在性能上和直接 props 传递数据,emit 回调事件的项目相比,其实并没有太大区别,反而是无止境的 props 和 emit ,给人一种麻烦难以维护的感觉。 像上述的 setglobalColor , 仅仅是跨越了两层组件, 过程就显得繁琐了。所以我建议在出现两级以上组件层次,数据流稍微多的项目中都可以这么去做,定义一个全局的事件巴士

export default (Vue) => {
 let eventHub = new Vue()
 Vue.prototype.$bus = {
  $on (...arg) {
   eventHub.$on(...arg)
  },
  $off (...arg) {
   eventHub.$off(...arg)
  },
  $emit (...arg) {
   eventHub.$emit(...arg)
  }
 }
}

将事件巴士绑定到当前vue对象,使用时候只需要:

this.$bus.$on('set-theme', (color) => {··· })
this.$bus.$emit('set-theme', '#000000')

在这个demo中,我在app.vue 绑定了

this.$bus.$on('set-theme', (color) => {
   this.loadingColor = color
   this.userinfo.color = color
  })

而在 setColor.vue 则在点击颜色块时候触发 this.$bus.$emit('set-theme', color), 则能实现我们设置全局颜色的效果。这样的好处在于,对于跨了多个层次,或者兄弟组件的通信,我们不再需要太繁琐的props,比如我在header.vue 也绑定了 this.$bus.$on('set-theme', (color) => { }) ,在 this.$bus.$emit 发生时候,header 的背景颜色就能直接改变,而不需要等待app.vue 将 全局的color值props传递到header.vue里面(仅做示例,这里 header.vue 只是 app.vue 的下一层级,通过props数据流会更清晰)

而对于其他路由页面组件,和 app.vue 都是直接上下级关系,我们依然采用props保持一个清晰的数据流向下传递, demo 里我是将 color 存在userinfo(以后还有其他数据), userinfo传到每个子路由, 最后,每个页面在创建时候,通过拿到这个全局的颜色,再用dom去更改对应的样式就好啦,例如

mounted () {
  this.$nextTick(() => {
   // 绑定设置主题的事件,一旦触发修改主题,则将当前字体颜色改为对应颜色
   this.$el.querySelector('.myTitle').style.color = this.userinfo.color
   this.$el.querySelector('.weui-btn_primary').style.backgroundColor = this.userinfo.color
   this.$el.querySelector('.add_icon').style.color = this.userinfo.color
  })
 }

详细的实现请参照项目代码,这里我只挑一些比较清奇的点出来讨论,项目和代码的一些规范和习惯还是挺重要的,希望有好的实践能互相借鉴进步~

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
jquery 元素相对定位代码
Oct 15 Javascript
基于jQuery的倒计时插件代码
May 07 Javascript
弹出最简单的模式化遮罩层的js代码
Dec 04 Javascript
可插入图片的TEXT文本框
Dec 27 Javascript
JS实现跟随鼠标的链接文字提示框效果
Aug 06 Javascript
微信开发 使用picker封装省市区三级联动模板
Oct 28 Javascript
RequireJS简易绘图程序开发
Oct 28 Javascript
关于react-router/react-router-dom v4 history不能访问问题的解决
Jan 08 Javascript
Vue-router的使用和出现空白页,路由对象属性详解
Sep 03 Javascript
手把手带你封装一个vue component第三方库
Feb 14 Javascript
js Array.slice的8种不同用法示例
Jul 10 Javascript
详解element-ui 表单校验 Rules 配置 常用黑科技
Jul 11 Javascript
seajs中模块依赖的加载处理实例分析
Oct 10 #Javascript
利用JS hash制作单页Web应用的方法详解
Oct 10 #Javascript
认识jQuery的Promise的具体使用方法
Oct 10 #jQuery
JavaScript如何获取到导航条中HTTP信息
Oct 10 #Javascript
使用Dropzone.js上传的示例代码
Oct 10 #Javascript
React中上传图片到七牛的示例代码
Oct 10 #Javascript
基于对象合并功能的实现示例
Oct 10 #Javascript
You might like
PHP操作xml代码
2010/06/17 PHP
php中如何判断一个网页请求是ajax请求还是普通请求
2013/08/10 PHP
PHP正则验证Email的方法
2015/06/15 PHP
Yii隐藏URL中index.php的方法
2016/07/12 PHP
Zend Framework实现自定义过滤器的方法
2016/12/09 PHP
读取input:file的路径并显示本地图片的方法
2013/09/23 Javascript
减少访问DOM的次数提升javascript性能
2014/02/24 Javascript
JavaScript定时器和优化的取消定时器方法
2015/07/03 Javascript
js中跨域方法原理详解
2015/07/19 Javascript
jQuery版本升级踩坑大全
2016/01/12 Javascript
Bootstrap组件(一)之菜单
2016/05/11 Javascript
快速解决vue-cli不能初始化webpack模板的问题
2018/03/20 Javascript
vue2.0移动端滑动事件vue-touch的实例代码
2018/11/27 Javascript
JS实现放烟花效果
2020/03/10 Javascript
基于JavaScript或jQuery实现网站夜间/高亮模式
2020/05/30 jQuery
[01:12:53]完美世界DOTA2联赛PWL S2 Forest vs SZ 第一场 11.25
2020/11/26 DOTA
python中global用法实例分析
2015/04/30 Python
python绘制铅球的运行轨迹代码分享
2017/11/14 Python
pip命令无法使用的解决方法
2018/06/12 Python
python3监控CentOS磁盘空间脚本
2018/06/21 Python
python多线程调用exit无法退出的解决方法
2019/02/18 Python
python3 dict ndarray 存成json,并保留原数据精度的实例
2019/12/06 Python
Python模块 _winreg操作注册表
2020/02/05 Python
提高python代码运行效率的一些建议
2020/09/29 Python
Pycharm编辑器功能之代码折叠效果的实现代码
2020/10/15 Python
Python可以用来做什么
2020/11/23 Python
借助HTML5 Canvas API制作一个简单的猜字游戏
2016/03/25 HTML / CSS
Laura Geller官网:美国彩妆品牌
2018/12/29 全球购物
施华洛世奇波兰官网:SWAROVSKI波兰
2019/06/18 全球购物
俄罗斯最大的在线珠宝大卖场:Nebo
2019/12/08 全球购物
卡拉威高尔夫官方网站:Callaway Golf
2020/09/16 全球购物
优秀女职工事迹材料
2014/02/06 职场文书
酒店财务经理岗位职责
2015/04/08 职场文书
创业计划书之牛肉汤快餐店
2019/10/08 职场文书
详解Vue的sync修饰符
2021/05/15 Vue.js
《雀魂PONG☆》4月1日播出 PV角色设定情报
2022/03/20 日漫