使用 Webkit 转换和转换时如何修复闪烁

Posted

技术标签:

【中文标题】使用 Webkit 转换和转换时如何修复闪烁【英文标题】:How to fix flicker when using Webkit transforms & transitions 【发布时间】:2011-02-27 20:20:15 【问题描述】:

我有一个非常简单的演示工作,它使用 Webkit 转换和转换在“面板”(div)之间实现平滑的水平滚动。

我想走这条路线而不是 javascript 驱动系统的原因是它适用于 iPad 并且 Javascript 性能很差,但是 css 转换和过渡非常流畅。可悲的是,我的演示在 iPad 上出现了很多闪烁。

你可以看到the demo here

您需要 Safari 或 iPad 才能看到它的实际效果。我从未在任何转换和过渡的演示中看到这种情况发生,所以我希望这是可以修复的。

不管怎样,这就是驱动这个东西的代码......

html 看起来像这样。

<html>
    <head>
        <title>Swipe Demo</title>
        <link href="test.css" rel="stylesheet" />
        <link href="styles.css" rel="stylesheet" />
        <script type="text/javascript" src="jquery.js"></script>
        <script type="text/javascript" src="functions.js"></script>
        <script type="text/javascript" src="swiping.js"></script>
    </head>
    <body>


    <div id="wrapper">
        <div class='panel one'>
            <h1>This is panel 1</h1>
        </div>

        <div class='panel two'>
            <h1>This is panel 2</h1>
        </div>

        <div class='panel three'>
            <h1>This is panel 3</h1>
        </div>

        <div class='panel four'>
            <h1>This is panel 4</h1>
        </div>
    </div>

    </body>
</html>

CSS 看起来像这样

    body,
    html
        
            padding: 0;
            margin: 0;
            background: #000;
        

    #wrapper
        
            width: 10000px;
            -webkit-transform: translateX(0px);
        

    .panel
        
            width: 1024px;
            height: 300px;
            background: #fff;
            display: block;
            float: left;
            position: relative;
        

而 javascript 看起来像这样

// Mouse / iPad Touch
var touchSupport = (typeof Touch == "object"),
touchstart   = touchSupport ? 'touchstart' : 'mousedown',
touchmove    = touchSupport ? 'touchmove'  : 'mousemove',
touchend     = touchSupport ? 'touchend'   : 'mouseup';

$(document).ready(function()

    // set top and left to zero
    $("#wrapper").css("top", 0);
    $("#wrapper").css("left", 0);

    // get total number of panels
    var panelTotal;
    $(".panel").each(function() panelTotal += 1 );

    // Touch Start
    // ------------------------------------------------------------------------------------------

    var touchStartX;
    var touchStartY;
    var currentX;
    var currentY;
    var shouldMove = false;
    document.addEventListener(touchstart, swipeStart, false);
    function swipeStart(event)

        touch = realEventType(event);

        touchStartX = touch.pageX;
        touchStartY = touch.pageY; 
        var pos = $("#wrapper").position();
        currentX = parseInt(pos.left);
        currentY = parseInt(pos.top);

        shouldMove = true;

    

    // Touch Move
    // ------------------------------------------------------------------------------------------

    var touchMoveX;
    var touchMoveY;
    var distanceX;
    var distanceY;
    document.addEventListener(touchmove, swipeMove, false);
    function swipeMove(event)
        if(shouldMove)
            touch = realEventType(event);
            event.preventDefault();

            touchMoveX = touch.pageX;
            touchMoveY = touch.pageY;

            distanceX = touchMoveX - touchStartX;
            distanceY = touchMoveY - touchStartY;       
            movePanels(distanceX);

        
    

    function movePanels(distance)
        newX = currentX + (distance/4);    
        $("#wrapper").css("left", newX);
    


    // Touch End
    // ------------------------------------------------------------------------------------------

    var cutOff = 100;
    var panelIndex = 0;
    document.addEventListener(touchend, swipeEnd, false);
    function swipeEnd(event)

        touch = (touchSupport) ? event.changedTouches[0] : event;

        var touchEndX = touch.pageX;
        var touchEndY = touch.pageY;

        updatePanelIndex(distanceX);

        gotToPanel();

        shouldMove = false;

    

    // --  --  --  --  --  --  --  --  --  --  --  --  --  --  --  --  --  --  --  --  --  --  --

    function updatePanelIndex(distance)

        if(distanceX > cutOff)
            panelIndex -= 1;

        if(distanceX < (cutOff * -1))
            panelIndex += 1;
        

        if(panelIndex < 0)
            panelIndex = 0;
        

        if(panelIndex >= panelTotal)
            panelIndex = panelTotal -1;

            console.log(panelIndex);

    

    // --  --  --  --  --  --  --  --  --  --  --  --  --  --  --  --  --  --  --  --  --  --  --

    function gotToPanel()

        var panelPos = getTotalWidthOfElement($(".panel")) * panelIndex * -1;

        $("#wrapper").css("-webkit-transition-property", "translateX");
        $("#wrapper").css("-webkit-transition-duration", "1s");
        $("#wrapper").css("-webkit-transform", "translateX("+panelPos+"px)");

    

);

