小程序換膚

導語 換膚,對於前端來講不算常見,卻也確定不陌生。可是你們有考慮太小程序端的換膚嗎?今天咱們就來聊一聊小程序的換膚。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

最後

因爲小程序的特殊性,在換膚這種需求中侷限性仍是很大的。以上只是給你們提供一下一些解決思路,若是你們有更好的方案的話歡迎留言。

原做者:梁忠玲

未經贊成,禁止轉載!

相關文章
相關標籤/搜索