CSS转换过渡-使用"px"比“百分比"更平滑/更有效 [英] CSS transform transition - using ´px´ more smooth/performant than 'percentage'

查看:60
本文介绍了CSS转换过渡-使用"px"比“百分比"更平滑/更有效的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我最近一直在研究改善网站上的动画-更具体地说是改善移动设备上的导航下拉菜单.

在这种情况下,我偶然发现了以下情况,希望借此获得更深入的了解.

情况是,当过渡/动画transform: translate3d()时,使用%而不是px应用浏览器时,似乎需要更多的计算.例如.在我的测试中,与从transform: translate3d(0, 100%, 0)过渡相比,从transform: translate3d(0, 500px, 0)过渡到transform: translate3d(0,0,0)所需的计算似乎更少,并且运行更流畅.

更新:经过进一步测试,我发现使用100vh/100vw可以绕过/缓解使用百分比的问题.在元素的窗口宽度百分比已知或为全角的情况下,可以使用它,从而提高性能.实际上,似乎使用此值的行为就像在Chrome中为px值分配了该值.

以下是每个动画中时间轴的几张图片.时间轴是使用性能"下的Google Dev工具获得的.为了更好地显示差异,在Chrome开发工具中,性能已被限制为低端移动设备"(CPU速度降低了6倍).

使用百分比进行转换:

使用像素(px)进行转换:

从图像中可以看出,使用%而不是px确定转换时,似乎正在进行更多的渲染和绘画.浏览器必须计算每一帧的百分比值是很有意义的(我想是吗?),但令我惊讶的是,与使用像素值相比,它花费了更多的钱.

,请注意,显示百分比时间轴的图片中的帧速率从未达到〜60 fps,而平均约为40 fps.

下面是用于复制案件的摘要.有一种使用百分号,另一种使用px.

 $(document).on("click", function(){
$(".bb").toggleClass("active");
}); 

 .aa{
  height:50px;
  background:blue;
  position:fixed;
  top:0;
  width:100%;

}
.bb{
  position:fixed;
  top:0px;
  background:none;
  height:100%;
  width:100%;
  left:0;
  transform:translateZ(0);
  overflow:hidden;
    pointer-events:none;
}
.cc{
  height:100%;
    transform:translate3d(0,500px,0);
    width:100%;
      transition:transform .5s ease-in;
      background:red;
}
.bb.active .cc{
  transform:none;
} 

 <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<p>Click the document to start animation<p>
<div class="bb">
<div class="cc">
<ul>
<li>Point one</li>
<li>Point two</li>
<li>Point three</li>
<li>Point four</li>
<li>Point five</li>
<li>Point six</li>
<li>Point seven</li>
</ul><ul>
<li>Point one</li>
<li>Point two</li>
<li>Point three</li>
<li>Point four</li>
<li>Point five</li>
<li>Point six</li>
<li>Point seven</li>
</ul>

</div>

</div> 

 $(document).on("click", function(){
$(".bb").toggleClass("active");
}); 

 .aa{
  height:50px;
  background:blue;
  position:fixed;
  top:0;
  width:100%;

}
.bb{
  position:fixed;
  top:0px;
  background:none;
  height:100%;
  width:100%;
  left:0;
  transform:translateZ(0);
  overflow:hidden;
    pointer-events:none;
}
.cc{
  height:100%;
    transform:translate3d(0,100%,0);
    width:100%;
      transition:transform .5s ease-in;
      background:red;
}
.bb.active .cc{
  transform:none;
} 

 <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<p>Click the document to start animation<p>
<div class="bb">
<div class="cc">
<ul>
<li>Point one</li>
<li>Point two</li>
<li>Point three</li>
<li>Point four</li>
<li>Point five</li>
<li>Point six</li>
<li>Point seven</li>
</ul><ul>
<li>Point one</li>
<li>Point two</li>
<li>Point three</li>
<li>Point four</li>
<li>Point five</li>
<li>Point six</li>
<li>Point seven</li>
</ul>

