动态换肤的原理和方案
皮肤是什么
产品的ui 是前端按照设计师的设计稿
,通过样式
组合一系列字体、图片、视频等资源,经浏览器渲染引擎解析后渲染
出的。
皮肤就是替换其中一部分的 样式、字体、图片、视频等。
如何切换皮肤
切换皮肤就是切换样式。
可能有同学会觉得只替换图片、字体等资源不行么?是不行的,因为打包的过程中有的资源是独立的,有的是嵌入到了样式中,没法替换。
现在项目的css样式是直接嵌入到页面中的,通过在head里面生成各种style标签来内嵌,这种情况下,如果想替换样式的必须得覆盖之前的,而样式本身是有优先级的,组件样式使用了scoped的方式,优先级较高,想覆盖得通过!important来设置样式优先级。
这种方式太过hack,不是正确和优雅的方式。更合理的方式是把样式从页面中分离出来,打包到单独的css样式文件中,然后直接替换css文件来实现切换皮肤效果。
皮肤样式的管理
我们项目中的样式是通过less管理的,可以定义一系列的变量,可以把皮肤涉及到的各种背景颜色,各种字体的大小等魔法值通过变量存储,集中管理,类似这样:
@loginBg: blue;
@loginBtnFontSize: 14px;
这个文件就是皮肤样式的文件,比如秋天皮肤的配置文件可以叫autumn-skin-variables.less,在总配置variables.less中引用不同的皮肤less文件,使用不同的变量,来实现皮肤的切换。
动态换肤
应用皮肤可以通过发版,静态替换css。也可以动态的换肤,每次启动请求服务端皮肤的接口,服务端会返回皮肤的版本号,和本地的皮肤版本做对比,若一致则忽略,如果不一致,则下载最新的样式替换本地的,同时更新本地版本号。这就实现了动态的换肤。
甚至,可以做实时的换肤,服务端通过websocket推送过来最新的皮肤文件,然后程序自动应用,就像投屏的技术方案一样。
下载新的样式文件需要消耗一些时间,这个过程可以在ui上展示一个进度条或loading的图片,提示用户正在换肤。也可以不做任何提示,下载完成后自动应用。
全量样式包 vs 部分皮肤样式包
上面说的是全量样式包的方案,就是完整的替换css,这样的方式简单粗暴,但是下载样式的过程长一些。
也可以只把皮肤相关的样式单独打包,来替换本地的皮肤样式,这样下载会快一些,但是写代码的时候要把皮肤相关样式摘出来写,类似下面这样,否则还是会有优先级问题,导致难以覆盖。
.login{
background: @loginBg;
}
.login-btn{
font-size: @loginBtnFontSize;
}
全量包的方式 简单,但是体积略大。
皮肤样式单独打包的方式 略复杂,但是体积小。
图片、视频等资源的处理
登录页有很多视频,每个视频的位置和宽高都不同,除了样式的部分,视频的dom元素也要动态生成。dom中的图片等资源也是。
服务端的皮肤接口可以返回一个json,类似
{
version: '2.0.0',
loginVideos: [
{width:300,height:400, left: 400, top: 200, src:'http://xxx.com/xx.mp4'},
{width:300,height:400, left: 400, top: 200, src:'http://xxx.com/xx.mp4'},
],
css: "xxxxxxxx"
}
把版本号,视频,样式分开,客户端通过一个SkinInstaller来替换css,生成视频的dom元素,
原理图如下:
皮肤相关规范
皮肤就是样式和图片、视频、字体等的集合,可以修改的地方很多,但是为了快速开发和减少测试量,建议不要动位置、布局等,只替换相关的颜色、背景等资源和变量。
总结
动态换肤就是动态的切换css,可以是全部替换或者部分替换的方式,涉及到的dom元素需要动态生成。服务端可以返回一个描述皮肤的信息的json,客户端通过解析json来做一系列的操作。
皮肤样式基于less,把相关的变量集中到一个皮肤文件中管理,如果是部分替换的方式,还要把相关的样式摘出来以单独打包。