小程序换肤

导语 换肤,对于前端来讲不算常见,却也确定不陌生。可是你们有考虑太小程序端的换肤吗?今天咱们就来聊一聊小程序的换肤。javascript

前言

有这么一句老话说得好“人靠衣装佛靠金装”,应用的UI风格的重要性犹如一我的的装扮风格。一个应用通过UI设计师们的精心“打扮”一样能为App赚很高的“回头率”。css

就像女人的衣柜里永远少一件衣服同样,一个应用可能也少一套皮肤,这里就涉及到换肤了。提及换肤,对于前端来讲不算常见,却也确定不陌生。所谓的换肤,无非就是颜色值的更换,在通常的前端项目中,实现的方法有不少种。可是你们有考虑太小程序端的换肤吗?!!html

能够看看 Elementui 换肤 Demo:elementui.github.io/theme-previ…前端

换肤需求

通常来讲换肤需求分两种:java

  • 一种是静态换肤,提供几种可选择的颜色/主题样式,进行选择切换,通常可供选择的主题样式不会太多;
  • 另外一种是动态换肤,可自定义色值,可经过取色板取色或者后端接口下发,可选择的范围比较大;

传统前端换肤方案

在聊小程序的换肤方案以前,咱们大概看一下通常前端项目常见的换肤方案以及优缺点:git

一、class 命名空间

这个应该是最简单的换肤方案,利用class 名称准备两个主题github

.red-theme {
    color: red
}

.blue-theme {
    color: blue
}
复制代码

根据所选皮肤,给标签添加对应的类:chrome

<body class="red-theme">

    <p>红色主题 p>
     
    ...
<body>
复制代码

优缺点:

  • 优势:简单,好理解,好实现
  • 缺点:CSS中需多写主题的class,代码容易混乱;需手动编写

2. 生成多套CSS皮肤

利用CSS预处理语言(如:Less,stylus 或 sass)以及 Webpack、gulp等工具输出多套主题样式。gulp

/** default-theme.css **/

.text {
 color: #333;
}

/** red-theme.css **/

.text {
 color: red;
}

/** blue-theme.css **/

.text {
 color: blue;
}
复制代码

页面加载后,根据用户需求经过js动态的link对应的皮肤样式。小程序

// js动态处理
 var theme = /\bt=(\w+)/.exec(location.search);
 theme = theme ? theme[1] : "light";

 changeTheme(theme);

function changeTheme(theme) {
    var head = document.getElementsByTagName("head")[0];
    var link = document.createElement("link");
    link.dataset.type = "theme";
    link.href = "assets/css/theme-" + theme + "/pages/home/home.css";
    link.rel = "stylesheet";
    link.type = "text/css";
    head.appendChild(link);
}
复制代码

若是须要保存用户使用的主题,能够经过以下方式:

  • 利用路由标记
  • 利用cookie标记
  • 利用localstorage
  • 保存到后端服务器

优缺点:

  • 优势:简单,好理解,好实现
  • 缺点:须要手写两份以上CSS配色样式;切换样式须要下载CSS的时间

Tips: 动态加载CSS文件可能需求必定的等待时间,可根据HTML 的 rel 属性下的 alternate配合 link 的 disabled 实现必定优化。

3. CSS变量换肤

利用CSS变量设置颜色, 用js动态修改CSS变量,进而换色。若是不考虑兼容性,这是最佳换肤方案。

// variable.less
:root {
  --fill-1: #fff;
  --text: #3c3c3c;
  --text-1: #757575;
  --text-2: #222;

  --font-size-large: 18px;
  --font-size-large-x: 22px;
  --font-size-medium: 14px;
  --font-size-medium-x: 16px;
  --font-size-small-s: 10px;
  --font-size-small: 12px;
}
复制代码

在页面对css变量作引入使用:

// 页面使用
@import "../../assets/less/variable.less";

.header {
  position: relative;
  height: 70px;
  text-align: center;
  font-size: 0; 
  .text {
    display: inline-block;
    vertical-align: top;
    line-height: 70px;
    font-size: var(--font-size-large);
    color: var(--text-2);
  }
}
复制代码

而后在页面中能够直接经过JavaScript修改变量的值

function changeColor(color = 'blue') {
 document.documentElement.style.setProperty("--theme-color",color);
}
复制代码

优缺点:

  • 优势:只需一套CSS文件;换肤不须要延迟等候;对浏览器性能要求低;可自动适配多种主题色;
  • 缺点:不支持IE, 2016年前的chrome,safari; 兼容性参见 Can I Use CSS Variables

4. Less 在线编译

使用 modifyVars()方法, 基于 less 在浏览器中的编译来实现。在引入less文件的时候须要经过link方式引入,而后基于less.js中的方法来进行修改less变量:

less.modifyVars({
  '@themeColor': 'blue'
});
复制代码

link方式引入主题色文件:

<link rel="stylesheet/less" type="text/css" href="./src/less/public.less" />
复制代码

小程序换肤方案

本文方案均以 less、gulp 为基本框架。

背景

在开发小程序的时候,尤为是开发第三方小程序,咱们做为开发者,只须要开发一套模板便可,可是个别客户的小程序须要作定制化配色方案,也就是说,不一样的小程序个体须要对页面的元素(好比:按钮,字体等)进行不一样的配色设置。

方案以及问题

