创建完美的轮播,第2部分

欢迎回到创建完美轮播教程系列。 我们正在使用JavaScript和Popmotion的物理特性,补间和输入跟踪功能制作出易于访问且令人愉快的轮播。

在本教程的第1部分中,我们了解了Amazon和Netflix如何创建轮播,并评估了其方法的利弊。 通过学习,我们决定了轮播的策略,并使用物理方法实现了触摸滚动。

在第2部分中,我们将实现水平鼠标滚动。 我们还将研究一些常见的分页技术并实现它们。 最后,我们将连接一个进度条,该进度条将指示用户通过轮播的距离。

您可以通过打开此CodePen来恢复保存点, 该代码从我们上次中断的地方开始。

水平鼠标滚动

JavaScript轮播很少考虑水平鼠标滚动。 真可惜:在实现基于动量的水平滚动的笔记本电脑和鼠标上,这是迄今为止导航轮播的最快方法。 就像强迫触摸用户通过按钮导航而不是滑动一样糟糕。

幸运的是,它只需几行代码即可实现。 在carousel功能的末尾,添加一个新的事件侦听器:

container.addEventListener('wheel', onWheel);

在您的startTouchScroll事件下面,添加一个名为onWheel的存根函数:

function onWheel(e) {
  console.log(e.deltaX)
}

现在,如果在转盘上运行滚轮并检查控制台面板,您将在x轴输出上看到轮距。

与触摸一样,如果轮子的移动大部分是垂直的,则页面应照常滚动。 如果它是水平的,我们要捕获车轮的运动并将其应用于旋转木马。 因此,在onWheel ,将console.log替换为:

const angle = calc.angle({
  x: e.deltaX,
  y: e.deltaY
});

if (angleIsVertical(angle)) return;

e.stopPropagation();
e.preventDefault();

如果滚动是水平的,则此代码块将停止页面滚动。 现在,更新滑块的x偏移量只是采用事件的deltaX属性并将其添加到当前的sliderX值中即可:

const newX = clampXOffset(
  sliderX.get() + - e.deltaX
);
sliderX.set(newX);

我们将重用以前的clampXOffset函数来包装此计算,并确保轮播不会滚动到其测量边界之外。

节流滚动事件

任何处理输入事件的优秀教程都将说明限制这些事件的重要性。 这是因为滚动,鼠标和触摸事件的触发速度都快于设备的帧频。

您不希望执行不必要的资源密集型工作,例如在一帧中渲染两次轮播,因为这浪费了资源,并且使界面变得迟钝。

本教程没有涉及到这一点,因为Popmotion提供的渲染器实现了Framesync (一个微小的帧同步作业调度程序)。 这意味着您可以连续多次调用(v) => sliderRenderer.set('x', v) ,昂贵的渲染只会在下一帧发生一次。

分页

滚动结束。 现在,我们需要为迄今为止最受欢迎的导航按钮注入一些生命。

现在,本教程是关于交互的,因此可以随意设计这些按钮。 我个人觉得方向箭头更直观(默认情况下完全国际化!)。

分页应如何工作?

在对轮播进行分页时,我们可以采取两种明确的策略: 逐项第一个模糊的项目 。 只有一种正确的策略,但是,因为我看到了另一种如此频繁地实施的策略,所以我认为值得解释一下它为什么不正确。

1.逐项

逐项示例

只需测量列表中下一项的x偏移量,然后按照该数量设置货架动画即可。 我认为这是一个非常简单的算法,是因为它的简单性而不是用户友好性。

问题在于,大多数屏幕一次都可以显示很多项目,人们会在尝试导航之前先扫描所有项目。

感觉迟钝,即使不是完全令人沮丧。 唯一可行的选择是,如果您知道轮播中的项目的宽度相同或仅比可视区域略小。

但是,如果我们要查看多个项目,则最好使用第一个模糊项目方法:

2.第一个被遮盖的物品

