向下滑动时向后滑动滑块

时间:2018-09-10 14:18:35

标签: javascript css performance css3 css-transforms

我跟随this article使用了垂直可滑动式卡片滑块。

这个问题分为两个部分。

1。我不明白在向下滑动时如何反转滑块的方向?

以下是相关的密码笔-https://codepen.io/bmarcelino/pen/vRYPXV

更新卡的相关功能

function updateUi() {
    requestAnimationFrame(function(){
        elTrans = 0;
        var elZindex = 5;
        var elScale = 1;
        var elOpac = 1;
        var elTransTop = items;
        var elTransInc = elementsMargin;

        for(i = currentPosition; i < (currentPosition + items); i++){
            if(listElNodesObj[i]){
                listElNodesObj[i].classList.add('stackedcards-bottom', 'stackedcards--animatable', 'stackedcards-origin-bottom');

                listElNodesObj[i].style.transform ='scale(' + elScale + ') translateX(0) translateY(' + (elTrans - elTransInc) + 'px) translateZ(0)';
                listElNodesObj[i].style.webkitTransform ='scale(' + elScale + ') translateX(0) translateY(' + (elTrans - elTransInc) + 'px) translateZ(0)';
                listElNodesObj[i].style.opacity = elOpac;
                listElNodesObj[i].style.display = 'block';
                listElNodesObj[i].style.zIndex = elZindex;

                elScale = elScale - 0.04;
                elOpac = elOpac - (1 / items);
                elZindex--;
            }
        }

    });

};

我不太精通Java。
截至目前,滑块在向后滑动时只能向一个方向移动-向前移动。我想了解向滑块添加后退运动的实现。

2。关于性能

此外,requestAnimationFrame确实有助于在滑动时提供流畅的体验。但是,在DOM中应该有多少张卡有限制?我将调用API服务来获取内容,因为它将返回媒体,因此将opacity设置为0会以任何方式减少内存使用吗?

作者认为删除DOM会迫使浏览器重新绘制,这会严重影响性能吗?但是,该虚拟列表不是吗?在这种情况下,性能/成本比是多少?

2 个答案:

答案 0 :(得分:1)

这不会是一个完整的答案,但是由于没有其他人做出回应,我将为您回答问题的第1部分提供帮助。请注意,这只是一个示例,可以帮助您了解如何使卡向后退而不是可用于生产的解决方案。

要重新连接“顶部”按钮以向后移动,您只需要进行以下更改:

  • onSwipeTop()中,将currentPosition = currentPosition + 1;更改为currentPosition = currentPosition - 1;
  • 还可以在onSwipeTop()中将transformUi(0, -1000, 0, topObj);更改为transformUi(0, 0, 0, topObj);。这会隐藏卡片上升动画。
  • updateUi()中将i < (currentPosition + items)更改为i <= (currentPosition + items)。这修复了三个卡中只有两个被更新的错误。

现在尝试几次单击“向左”或“右”,然后单击“顶部”两次。每次单击“顶部”,您应该会看到一张卡片回来。

大概您想将新按钮设置为“底部”,而不是重新布线“顶部”,并且您可能还希望限制对currentPosition的更改,因此您不能超越第一个/最后一张卡,但这至少应该可以帮助您入门。

我希望这会有所帮助。

答案 1 :(得分:1)

反转滑块

以下内容是有关脚本工作方式的非常简短的解释,包括有关建议使脚本反转方向和“取消刷卡”的建议。反向方法的灵感来自Rocky的出色回答-他理应得到这个想法的充分肯定。

  1. 文档加载后,脚本将获得所有可用卡的列表。在您的示例中,卡片是DOM中的硬编码元素,卡片列表listElNodesObj是这些元素的列表。要记住这一点很重要:卡片不是抽象的,它们从根本上讲是页面中的元素。当您向卡中添加媒体和数据时,必须将其附加到DOM中的元素上(例如,具有数据属性)。

  2. 该脚本获取名为currentPosition的当前卡索引;首先,这是头牌。然后,它显示当前卡及其后的两张卡(currentPosition + 1currentPosition + 2)。

  3. 在输入时,该卡具有适当的动画效果,可以从左侧,右侧或顶部飞出。当前的卡索引将增加1,再增加1进入堆栈。显示新的当前卡及其后的两张卡。

目前,所有动作-向左,向右和向上滑动-全部进入堆栈。要反转方向,您需要侦听新动作(或重新使用当前动作,例如滑动顶部),并且在该动作上,当前卡索引会减少1。添加小于零的校验。 currentPosition = Math.max( 0, currentPosition - 1 );

Rocky回答了一个出色的解决方案。

现在,此实现从DOM中已经存在的所有卡开始。您似乎想从后端API更新卡堆栈。为此,您需要一种在刷卡时从一端弹出卡并在另一端添加新卡的方法。如上所述,您的卡片列表与DOM紧密相关,因此您需要对其进行抽象一些以实现此目的。创建一个从API填充的元素列表,并用它填充初始文档(而不是相反)。滑动时,无论向前还是向后,请从后退端弹出一个元素,然后将从API填充的新元素添加到前进端。有趣的是,列表大小当前位置将始终保持不变。

如果您当前的位置始终是最顶层卡片的后退位置,并且堆栈中的最后一张卡片比最后一张可见的卡片多一个,则您将始终有一张卡片可以进行动画显示。

性能

