居中为什么用transform,而不是margin top/left

transform

transform 属于合成属性(composite property),对合成属性进行 transition/animation 动画将会创建一个合成层(composite layer),这使得被动画元素在一个独立的层中进行动画。通常情况下,浏览器会将一个层的内容先绘制进一个位图中,然后再作为纹理(texture)上传到 GPU,只要该层的内容不发生改变,就没必要进行重绘(repaint),浏览器会通过重新复合(recomposite)来形成一个新的帧。

margin top / left

top/left属于布局属性,该属性的变化会导致重排(reflow/relayout),所谓重排即指对这些节点以及受这些节点影响的其它节点,进行CSS计算->布局->重绘过程,浏览器需要为整个层进行重绘并重新上传到 GPU,造成了极大的性能开销。

小总结

对布局属性进行动画,浏览器需要为每一帧进行重绘并上传到 GPU 中
对合成属性进行动画,浏览器会为元素创建一个独立的复合层,当元素内容没有发生改变,该层就不会被重绘,浏览器会通过重新复合来创建动画帧

以下通过模拟测试在chrome上检测动画的FPS

在这里插入图片描述
左方的图片是通过改变元素top/left进行动画的帧率,而右方则是调用translate函数的帧率。我们可以明显看出左图的每一帧都执行了相对较长时间的paint,在每一帧内,cpu都需要计算该元素的其他样式,特别是相对需要复杂计算的盒阴影,渐变,圆角等样式,最后都需要将这些样式应用到该元素内。从这个角度看,如果对于较为老旧的移动设备进行相对复杂的动画,那么效果肯定不理想。
而通过调用translate,会启动硬件加速,即在GPU层对该元素进行渲染。这样,GPU就会相对解放出来进行其他的计算,GPU对样式的计算相对较快,且保证较大的帧率。我们可以通过2d和3d的transform来启用GPU计算。

居中为什么用transform,而不是margin top/left

我们主要还是从浏览器渲染的性能方面考虑。浏览器渲染过程我们知道,浏览器中有JS引擎和渲染引擎,对于HTML页面的渲染就靠渲染引擎来完成。下面是chrome浏览器页面渲染的整体过程图:
在这里插入图片描述

Chrome渲染主要包括

  • Parse Html----html解析
  • Recalculate Style----查找并计算样式
  • Layout-----排布
  • Paint-----绘制
  • Image Decode----图片解码
  • Image Resize----图片大小绘制
  • Composite Layers----合并图层并输出页面到屏幕

浏览器最终渲染出来的页面,跟Photoshop有点类似,是由多个图层合并而来。

补充transform的原理

transform是通过创建一个RenderLayers合成层,拥有独立的GraphicsLayers。每一个GraphicsLayers都有一个Graphics Context,其对应的RenderLayers会paint进Graphics Context中。合成器(Compositor)最终会负责将由Graphics Context输出的位图合并成最终屏幕展示的图案。满足如下条件的RenderLayers,会被认为是一个独立的合成层:有3D或者perspective transform的CSS属性的层video元素的层canvas元素的层flash对opacity和transform应用了CSS动画的层使用了CSS滤镜(filters)的层有合成层后代的层同合成层重叠,且在该合成层上面(z-index)渲染的层如果RenderLayer是一个合成层,那么它有属于它自己的单独的GraphicsLayer,否则它和它的最近的拥有GraphicsLayer的父layer共用一个GraphicsLayer。由此可见,transform发生在Composite Layer这一步,它所引起的paint也只是发生在单独的GraphicsLayer中,并不会引起整个页面的回流重绘。

层创立的条件如下:
  • 3D 或透视变换 CSS 属性
  • 使用加速视频解码的 元素
  • 拥有 3D (WebGL) 上下文或加速的 2D 上下文的 元素
  • 复合插件(如 Flash)
  • 进行 opacity/transform 动画的元素
  • 拥有加速 CSS filters 的元素
  • 元素有一个包含复合层的后代节点(换句话说,就是一个元素拥有一个子元素,该子元素在自己的层里)
  • 元素有一个 z-index 较低且包含一个复合层的兄弟元素(换句话说就是该元素在复合层上面渲染)
GPU

