利用CSS預處理技術實現項目換膚功能(less css + asp.net mvc4.0 bundle)


一、背景

    在越來越重視用戶體驗的今天,換膚功能也慢慢被重視起來。一個web系統用戶可以選擇一個自己喜歡的系統主題,在用戶眼里還是會多少加點分的。我們很開心的是easyui v1.3.4有自帶default gray black bootstrap metro五款皮膚,但是它並不像bootsrap提供了很完整的css框架,不能提供項目需要的所有的css,所以還需要自己編寫控件之外的一些css。給系統換膚時,easyui控件都沒問題,問題就在於自己編寫的這部分css怎么實現換膚,當然,最簡單的辦法就是為每一款主題都寫對應的一份自定義css然后在項目中加載,這樣是可以實現。
    但是我覺得這樣有點羅嗦了,當你添加新的css或修改css時,你要同時修改N份css,每一個主題對應一份,而且easyui除了這5款默認的主題還有其它主題或者我們還可以自定義主題,那這樣修改css就更不現實了。所以我們就想到的動態css,也就是css預處理技術。

二、CSS預處理技術

CSS 預處理器技術已經非常的成熟,常用的預處理器框架有:
1、Less 官網:http://lesscss.org/
2、Sass 官網:http://sass-lang.com/
3、Stylus 官網:http://learnboost.github.io/stylus/

我研究比較多的只有less,后兩者也只是了解了下,所以這里我還是選用less來實現

我們先來看看用less帶來了哪些方便
1、用Less我們可以實現用變量去寫css,可以很方便的實現換膚功能(只要改變變量的值即可)

@the-border: 1px;
@base-color: #111;
@red:        #842210;

#header {
  color: (@base-color * 3);
  border-left: @the-border;
  border-right: (@the-border * 2);
}
#footer {
  color: (@base-color + #003300);
  border-color: desaturate(@red, 10%);
}

2、Less提供了很多很有用的函數比如lighten darken fadein fadeout…等等,比如把字體A顏色設置為#1382CE,字體B跟A同一顏色,只是比較淡:

@fontcolor:#1382CE;
.font1 {
    color: @fontcolor;
}
.font2 {
    color: lighten(@fontcolor,30%);
}

3、我們也可以自己定義自己的less函數,比如我們寫一個背景漸變的css,我們可以定義一個漸變的函數,如下:

.gradient(@color: #F5F5F5, @start: #EEE, @stop: #FFF) {
  background: @color;
  background: -webkit-gradient(linear,
                               left bottom,
                               left top,
                               color-stop(0, @start),
                               color-stop(1, @stop));
  background: -ms-linear-gradient(bottom,
                                  @start,
                                  @stop);
  background: -moz-linear-gradient(center bottom,
                                   @start 0%,
                                   @stop 100%);
  background: -o-linear-gradient(@stop,
                                 @start);
  filter: e(%("progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)",@stop,@start));
}

然后我們只需要這樣用:

.head-north
{
    .gradient(colorDefault,colorFrom, colorTo);
}

我就簡單介紹這些,less還有很多用法,大家自己去探究。

三、系統換膚實現思路

當然不光是引入less就完了,事情遠沒有這么簡單,我們知道既然是dynamic css那么一定是需要編譯的,less最終也是只是編譯生成css代碼,那么就有一個問題,什么時候編譯
1、使用編譯工具比如koala、SimpleLess等,在項目發布前編譯好放在項目中
2、前端解析編譯,需要在項目中引入less.js
3、后台動態解析,在java環境下的編譯引擎比較多,.net下好像我就找到一個dotless,而且實現的還不是很完整,只能說是less v1.5的部分實現。

先分析下這三種方式,第一種用編譯工具,就是我發布項目要做的事情變多了,我一向比較懶,喜歡簡單的,萬一我忘記了怎么辦,總覺得是多了一趟事情。
第二種前端實時解析,這種其實是很理想的一種方便,也很方便,但是帶來的一個問題就是前端的效率,如果css少還好說,多了肯定會影響效率的。
第三種呢,后台動態編譯,后台只編譯一次后緩存起來,對服務器基本沒有影響,這樣很好,問題是我這個框架是.net的,是dotless實現不完整,但是我們不一定會用到less所有的功能,有基本功能就夠用了,比如條件判斷等更高級的使用,我們可以在處理前自己先預處理一下,再給less類庫去解析。

好吧,那么我就選擇第三種在后台動態解析了。
具體思路:
1、根據當前用戶選擇的theme取得easyui.css文件並根據特征獲得主題的相關變量@body-background-color或@body-text-color等等,這些變量我在我自定義的css中會常用到,取得這些變量是很容易做到的。
2、利用這些變量,我們可以theme.less中編寫自定義的css
3、利用asp.net mvc4.0中的bundle中來處理less
4、頁面引用輸出css