function realEventType(event)
    e = (touchSupport) ? event.targetTouches[0] : event;
    return e;

【问题讨论】:

【参考方案1】:

我发现 translate3D 是比动画 left: 或 right: 类更好的解决方案。它在 safari 上运行良好并且更流畅。即使在 ios13 上,我也遇到了严重的闪烁问题。

这是一张幻灯片:

.cartout-enter-active 
  -webkit-animation: cartout 0.2s;
  animation: cartout 0.2s;

.cartout-leave-active 
  -webkit-animation: cartout 0.2s reverse;
  animation: cartout 0.15s reverse;

@-webkit-keyframes cartout 
  from 
    transform: translate3d(100%, 0, 0);
  

  to 
    transform: translate3d(0%, 0, 0);
  

【讨论】:

【参考方案2】:

基于@tobiasahlin WebExpo 上的演讲。

Safari 闪烁问题修复最佳解决方案是

transform: translateZ(0);

【讨论】:

【参考方案3】:

如今,在 iOS8 中,另一个很好的解决方案是将 overflow: hidden 应用于被指控的元素(或其容器)。

【讨论】:

【参考方案4】:

我首先让视图处于“3D”状态,从而使闪烁消失。首先,我所有的观点都启用了preserve-3D。然后我有这个代码,

MyNamespace.flickerFixer = function(children) 
 children.css(
  "-webkitTransform": "translate3D(0px, 0px, 0px)",
  "-webkit-transition": "1s ease-in-out"
 );

然后我在做 webkit 动画之前初始化它:

MyNamespace.flickerFixer($this.parent(".ui-content"));

【讨论】:

【参考方案5】:

@gargantaun 是对的,如果您要设置动画的元素大于屏幕,Webkit 会闪烁。但是有一个简单的解决方法。只需添加:

-webkit-backface-visibility: hidden;

到元素,你很高兴!

【讨论】:

我很好奇,你是怎么想出来的? 找到它here,我们的companies website 需要它(只需等待几秒钟的动画)。 使用风险自负!当 IOS7 出来时,我们的应用程序由于内存错误而开始崩溃。它通常使用 10-15 MB,但 IOS 崩溃日志报告使用约 600 MB。经过一番挖掘,事实证明这个背面可见性黑客(我们从源代码中确实有一个指向这个 SO 线程的链接)是原因,或者至少,删除它使崩溃消失了。不知道为什么,它只是IOS7。我认为Apple的Webkit分支中一定存在一些内存泄漏错误或某些东西。现在很多开发者都在抱怨 IOS7 Safari 崩溃了。 桌面(Chrome)上也会发生同样的情况。我只是尝试在带有图像的可滚动列表上使用它,结果导致严重的帧率下降。【参考方案6】:

如上所述,最好使用 Translate3d,因为硬件加速可以实现更平滑的过渡。

但是,当被动画的 div 大于屏幕时会导致闪烁。所以,如果你有一个区域加起来最多 3.5 个屏幕宽度,你希望水平过渡,它应该像这样分成 4 个 div

[1][2][3][.5]

所有 div 都不应超过屏幕的高度或宽度。

很抱歉迟到了这个答案。在收到“热门问题”通知之前,我完全忘记了它。

【讨论】:

庞大 - 我遇到了同样的问题,但我不确定我是否理解这个答案。我有一个类似的布局 - 一个宽广的可视区域,其宽度是视口/屏幕的 300%,我正在为该区域左右设置动画。如果我将它分成 3 个 div,每个都与屏幕一样宽 - 那么您是否建议我单独为每个 div 设置动画?【参考方案7】:

尝试使用 translate3d 而不是 translateX。似乎只有 translate3d 在 iPad 3.2 上进行了硬件加速。

【讨论】:

以上是关于使用 Webkit 转换和转换时如何修复闪烁的主要内容,如果未能解决你的问题,请参考以下文章

CSS3不透明度转换的Webkit问题?

在 Android 上使用 jQuery Mobile 在页面转换时仍然存在闪烁/闪烁问题

当触发 iOS Safari 中的虚拟键盘时,它会使我的 CSS 过渡闪烁。如何解决这个问题?

iOS5 + jquery-mobile 过渡闪烁

如何在 CSS 过渡期间防止 Webkit 文本呈现变化

如何在 CSS 过渡期间防止 Webkit 文本呈现变化