我们经常会听到GPU会加速渲染,那GPU在这里又扮演什么角色呢?前面说到,合成器会负责将层合成绘制为最终的屏幕画面。在硬件加速体系结构,合成由GPU负责。在chrome浏览器多进程模型中,有一个专门的进程来负责传递Render进程的命令,即GPU进程。Render进程和GPU进程是通过共享内存传递的。Render进程可以快速的将命令发给命令缓冲区,并且返回到CPU密集的render活动中,留给GPU进程去处理这些命令。我们可以充分利用多内核机器上的GPU进程和CPU进程。这也是为什么GPU会加速渲染,使transform渲染速度更快的又一原因。4. margin top/leftmarign:外边距,定义元素周围的空间;简言之,可以改变元素的位移。在浏览器页面渲染的时候,margin可以控制元素的位置,也就是说,改变margin,就会改变render tree的结构,必定会引起页面layout回流和repaint重绘。因此,从浏览器性能考虑,transform会比margin更省时间。但是,transform真的处处适用吗?5. transform的局限性上面提到,transform实际上也是用到了GPU加速,也就是说占用了内存。由此可见创建GraphicsLayer,虽然洁身了layout,paint阶段,但Layer创建的越多,占用内存就会越大,而过多的渲染开销会超过性能的改善。因此,当且仅当需要的时候,才会为元素创建渲染层。

对元素进行动画的一些要点:
  • 1,尽量使用keyframes和transform进行动画,这样浏览器会自身分配每帧的长度,并作出优化
  • 2,如果非要使用js来进行动画,使用requestAnimateFrame
  • 3,使用2d transform而不是改变top/left的值,这样会有更短的repaint时间和更圆滑的动画效果
  • 4,移动端的动画效果可能会比pc端的差,因此一定要注意性能优化,尽量减少动画元素的DOM复杂性,待动画结束后异步执行DOM操作

参考:
https://juejin.im/post/5c32b0fb6fb9a049ac7950d9
http://www.cnblogs.com/accordion/p/4593576.html
https://www.zhihu.com/question/33629083
https://blog.csdn.net/qq_33988065/article/details/80371006

  • 4
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
CSS 用定位可以通过设置元素的 `position` 属性来实现。常用的定位属性有 `relative`、`absolute`、`fixed` 三种。 - `relative`:相对定位,元素本身的位置不会改变,但是可以通过设置 `top`、`bottom`、`left`、`right` 四个属性来调整元素相对于原来位置的偏移量。 - `absolute`:绝对定位,元素相对于最近的已定位祖先元素(如果没有已定位的祖先元素,则相对于 body 元素)进行定位,可以通过设置 `top`、`bottom`、`left`、`right` 四个属性来调整元素的位置。 - `fixed`:固定定位,元素相对于浏览器窗口进行定位,可以通过设置 `top`、`bottom`、`left`、`right` 四个属性来调整元素的位置。 下面是一个使用绝对定位实现左右居中的例子: ```html <div class="container"> <div class="box">Hello, world!</div> </div> ``` ```css .container { position: relative; } .box { position: absolute; left: 50%; transform: translateX(-50%); } ``` 在这个例子中,`.container` 元素使用相对定位,`.box` 元素使用绝对定位。`left: 50%` 让 `.box` 元素的左边缘距离 `.container` 元素的左边缘的距离等于 `.container` 元素宽度的一半,这样就把 `.box` 元素的左边缘放到了 `.container` 元素的中间位置。`transform: translateX(-50%)` 通过水平方向上的负偏移量,把 `.box` 元素向左移动了它自身宽度的一半,使得它的中心点正好在 `.container` 元素的中心位置,从而实现了左右居中的效果。 通过 margin 实现左右居中同样也很简单,只需要在元素的样式中设置 `margin: 0 auto` 即可: ```css .box { margin: 0 auto; } ``` 这个例子中,`margin: 0 auto` 把 `.box` 元素的上下边距都设置为 0,左右边距都设置为自动,从而实现了左右居中的效果。注意,这种方式只适用于块级元素,如果要对行内元素进行左右居中,可以给它们的父元素设置 `text-align: center`,然后再把它们自身设置为 `display: inline-block`。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值