</div>

</div> 

为解决此问题",其中在transform中使用百分比可能会导致动画性能变差,我提出了以下建议,这可能会改善性能.但是,我有兴趣听取其他意见,因为我不确定这是否必要以及为什么(?).

我正在做的基本上只是使用jQuery来应用transform值(以像素为单位)而不是百分比.对于生产情况,自然需要根据窗口调整大小进行更新.

此方法的最终时间表是:

 $(document).ready(function(){
var bbWidth = $("#bb .cc").css('transform').split(',')[5].slice(0,-1);

$("#bb .cc").css("transform", "translate3d(0," + bbWidth + "px,0");
$(document).on("click", function(){
$("#bb").toggleClass("active");
});
}); 

 .aa{
  height:50px;
  background:blue;
  position:fixed;
  top:0;
  width:100%;

}
#bb{
  position:fixed;
  top:0px;
  background:none;
  height:100%;
  width:100%;
  left:0;
  transform:translateZ(0);
  overflow:hidden;
}
.cc{
  height:100%;
    transform:translate3d(0,100%,0);
    width:100%;
      transition:transform .5s ease-in;
      background:red;
      position:absolute;
      top:0;
}
#bb.active .cc{
  transform:none!important;
} 

 <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="bb">
<div class="cc">
<ul>
<li>Point one</li>
<li>Point two</li>
<li>Point three</li>
<li>Point four</li>
<li>Point five</li>
<li>Point six</li>
<li>Point seven</li>
</ul><ul>
<li>Point one</li>
<li>Point two</li>
<li>Point three</li>
<li>Point four</li>
<li>Point five</li>
<li>Point six</li>
<li>Point seven</li>
</ul>

</div>

</div> 

问题:

  • 我是否遇到这种行为(在px中分配值要比%更好)是正确的,如果是这样,为什么会发生这种情况?如前所述,我觉得应该这样做,但是我确实缺乏一些技术/深入的解释.
  • 是否有比我的建议更好的绕过此问题的方法?使用transform: translate()例如如果您不能"使用%并且希望同时获得平滑的动画,那么在屏幕外隐藏导航非常麻烦.

解决方案

我是否遇到这种现象(在px中分配值要比%更好)是正确的,如果是这样,为什么会发生这种情况?如前所述,这对我来说应该是合理的,但是我确实缺乏一些技术/深入的解释.

确实是正确的.像素是绝对值(即不依赖于任何事物并以原样"表示).百分比是相对值,这意味着它们必须依赖于其他一些值才能产生结果.因此,每次分配百分比值时,都必须获取其相对值才能执行计算. 使用像素进行平移时,您只需要更改平移值,而使用百分比,则必须先获取元素的尺寸,然后应用平移.而且必须为每个动画帧完成.

要缓解此问题,您必须在动画之前仅重新计算一次元素的尺寸.然后使用!important覆盖样式属性中设置的内容.代码段中的代码完全可以做到这一点.

还要注意,我已经添加了resize侦听器.如果需要调整窗口大小,这是必需的,因此您的元素将隐藏.

 $(function(){
var $el = $("#bb");
$(document).on("click", function(){
  var height = $el.outerHeight();
  $el
    .css('transform', 'translateY(' + height + 'px)')
    .toggleClass("active");
});
$(window).on('resize', function() {
  $el.removeClass('active').removeAttr('style');
});
}); 

 #bb{
  position:fixed;
  top:0px;
  background-color: red;
  height:100%;
  width:100%;
  left:0;
  overflow:hidden;
  transform: translateY(100%);
  transition: transform .5s ease-in;
}
#bb.active
{
  transform: translateY(0px) !important;
} 

 <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="bb">
<div class="cc">
<ul>
<li>Point one</li>
<li>Point two</li>
<li>Point three</li>
<li>Point four</li>
<li>Point five</li>
<li>Point six</li>
<li>Point seven</li>
</ul><ul>
<li>Point one</li>
<li>Point two</li>
<li>Point three</li>
<li>Point four</li>
<li>Point five</li>
<li>Point six</li>
<li>Point seven</li>
</ul>

