• 利用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几张题头的图片换上。
    总体效果当然和专业美工做的当然没法比,不过做做业务管理系统忽悠忽悠客户已经足够了。

  • 相关阅读:
    Maven+SSM框架搭建【spring+springmvc+mybatis】
    [福大软工] W班 总成绩排行榜
    项目Beta冲刺团队随笔集
    45度炸队Alpha冲刺博客集
    SDN期末验收
    小黄衫——共同的荣誉
    软件工程实践总结作业
    SDN第五次上机作业
    SDN第四次作业
    总结随笔
  • 原文地址:https://www.cnblogs.com/xqin/p/3455068.html
Copyright © 2020-2023  润新知