因为小程序它自身的技术特色,传统方案的 CSS变量以及 Less在线编译 换肤方案没法使用,因此小程序换肤方案主要是:

  1. 若是没有线上存在多套皮肤的需求,能够抽取颜色变量经过线下编译修改主题色。
  2. 若是有线上多套皮肤的需求,则采用传统前端的多套CSS皮肤方案加更改类名的方式。
  3. 针对动态换肤,后端接口返回色值字段,前端经过 内联 方式对页面元素进行色值设置。

这几种方案都有一些问题没法避免:

  • 方案一、2 比较死板,每次更改主题样式都须要发版小程序,若是主题样式变更不大,能够考虑这种;
  • **方案3 对于前端的改动很是的大,*内联*也就是经过 style 的方式内嵌到 wxml 代码中,代码的阅读性会变差,可是能够解决主题样式变更不用发版小程序的问题

方案一

针对方案一,咱们只须要抽取相关的变量色值到独立的文件中,约定项目在使用色值的地方统一引用该文件的变量。当须要修改主题色的时候修改对应变量便可。

/** variable.less **/
@theme-color: #FD7622;

@txt-default: #333;
@txt-body: #666;
@txt-info: #999;
@txt-muted: #ccc;
@txt-warning: #FF0500;
@txt-highlight: @theme-color;
@txt-link: #00a5e0;
@txt-feeds: #314c83;
@txt-white: #fff;
复制代码

在编译阶段,经过 gulp-less的 modifyVars属性修改相关变量便可:

// gulpfile.js
var gulp = require('gulp');
var less = require('gulp-less');
var rename = require('gulp-rename');

function lessTask() {
  return gulp.src('./less/**/*.less')
             .pipe(less({ 
                 modifyVars: {
      '@theme-color': '#757575',
      '@txt-default': '#212121',
     }
       }))
             .pipe(rename(function(path) {
               path.extname = '.wxss'
             }))
             .pipe(gulp.dest('./wxss'))
}

function autosTask() {
  gulp.watch('./less/**/*.less', lessTask)
}

exports.default = gulp.series(gulp.parallel(lessTask, autosTask))
复制代码

方案二

这个方案咱们须要定制多套主题变量,并编译出多套皮肤样式。

主题色变量配置文件

/** variable.less **/

#theme() {
  .colors(dark) {
    @theme-color: #000;
  }
  
  .colors(light) {
    @theme-color: #fff;
  }
}
复制代码

页面样式文件

@import 'variable.less'

.dark {
  @colors: #theme.colors(dark);
  
  .btn {
      .btnMixin;
  }
}


.light {
  @colors: #theme.colors(light);
  
  .btn {
    .btnMixin;
  }
}


.btnMixin() {
  background: @colors[@theme-color];
}


/** 输出 .dark .btn { background: #000; } .light .btn { background: #fff; } **/

复制代码

页面中使用的方式

1.页面的 wxml 引入主题变量 theme

<view class="index-layout {{theme}}">
 <button class="btn">按钮button>
view>
复制代码

2.经过页面中的 this.data.theme 来控制主题

page({
  data: {
    theme: ''
  },

  themeChange(e) {
    const { theme } = e.target.dataset
    this.setData({ theme })
  }
})
复制代码

上面两个方案到目前为止也只是解决了在less文件中的换色问题,而实际的项目中咱们不少时候并不能避免一些色值是内联写在 wxml 上以及写死在 javascript 文件中的。

好比,下面的 radio 组件

<radio value="light" checked="true" color="#fd7622" />
复制代码

这种状况下一样抽取出一个颜色变量的 wxs文件在 wxml 使用,如

// variable.wxs
var themeColor = {
  dark: {
    '@theme-color': '#333',
  },
  light: {
    '@theme-color': '#fd7622',
  },
};


function getVariable(theme) {
    return themeColor[theme]
}

module.exports = getVariable;
复制代码
<wxs src="@wxsVar" module="getVariable" />

<radio value="light" checked="true" color="{{getVariable(theme)['@theme-color']}}" />
复制代码

js 文件同理,这里再也不复诉。

方案三

小程序中要实现动态换肤,目前能想到的办法就是在涉及到颜色设置时经过 **内联(设置 style)**方式对页面元素进行色值设置。这种方法目前来讲成本较高,对于已经成型的项目来讲风险过大。

wxml设置颜色时咱们一样能够经过 wxs来实现。

function getStyle(style, theme) {
    return style + ':' + theme;
}


function setStyle(styles = [], theme) {
    if (!styles || !styles.length) return '';

    var styleArr = []
    styles.forEach(function(style) {
        styleArr.push(getStyle(style, theme))
    })

    return styleArr.join(';');
}

module.exports = setStyle;
module.exports = setStyle;
复制代码

wxml 中使用

<wxs src="@dynamic" module="setStyle" />

<view class="container">
  <view class="box" style="{{ setStyle(['color', 'border-color'], theme) }}">
    setStyle(['color', 'border-color'])
  view>
  <input type="text" placeholder="输入一个色值" style="{{ setStyle(['border-color'], theme) }}" bindconfirm="onChange"/>
view>
复制代码
Page({
  data: {
    theme: '#222',
  },

  onChange(e:{detail: {value: string}}) {
    this.setData({
      theme: e.detail.value,
    })
  }
})
复制代码

示例

这里给一个简单的示例。

一个简单的Demo

最后

因为小程序的特殊性,在换肤这种需求中局限性仍是很大的。以上只是给你们提供一下一些解决思路,若是你们有更好的方案的话欢迎留言。

原做者:梁忠玲

未经赞成,禁止转载!

相关文章
相关标签/搜索