</div>

</div> 

I've recently been looking into improving the animations on my website - more specifically of the navigation dropdown on mobile devices.

In this connection I stumbled upon the following case, which I'm hoping to get some more in-depth knowledge about.

The case is that when transitioning/animating transform: translate3d() it seems that the browser requires more calculations when it's applied using % rather than px. E.g. in my tests it seems that transitioning from transform: translate3d(0, 500px, 0) to transform: translate3d(0,0,0) requires less computations and runs smoother than transitioning from transform: translate3d(0, 100%, 0).

Update: Upon testing further I've found that using 100vh/100vw bypasses/mitigates the issue of using percentages. This can be usefyk in cases where the element has a known percentage width of the window or is full-width, and thereby improve the performance. Actually it seems that using this value behaves as if it was assigned with px value in Chrome.

Here is a couple of pictures of the timeline from each animation. The timelines are obtained using Google Dev tools under "Performance". To better show the difference the performance has been limited in Chrome Dev Tools to "low-end mobile" (6x CPU slowdown).

Transform using percent:

Transform using pixel (px):

As can be seen from the images it seems that alot more rendering and painting is going on when using % rather than px to determine the transformation. It makes great sense that the browser has to calculate the percentage values for each frame (I guess?), but I'm surprised that it takes that much more, compared to using a pixel value.

Also please note that the framerates in the picture showing the timeline of percentage never reaches ~60 fps, but is rather averaging around 40 fps.

Below is snippets to replicate the case. There's one using percent and one using px.

$(document).on("click", function(){
$(".bb").toggleClass("active");
});

.aa{
  height:50px;
  background:blue;
  position:fixed;
  top:0;
  width:100%;

}
.bb{
  position:fixed;
  top:0px;
  background:none;
  height:100%;
  width:100%;
  left:0;
  transform:translateZ(0);
  overflow:hidden;
    pointer-events:none;
}
.cc{
  height:100%;
    transform:translate3d(0,500px,0);
    width:100%;
      transition:transform .5s ease-in;
      background:red;
}
.bb.active .cc{
  transform:none;
}

<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<p>Click the document to start animation<p>
<div class="bb">
<div class="cc">
<ul>
<li>Point one</li>
<li>Point two</li>
<li>Point three</li>
<li>Point four</li>
<li>Point five</li>
<li>Point six</li>
<li>Point seven</li>
</ul><ul>
<li>Point one</li>
<li>Point two</li>
<li>Point three</li>
<li>Point four</li>
<li>Point five</li>
<li>Point six</li>
<li>Point seven</li>
</ul>

</div>

</div>

$(document).on("click", function(){
$(".bb").toggleClass("active");
});

.aa{
  height:50px;
  background:blue;
  position:fixed;
  top:0;
  width:100%;

}
.bb{
  position:fixed;
  top:0px;
  background:none;
  height:100%;
  width:100%;
  left:0;
  transform:translateZ(0);
  overflow:hidden;
    pointer-events:none;
}
.cc{
  height:100%;
    transform:translate3d(0,100%,0);
    width:100%;
      transition:transform .5s ease-in;
      background:red;
}
.bb.active .cc{
  transform:none;
}

<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<p>Click the document to start animation<p>
<div class="bb">
<div class="cc">
<ul>
<li>Point one</li>
<li>Point two</li>
<li>Point three</li>
<li>Point four</li>
<li>Point five</li>
<li>Point six</li>
<li>Point seven</li>
</ul><ul>
<li>Point one</li>
<li>Point two</li>
<li>Point three</li>
<li>Point four</li>
<li>Point five</li>
<li>Point six</li>
<li>Point seven</li>
</ul>

</div>

</div>

