展新:企业CSS应用
支付宝前端发展过程:
很早之前的支付宝的时候只有pa.css, 解决基础样式、重用等功能
=> 后来超过了6000行/100k,全局样式太多,重置困难,烂代码积压
=> 2011年的时候出现了前端解决方案: CSS样式规范,浏览器解决方案: 例如: 解决1px圆角,解决ie6下的浮动 形成了 Alice V3 写样式有道可寻,可维护性强..文件进行拆分,最基础只有20k 但是随着 交互组件缺乏,工程化不足,产品差异化越来越多
=> 出现了AliceUI 模块化命名 和组织方式,强大的工具支持-spm等 CSS3新技术使用 但是视觉规范多变,企业平台业务据增,新技术发展。 所以很多前端技术可能刚开始的使用是放在后台去使用。
(1)在图标设计规范:
在设计iconfont字体的时候,比如在16*16px的设计图中,内部的icon的大小应该是在14*14 或则 15*15,而不是16*16
1. 尽可能地接近普通文字的font-size
2. 视觉上不同的形状做适当的调整,以保证[看起来]都差不多大
3. 图标的线条的粗细保证是像素格的整数倍
(2)iconfont的命名上也是非常讲究的:
一般命名的规范: name - type - direction
比如: caret-circle-left
caret-circle-o-right
caret-o-down
caret-up
(3)色彩中的学问
固定必须要知道的色谱,
基础的色谱: active / link / hover / outline
固定的色谱: 标题颜色, 正文的颜色,辅助的颜色
提示性色谱: 出错的颜色, 成功的颜色, 警告的颜色
郑海波: CSS预处理器
为什么要写css预处理器.?
CSS的"罪":
变量
函数
逻辑/循环
前缀
运算/表达式
CSS缺乏是抽象能力。
抽离细节
减少关注点
CSS中的抽象:CASCADING
<div class='m-layer m-layer-bg m-layer-primary z-show'>
Cascading 是CSS的灵魂, 也是限制预处理能力的 “关键点”
</div>
.m-layer{...}
.m-layer-bg{...}
.m-layer-primary{...}
.z-show{...}
预处理器:处理特定格式源文件到目标CSS的处理程序。
// 编译前
$color-primary = #de12ec;
.m-layer{
background-color: $color-primary;
&:hover{
background-color: l-adjust($color-primary, -10%);
}
}
// 编译后
.m-layer{
background-color:#de12ec;
}
.m-layer:hover{
background-color:#b10ebd;
}
预处理器青铜时代: LESS-SCSS-Stylus
nav {
ul {
margin: 0;
padding: 0;
list-style: none;
}
li { display: inline-block; }
a {
display: block;
padding: 6px 12px;
text-decoration: none;
}
}
优点:
对前端更友好
不基于缩进,不简洁但更安全
与自定义DSL同样强大的语言能力
可以直接使用css书写
预处理器钢铁时代(纯CSS语法)
严格遵守CSS语法的提供额外能力的CSS预处理器
postcss: https://github.com/postcss
rework: https://github.com/reworkcss
postcss简介:
npm install postcss --save-dev
postcss其实只是个平台,它提供解析,AST便利操作。
SourceMap和翻译器,具体功能由插件实现。
官方案例:
var postcss = require('postcss');
postcss([
// 允许你使用"阉割版"的未来的CSS特性
require('cssnext')(),
// 一个高级的css压缩器
require('cssnano')()
])
.process(css, { from: 'src/app.css', to: 'app.css' })
.then(function (result) {
// 写入变换后的css文件
fs.writeFileSync('app.css', result.css);
// sourmap文件
if ( result.map ) fs.writeFileSync('app.css.map', result.map);
});
POSTCSS正在渐渐成为预处理器中的jQuery;
// 编译前before compile
.m-nav{
.list{
padding: 10px;
}
.link{
color: red;
&:hover{
color: green;
}
}
}
// 编译后 after compile
.m-nav .list{
padding:10px;
}
.m-nav .link{
color:#f00;
}
.m-nav .link:hover{
color:#008000;
}
Nesting建议嵌套尽量不要超过3层
1. 使用选择器提升
.m-layer{ // 一般这个表示的是一个组件对应的样式,但是嵌套多层不好
.cnt{
.nav{
.bar{
padding: 10px;
}
}
}
}
=>
.m-layer{ // 这样的选择依然是唯一的,但是不用嵌套多层
.layer_bar{
padding: 10px;
}
}
2 避免深层选择器序列
// 不好的实践
div, p, span, ul{
div, p, span, ul{
div, p, span, ul{
left: 10px;
}
}
}
变量的最佳实践:
- 变量名要达意
// BAD
$fs1 = 14px;
$fs2 = 16px;
$fs3 = 18px;
$fc0 = #253443; // 主色1
$fc1 = #153c3d; // 主色2
$fc2 = #777 ; // 灰色
// GOOD
$fsize-base = 14px;
$fsize-small = 12px;
$fsize-big = 16px;
$color-primary = #253443;
$color-inverse = #153c3d;
$color-gray = #777;
2 全局变量集中管理
Rule更易于迁移
变量更易于查找管理
3 私有变量声明在模块内
.m-module{
$gap = 32px;
.side{
margin-right: $gap;
}
}
4 必须统一管理z-index
// z-index config:
$modal-index = 1000;
$dropdown-index = 100;
$mask-index = $modal-index - 10;
施峰峰:移动下的布局
vw: viewport 宽度的1%
vh: viewport 高度的1%
vmin: viewport Math.min(高度, 宽度)的1%
vmax: viewport Math.max(高度, 宽度)的1%
案例如下:
<div>按钮内容</div>
div{
height: 10vw;
width: 50vw;
line-height: 10vw;
font-size: 5vw;
text-align: center;
color: #fff;
background: red;
margin: auto;
}
css: 神奇的padding/margin-top;子元素的padding-top百分比基准是父元素的宽度。
.papa {
width:100px;
height: 300px;
background-color: #ddd;
overflow: hidden;
}
.child {
width: 50px;
height: 50px;
background-color: red;
margin-top: 100%; // 这里的值是父元素的宽度
}
结合使用rem和@media
html {
font-size:62.5%;
}
body {
font-size: 1.6rem;
}
h1 {
text-align: center;
font-size: 3rem;
}
p {
text-align: center;
font-size: 1.8rem;
}
@media all and (max-width: 700px) {
html {
font-size: 60%;
}
}
@media all and (max-width: 608px) {
html {
font-size: 52%;
}
}
@media all and (max-width: 533px) {
html {
font-size: 45%;
}
}
黄薇(瓜瓜):高性能CSS动画
讲讲web浏览器渲染过程:
1 通过一个get请求,获取一个html页面
2 分析html结构生成DOM树
3 Recalculate Style:分析CSS,浏览器让DOM+CSS生成了叫渲染树(render tree)
4 layout 将渲染树中每个节点,根据宽度高度模型相关的属性来计算它的位置和大小
5 Paint 开始涂鸦,把每个区域涂鸦完毕生成一个位图
6 Composite Layers 浏览器将位图从CPU 传送到 GPU 然后渲染到屏幕上,完成一次渲染工作
从图中可以看出,在width,height,margin等是处于Layout阶段。以及box-shadow处于paint阶段,transform处在Composite Layers阶段。
所以在改变CSS样式属性的时候会触发浏览器中的哪个阶段呢,当触发了某个阶段的执行,同时也会触发该阶段后面的执行。举例触发了Layout阶段,那么Paint和Composite Layers阶段也一样要触发。所以 绘制的阶段,触发的时间越早,消耗的代价越大。
渲染小结
- 渲染主要三阶段: Layout计算范围,Paint计算展现,Composite合成Bitmap
- 修改不同CSS属性会触发不同阶段
- 触发的阶段越前,渲染的代价越高
硬件加速
硬件加速也称为GPU加速。
- 术语: texture。 可看做一个放在GPU上的位图
- GPU擅长队texture进行偏移,放大缩小,旋转,更改透明度(意思是处理的特别快,时间短)
浏览器是如何使用GPU的呢?
浏览器中有个Layer模型
- 浏览器会根据CSS属性为元素生成Layers
- 将Layers作为texture上传到GPU
- 当改变Layer的transform,opacity属性时,渲染会跳过Layout,paint;直接通知GPU对Layer做变换。
为什么使用硬件加速实现动画快?
(1)首先,当使用jquery实现div向下移动的动画时。浏览器是做了那些事情?
这里每一次top新增1px之后都会触发浏览器重排和重绘制。
(2)使用硬件加速,就可以使用CSS3中的Animate transform
然而这里是直接触发了Composite Layers阶段,减少了重排和重绘的过程。
节省了CPU进行Layout,Paint的时间,CPU向GPU传输位图的时间。
fps
对人类的眼睛来说,30FPS+是感觉流畅的,60FPS(1s显示60张图片)会更加舒服。
所以为了使得舒服顺滑的动画,就必须在1/60FPS,约等于16.7ms内,把这一帧准备好。
渲染时机的选择:
(1)setTimeout(callback, 1/60)来实现动画
缺点:
- 依靠浏览器内置时钟更新频率,然后ie等浏览器更新的间隔时间不一样
- main thread队列,如果上个帧的动画展示出错那么会影响下一帧的动画。
(2)HTML5的出现:requestAnimationFrame(更好的选择)
- 定义绘制每一帧前的工作。 requestAnimationFrame(callback)
- 自动调节频率。callback工作太多无法在一帧内完成,会自动降低为30FPS, 虽然频率降低但比丢帧好。
渲染一帧的时间:
目标:16ms
触发Layout:
- 改变width, height, margin等和大小、位置相关的属性
- 读取size, position相关得属性(因为一个页面中有多个脚本,有时候很多脚本就是改变了width和height,为了页面的正确性,所以当读取这些属性的时候会触发Layout阶段)
相关属性有:
clientHeight, clientLeft, clientTop, clientWidth, focus(), getBoundingClientRect(), getClientRects(), innerText, offsetHeight, offsetLeft, offsetParent, offsetTop, offsetWidth, outerText, scrollByLines(), scrollByPages(), scrollHeight, scrollIntoView(), scrollIntoViewIfNeeded(), scrollLeft, scrollTop, scrollWidth .....
尽量不要去触发Layout: 使用transform代替top, left的动画
// 频繁Layout
var h1 = element1.clientHeight;
element1.style.height = (h1 * 2) + 'px';
var h2 = element2.clientHeight;
element2.style.height = (h2 * 2) + 'px';
var h3 = element3.clientHeight;
element3.style.height = (h3 * 2) + 'px';
读取clientHeight属性的时候,就会出发Layout,比如上图,读取h2的时候无法确认h1的位置有没有变化,所以会发生Layout。
// 分离读取操作
// Read
var h1 = element1.clientHeight;
var h2 = element2.clientHeight;
var h3 = element3.clientHeight;
// Write
element1.style.height = (h1 * 2) + 'px';
element2.style.height = (h2 * 2) + 'px';
element3.style.height = (h3 * 2) + 'px';
这样浏览器会先将数据取出来,然后再下面才会进行改变,触发一次Layout。
事实上,两段代码的实际写法如下:
document.body.addEventListener('click', function() {
var h1 = element1.clientHeight;
element1.style.height = (h1 * 2) + 'px';
});
document.body.addEventListener('click', function() {
var h2 = element2.clientHeight;
element2.style.height = (h2 * 2) + 'px';
}); // 在这里无法将两次的操作读写放在一起
所以使用requestAnimationRequest推迟执行
document.body.addEventListener('click', function() {
// Read
var h1 = element1.clientHeight;
// Write
requestAnimationFrame(function() {
element1.style.height = (h1 * 2) + 'px';
});
});
document.body.addEventListener('click', function() {
// Read
var h2 = element2.clientHeight;
// Write
requestAnimationFrame(function() {
element2.style.height = (h2 * 2) + 'px';
});
});
// 使用了requestAnimationFrame使得在第一帧的时候只会去读取h1和h2这两个数据的值,
// 而在下一帧的时候才回去去触发Layout
触发Paint
当修改border-radius,box-shadow,color等展示相关属性时,会触发paint
减少不必的绘制: gif图即使被其他Layer盖住不可见,也可能导致paint,不需要时应将gif图的display属性设为none。
减少绘制区域:为引起大范围Paint的元素生成独立的Layer以减小Paint的范围
附上中国第二届CSS开发者大会地址
https://www.w3ctech.com/topic/1463