第一个被遮盖的物品

此方法只是在我们要移动轮播的方向上查找第一个被遮挡的项目 ,获取其x偏移量 ,然后滚动到该位置。

这样做时,我们假设用户已经看到了所有当前存在的项目,从而获得了最大数量的新项目。

由于我们要拉入更多商品,因此轮播需要较少的点击才能浏览。 更快的导航将增加参与度,并确保您的用户看到更多您的产品。

事件监听器

首先,让我们设置事件监听器,以便我们可以开始使用分页。

我们首先需要选择上一个和下一个按钮。 在carousel功能的顶部 ,添加:

const nextButton = container.querySelector('.next');
const prevButton = container.querySelector('.prev');

然后,在carousel功能的底部 ,添加事件侦听器:

nextButton.addEventListener('click', gotoNext);
prevButton.addEventListener('click', gotoPrev);

最后,在事件侦听器块的上方,添加实际功能:

function goto(delta) {
}

const gotoNext = () => goto(1);
const gotoPrev = () => goto(-1);

goto是将处理所有分页逻辑的函数。 它只需要一个数字即可代表我们希望分页的行进方向。 gotoNextgotoPrev简单地使用1-1调用此函数。

计算“页面”

用户可以自由滚动该转盘,并且其中有n项目,并且该转盘可能会调整大小。 因此,传统页面的概念不适用于此处。 我们不会计算页面数。

相反,当调用goto函数时,我们将朝着delta的方向看,并找到第一个部分被遮盖的项目。 这将成为我们下一个“页面”上的第一项。

第一步是获取滑块的当前x偏移量,并将其与滑块的整个可见宽度一起使用,以计算要滚动到的“理想”偏移量。 理想的偏移量是如果我们不熟悉滑块的内容滚动到的位置。 它为我们提供了一个不错的起点,开始搜索我们的第一件商品。

const currentX = sliderX.get();
let targetX = currentX + (- sliderVisibleWidth * delta);

我们可以在这里使用厚脸皮的优化。 通过将我们的targetX提供给我们在上一个教程中创建的clampXOffset函数,我们可以查看其输出是否不同于targetX 。 如果是,则意味着我们的targetX在可滚动范围之外,因此我们无需找出最接近的项目。 我们只是滚动到最后。

const clampedX = clampXOffset(targetX);

targetX = (targetX === clampedX)
  ? findClosestItemOffset(targetX, delta)
  : clampedX;

寻找最近的物品

重要的是要注意,以下代码在您的轮播中所有项目都大小相同的前提下工作。 在此假设下,我们可以进行优化,例如不必测量每个项目的大小。 如果您的项目不同的尺寸,这仍然将成为一个好起点。

goto函数上方,添加最后一个片段中引用的findClosestItemOffset函数:

function findClosestItem(targetX, delta) {
}

首先,我们需要知道项目的宽度和它们之间的间距。 Element.getBoundingClientRect()方法可以提供我们需要的所有信息。 对于width ,我们仅测量第一项元素。 要计算项目之间的间距,我们可以测量第一个项目的right偏移量和第二个项目的left偏移量,然后从后者减去前者:

const { right, width } = items[0].getBoundingClientRect();
const spacing = items[1].getBoundingClientRect().left - right;

现在,有了传递给函数的targetXdelta变量,我们有了快速计算要滚动到的偏移量所需的所有数据。

计算方法是将targetX的绝对值除以width + spacing 。 这将为我们提供在该距离内可以容纳的确切项目数。

const totalItems = Math.abs(targetX) / (width + spacing);

然后,根据分页方向(我们的delta )向上或向下取整。 这将给我们提供我们可以容纳的完整物品的数量。

const totalCompleteItems = delta === 1
  ? Math.floor(totalItems)
  : Math.ceil(totalItems);

最后,将该数字乘以width + spacing即可得出带有完整项目的偏移平齐。