To solve this "issue" in which using percentages in transform possibly causes worsened animation performance, I've made the following suggestion, which might improve the performance. However, I'm interested in hearing other opinions as I'm not quite sure whether this should be necessary and why(?).

What I'm doing is basically just using jQuery to apply the transform value in pixels rather than percentage. For production cases this will naturally require to be updated on window resizes.

The resulting timeline for this approach is:

$(document).ready(function(){
var bbWidth = $("#bb .cc").css('transform').split(',')[5].slice(0,-1);

$("#bb .cc").css("transform", "translate3d(0," + bbWidth + "px,0");
$(document).on("click", function(){
$("#bb").toggleClass("active");
});
});

.aa{
  height:50px;
  background:blue;
  position:fixed;
  top:0;
  width:100%;

}
#bb{
  position:fixed;
  top:0px;
  background:none;
  height:100%;
  width:100%;
  left:0;
  transform:translateZ(0);
  overflow:hidden;
}
.cc{
  height:100%;
    transform:translate3d(0,100%,0);
    width:100%;
      transition:transform .5s ease-in;
      background:red;
      position:absolute;
      top:0;
}
#bb.active .cc{
  transform:none!important;
}

<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="bb">
<div class="cc">
<ul>
<li>Point one</li>
<li>Point two</li>
<li>Point three</li>
<li>Point four</li>
<li>Point five</li>
<li>Point six</li>
<li>Point seven</li>
</ul><ul>
<li>Point one</li>
<li>Point two</li>
<li>Point three</li>
<li>Point four</li>
<li>Point five</li>
<li>Point six</li>
<li>Point seven</li>
</ul>

</div>

</div>

Question:

  • Is it correct that I'm experiencing this behaviour (assigning value in px performs better than %) and if so, why is this happening? As mentioned earlier it kinda makes sense to me that it should, but I'm really lacking some technological/in-depth explanation.
  • Is there a better way to bypass this issue than my suggestion? Using transform: translate() to e.g. hide navigations off-screen is very circumstantial if you "can't" use % and if you want smooth animations at the same time.

解决方案

Is it correct that I'm experiencing this behaviour (assigning value in px performs better than %) and if so, why is this happening? As mentioned earlier it kinda makes sense to me that it should, but I'm really lacking some technological/in-depth explanation.

It's correct indeed. Pixels are absolute values (i.e. are not dependent on anything and represented ‘as is’). Percentages are relative values, which means they have to depend on some other value in order to produce result. So every time you assign a percentage value it has to get it's relative value to perform a calculation. When doing a translation with pixels you only have to change the translation values, but with percentages you have to get element's dimensions first and then apply the translation. And that has to be done for every animation frame.

To mitigate this issue you have to recalculate element's dimensions only once before the animation. Then use !important to override what's set in the style attribute. The code in the snippet does exactly that.

Also notice that I've added a resize listener. That's required if the window gets resized, so your element becomes hidden.

$(function(){
var $el = $("#bb");
$(document).on("click", function(){
  var height = $el.outerHeight();
  $el
    .css('transform', 'translateY(' + height + 'px)')
    .toggleClass("active");
});
$(window).on('resize', function() {
  $el.removeClass('active').removeAttr('style');
});
});

#bb{
  position:fixed;
  top:0px;
  background-color: red;
  height:100%;
  width:100%;
  left:0;
  overflow:hidden;
  transform: translateY(100%);
  transition: transform .5s ease-in;
}
#bb.active
{
  transform: translateY(0px) !important;
}

<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="bb">
<div class="cc">
<ul>
<li>Point one</li>
<li>Point two</li>
<li>Point three</li>
<li>Point four</li>
<li>Point five</li>
<li>Point six</li>
<li>Point seven</li>
</ul><ul>
<li>Point one</li>
<li>Point two</li>
<li>Point three</li>
<li>Point four</li>
<li>Point five</li>
<li>Point six</li>
<li>Point seven</li>
</ul>

</div>

</div>

这篇关于CSS转换过渡-使用"px"比“百分比"更平滑/更有效的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