此处的术语稍作澄清:更改类似opacity的内容会导致重新绘制,从DOM中删除元素(无论是软删除还是硬删除)都会导致重排。重绘是昂贵的,因为浏览器必须检查DOM中每个元素的可见性。由于必须重新计算布局,因此回流焊甚至更加昂贵。参见What's the difference between reflow and repaint?

有两种方法可以限制DOM中的卡数。您可以设置display: none,将其保留在内存和DOM中,但阻止浏览器在重排或重新绘制时考虑它。或者,您可以使用parent.appendChild(child)添加卡,并使用parent.removeChild(child)删除卡,以确保JavaScript在删除后就不存在对该元素的引用,并且一旦垃圾收集器运行,该删除的元素将被物理地从内存中删除。两者都会触发回流。带有opacity: 0的元素将完全保留在DOM中,以进行重排和重绘。

关于什么能提供最佳性能:更改不透明度或从内存中删除,这实际上取决于您的实现。我可以给你一些相关的指示。

内存限制在DOM中应有多少卡有限制?”绝对可以,但这取决于您的数据。如果您的卡总数很少,则确实可以一开始就全部加载,并用opacity: 0display: none隐藏刷过的卡。甚至可以改善动画的流动性(请参见下面有关动画块计算的要点)。纯粹由于更高的内存使用而导致的性能差异几乎肯定不会引起注意,因为现代浏览器具有大量内存,并且会在需要页面文件或交换之前将脚本停止运行。如果您确实会在内存中拥有如此巨大的DOM以至于性能会明显下降,那么您的内容下载时间将是一个更大的问题。

然而,更重要的是,您正确地问到删除或添加元素是否是虚拟列表的全部内容。如果您永远不会再访问元素,为什么还要保留一个元素在内存中,或者为什么加载一个远远超出列表可能永远无法到达的元素。实际上,您声明您将要通过API访问内容,这强烈暗示您将一次访问一张卡上的内容。等到您拥有API中的所有内容后,可能要花费很长时间。您似乎已经知道,仅访问填写固定大小列表所需的卡会提供更好的体验。 (如果要反转滑块的方向,则应在内存中至少保留一张已刷卡,这样在向后刷卡时,您不会在等待下载内容时通过暂停或发送空卡来破坏动画)< / p>

动画阻止计算,除了下载时间以外,display: noneopacity: 0对于刷卡或在列表中距离太远的卡的真正性能优势在于它们的内容在DOM中已经存在。 (并且如上所述,opacity: 0还有一个优点:它不会触发重排)。通过比较,从物理上在DOM中添加和删除元素需要额外的计算,即在DOM树中插入或删除卡节点及其所有子代。如果这是同步完成的,那么您将进行动画阻止计算,从而在DOM树更新完成之前才能进行滑动动画。

但是,让我们保持透视。首先,从DOM树中添加和删除节点通常非常快。事实证明,innerHTML在添加到DOM时速度稍快,但是在删除时则慢得多,因此请谨慎选择毒药。但是,除非您要向卡中添加数十或数百千字节的数据,否则操作将likely take less than a millisecond。其次,您声明将从API中检索列表的内容,这意味着异步连接。如果在构造异步函数时要小心,则将内容添加到DOM的函数不一定会干扰动画。 (这并不意味着JavaScript是多线程的;它是单线程的,但结构良好的异步代码意味着函数执行的顺序无关紧要)。如果在没有将动画排入队列的情况下完成了添加或移除的卡片的操作,则对性能的影响都将变成shorter and less perceivable

最后,每个DOM操作都是一个新的渲染更新,因此您希望进行尽可能少的操作。因此,您将在内存中创建彼此相对的元素,并且仅在最后将最高元素插入DOM。参见Fastest DOM insertion。如果您可以在该卡中包含所有卡数据,则每次滑动仅需要两次DOM操作。这取决于卡所包含的媒体,但是可以想象,最坏的情况是,将卡添加和移除到堆栈中的总时间仅需数十毫秒。

GPU 大多数渲染引擎都可以访问GPU,该GPU在涉及大量像素的绘图和合成操作中可以比CPU效率更高。默认情况下,渲染层不使用GPU渲染。页面GPU Accelerated Compositing in Chrome指出,

  

从理论上讲,每个单独的RenderLayer都可以将其自身绘制到单独的背衬表面中(即GPU可访问的合成层],实际上这可能在内存(尤其是VRAM)方面非常浪费。

为确保使用GPU渲染不透明度变化之类的动画,您需要以隐式合成的方式访问该动画。上面的页面提供了如何执行此操作的完整列表,但实际上,您将使用CSS动画来更改不透明度,从而提示浏览器将元素提升为合成层。您提供的当前代码使用JavaScript更新每个动画帧的不透明度,因此不透明度更改不是隐式合成的候选对象。 (您似乎对运动动画使用了3D变换,该变换触发了隐式合成,因此可能已经对GPU进行了优化)。更改代码以使用CSS动画并不是一件容易的事,但是很可能会提高性能,特别是动画过程中的帧频。当然,需要针对特定​​情况进行基准测试,以验证这一点,有关https://www.smashingmagazine.com/2016/12/gpu-animation-doing-it-right/的某些GPU动画为何运行速度较慢的讨论,请参见{{3}}。


总而言之,由于从虚拟列表中动态添加和删除元素而导致的性能提高,虽然可能比完全加载整个列表略高,但每次滑动可能仅多了几毫秒。使用异步实现时,动画期间的帧频不应更改。通常,这很容易让步,因为可能会节省大量的初始下载时间,但必须与特定实现的其他细节一起考虑。