return 0 - totalCompleteItems * (width + spacing);

动画分页

现在我们已经计算出了targetX ,我们可以对其进行动画处理了! 为此,我们将使用网络动画的主力工具tween

对于新手来说,“吐温”是短期的BE吐温。 补间在设定的持续时间内从一个值更改为另一个值。 如果您使用过CSS过渡,那是同一回事。

通过补间使用CSS上JavaScript有很多好处(和缺点!)。 在这种情况下,因为我们还将使用物理效果和用户输入来对sliderX进行动画sliderX ,所以使我们能够更轻松地停留在补间的工作流程中。

这也意味着以后我们可以连接一个进度条,它可以自然地与我们所有的动画一起免费使用。

我们首先要从Popmotion导入tween

const { calc, css, easing, physics, pointer, transform, tween, value } = window.popmotion;

goto函数的末尾,我们可以添加从currentXtargetX动画补间:

tween({
  from: currentX,
  to: targetX,
  onUpdate: sliderX
}).start();

默认情况下,Popmotion将duration设置为300毫秒,并easeeasing.easeOut 。 这些是专门为使响应用户输入的动画具有响应感而选择的,但是可以随意玩转,看看您是否提出了更适合您品牌感觉的东西。

进度指标

对于用户来说,表明他们在轮播中的位置很有用。 为此,我们可以连接一个进度指示器。

您的进度栏可以通过多种方式设置样式。 在本教程中,我们制作了一个彩色的div,高度为5px,在上一个和下一个按钮之间运行。 这是我们将其连接到代码并为栏设置动画的一种方式,这一点很重要,也是本教程的重点。

您尚未看到该指标,因为我们最初使用transform: scaleX(0)对其进行样式设置。 我们使用scale变换来调整条形的宽度,因为,正如我们在第1部分中所解释的,变换比更改属性(如left或在这种情况下, width更具性能。

它还使我们能够轻松地编写将比例设置为百分比的代码 sliderX的当前值介于minXOffsetmaxXOffset之间。

让我们div.progress-barpreviousButton选择器之后选择div.progress-bar

const progressBar = container.querySelector('.progress-bar');

定义sliderRenderer ,我们可以为progressBar添加一个渲染器:

const progressBarRenderer = css(progressBar);

现在让我们定义一个函数来更新进度条的scaleX

我们将使用称为getProgressFromValuecalc函数。 这需要一个范围,在我们的例子中是minmaxXOffset ,以及第三个数字。 它返回给定范围内第三个数字的进度01之间的数字)。

function updateProgressBar(x) {
  const progress = calc.getProgressFromValue(maxXOffset, minXOffset, x);
  progressBarRenderer.set('scaleX', progress);
}

我们将范围写为maxXOffset, minXOffset在直观上应该反转)。 这是因为x是一个负值, maxXOffset也是一个负值,而minXOffset0 。 从技术上讲, 0是两个数字中较大的一个,但较小的值实际上表示最大偏移量。 负面的吧?

我们希望进度指示器与sliderX更新,因此让我们更改此行:

const sliderX = value(0, (x) => sliderRenderer.set('x', x));

到这行:

const sliderX = value(0, (x) => {
  updateProgressBar(x);
  sliderRenderer.set('x', x);
});

现在,每当sliderX更新时,进度条也会更新。

结论

这就是本分期付款! 您可以在此CodePen上获取最新代码。 我们已经成功引入了水平轮滚动,分页和进度条。

到目前为止,轮播的状态还不错! 在最后一期中,我们将更进一步。 我们将使旋转木马完全键盘可访问,以确保任何人都可以使用它。

当用户尝试使用触摸滚动或分页功能将旋转木马滚动越过其边界时,我们还将使用弹簧动力拖船添加一些令人愉悦的触摸。

回头见!

翻译自: https://code.tutsplus.com/tutorials/create-the-perfect-carousel-part-2--cms-29592

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值