四、具體實現

1、引入dotless類庫
2、定義easyui中的變量,應該包括以下變量

/*common*/
@border-color
@border-radius
@font-size
@shadow-background-color
@mask-background-color
@toolbar-background-color
@toolbar-border-color
@split-color
@split-proxy-color

/*Header*/
@header-background-color
@header-text-color
@header-gradient-used
@header-gradient-from
@header-gradient-to

/*body*/
@body-background-color
@body-text-color

/*grid*/
@grid-header-background-color
@grid-header-gradient-from
@grid-header-gradient-to
@cell-border-color
@alt-background-color

/*state*/
@selected-background-color
@selected-text-color
@selected-border-color
@hover-background-color
@hover-text-color
@hover-border-color
@invalid-background-color
@invalid-border-color
@invalid-text-color

/*menu*/
@menu-background-color
@menu-text-color
@menu-border-color

/*button*/
@button-background-color
@button-selected-color
@button-text-color
@button-gradient-used
@button-gradient-from
@button-gradient-to
@button-radius
@button-split-color1
@button-split-color2

這些變量要通過用戶的theme取得easyui.css文件並解析這個文件去給這些less變量賦值

3、自定義自己的動態css,下面是我的項目中theme.less文件的片段

.z-body
{
    background:@body-background-color;
}

.z-toolbar,.z-toolbar-dialog{
    border-color:@border-color;
    background:@header-background-color;
}

.z-txt {
    border-color:@border-color;
    background:white;
}
.head-left, .head-right, .head-right a
{ color: $when(@theme=gray,default,black,bootstrap| #fff | #000); } .head-north {
    .gradient(@selected-background-color,@header-background-color, @selected-background-color);
}

.head-south,.head-south a
{
    background:@header-background-color;
    color: lighten(@body-text-color,30%);
}
 
……

4、在項目中的BundleConfig.cs中的RegisterBundles中注冊bundles

using System;
using System.IO;
using System.Web;
using System.Web.Hosting;
using System.Web.Mvc;
using System.Web.Optimization;
using Zephyr.Utils;

namespace Zephyr.Web.Mvc
{
    public class BundleConfig
    {
        public static void RegisterBundles(BundleCollection bundles)
        {
            var dirBase = new DirectoryInfo(HttpContext.Current.Server.MapPath(
                string.Format("~/Content/js/easyui/{0}/themes",AppSettings.EasyuiVersion)));
            var dirs = dirBase.GetDirectories();
            foreach (var dir in dirs)
            {
                if (dir.Name == "icons") continue;
                var theme = dir.Name;
                var themeBundle = new Bundle(string.Format("~/Content/css/theme/{0}", theme)).Include(
                    "~/Content/css/less/elements.less", 
                    "~/Content/css/less/theme.less");
                themeBundle.Transforms.Add(new EasyuiLessTransform(theme));
                themeBundle.Transforms.Add(new LessTransform());
                themeBundle.Transforms.Add(new CssMinify());
                bundles.Add(themeBundle);
            }
        }
    }
}

這里在bundle的Transforms中添加了三個BundleTransform處理,
其中EasyuiLessTransform是我對easyui變量及自定義條件判斷$when的處理
LessTransform則是調用dotless庫解析less代碼

using System;
using System.Web.Optimization;
using dotless.Core;

namespace Zephyr.Web.Mvc
{
    public class LessTransform : IBundleTransform
    {
        public void Process(BundleContext context, BundleResponse response)
        {
            var compiled = Less.Parse(response.Content);
            if (string.IsNullOrEmpty(compiled))
                throw new Exception("less文件中語法有錯誤!");
            response.Content = compiled;
            response.ContentType = "text/css";
        }
    }
}

第三個CssMinify則是System.Web.Optimization下面的對css混淆壓縮處理。

5、在頁面中引用,razor頁面中只需要以下代碼即可

@Styles.Render("~/Content/css/theme/" + AppLoginer.Theme)

至此,換膚功能已完成,我們可以看看效果

五、各種主題下的效果
1、默認主題
image

image

2、gray風格image

image

3、bootstrap風格
image

image

4、black風格,這個好像口味比較重
image

image

6、metro風格,這款很干凈簡潔,我自己很喜歡

image

image

六、后述

這樣一來,這個功能就算是很靈活了,就算是以后再加入一款新主題,代碼也完全不用修改,而且想效果更好點還可以p幾張題頭的圖片換上。
總體效果當然和專業美工做的當然沒法比,不過做做業務管理系統忽悠忽悠客戶已經足夠了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM