基于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 相关文章推荐
IE bug table元素的innerHTML
Jan 11 Javascript
js jquery数组介绍
Jul 15 Javascript
仿当当网淘宝网等主流电子商务网站商品分类导航菜单
Sep 25 Javascript
javascript的document.referrer浏览器支持、失效情况总结
Jul 18 Javascript
EasyUI中datagrid在ie下reload失败解决方案
Mar 09 Javascript
JavaScript中的ParseInt(&quot;08&quot;)和“09”返回0的原因分析及解决办法
May 19 Javascript
JS实现去除数组中重复json的方法示例
Dec 21 Javascript
JavaScript中的一些隐式转换和总结(推荐)
Dec 22 Javascript
react native基于FlatList下拉刷新上拉加载实现代码示例
Sep 30 Javascript
详解Vue前端对axios的封装和使用
Apr 01 Javascript
React组件设计模式之组合组件应用实例分析
Apr 29 Javascript
Vue-router中hash模式与history模式的区别详解
Dec 15 Vue.js
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
10个实用的PHP代码片段
2011/09/02 PHP
php验证手机号码(支持归属地查询及编码为UTF8)
2013/02/01 PHP
ubuntu下编译安装xcache for php5.3 的具体操作步骤
2013/06/18 PHP
php foreach循环中使用引用的问题
2013/11/06 PHP
对laravel的session获取与存取方法详解
2019/10/08 PHP
js活用事件触发对象动作
2008/08/10 Javascript
JavaScrip单线程引擎工作原理分析
2010/09/04 Javascript
JavaScript之appendChild、insertBefore和insertAfter使用说明
2010/12/30 Javascript
js中将HTMLCollection/NodeList/伪数组转换成数组的代码
2011/07/31 Javascript
理解JAVASCRIPT中hasOwnProperty()的作用
2013/06/05 Javascript
jquery实现点击文字可编辑并修改保存至数据库
2014/04/15 Javascript
js中javascript:void(0) 真正含义
2020/11/05 Javascript
纯js代码实现简单计算器
2015/12/02 Javascript
基于socket.io+express实现多房间聊天
2016/03/17 Javascript
基于jquery实现最简单的选项卡切换效果
2016/05/08 Javascript
jQuery实现对无序列表的排序功能(附demo源码下载)
2016/06/25 Javascript
详解使用nvm管理多版本node的方法
2017/08/30 Javascript
帝国cms首页列表页实现点赞功能
2017/10/30 Javascript
vue 每次渲染完页面后div的滚动条保持在最底部的方法
2018/03/17 Javascript
js动态设置select下拉菜单的默认选中项实例
2018/08/21 Javascript
vue实现随机验证码功能(完整代码)
2019/12/10 Javascript
Vue-cli3生成的Vue项目加载Mxgraph方法示例
2020/05/31 Javascript
vue实现防抖的实例代码
2021/01/11 Vue.js
python kmeans聚类简单介绍和实现代码
2018/02/23 Python
python机器学习之随机森林(七)
2018/03/26 Python
更新pip3与pyttsx3文字语音转换的实现方法
2019/08/08 Python
Python Django 添加首页尾页上一页下一页代码实例
2019/08/21 Python
python读取dicom图像示例(SimpleITK和dicom包实现)
2020/01/16 Python
分享CSS3中必须要知道的10个顶级命令
2012/04/26 HTML / CSS
结合CSS3的新特性来总结垂直居中的实现方法
2016/05/30 HTML / CSS
能否解释一下XSS cookie盗窃是什么意思
2012/06/02 面试题
大学生创业计划书的范文
2014/01/07 职场文书
村主任个人对照检查材料
2014/10/01 职场文书
中学生的1000字检讨书
2014/10/11 职场文书
2014年全国法制宣传日宣传活动方案
2014/11/02 职场文书
小爸爸观后感
2015/06/15 职场文书