资讯专栏INFORMATION COLUMN

还在为无缝滚动而烦恼?是时候彻底get到这个知识点了

stormjun / 2491人阅读

摘要:最近一直在忙公司炒股大赛的页面,终于在昨天把他给上线了。刚开始学习的时候,真心觉得无缝滚动是一个神奇的功能。原理假如需要无缝滚动的个元素是一个中的个。我们将控制在容器中滚动。这样无缝滚动就已经实现了。

最近一直在忙公司炒股大赛的页面,终于在昨天把他给上线了。一个看似简单的页面,做起来才知道其中的艰辛,暗藏深坑。由于直接使用jquery来写页面逻辑,因此要比想象中复杂很多。无论是从布局,功能还是逻辑上来说,都有值得总结的地方。

这篇文章主要说说关于无缝滚动的实现。

刚开始学习js的时候,真心觉得无缝滚动是一个神奇的功能。背后到底是怎么回事?为什么明明只有几个方块就是滚不到头?后来明白了原理之后,发现原来是通过一些障眼法来实现。

原理

假如需要无缝滚动的4个元素是一个ul.items中的6个li.item。我们将控制ul.items在容器.wrap中滚动。html代码如下:

ul.items表示className为items的ul元素,其他地方同理

我们的目标是实现水平方向上的滚动,因此需要li.item水平排列。能够达到目标的方式常用的有使用float: left,或者使用display: inline-block。我们知道控制页面元素的移动无非就是控制元素的left, top, translateX, translateY,还有一种就是控制滚动距离scrollTop, scrollLeft。布局的选择,同时也会影响到js控制属性的选择。

本例选择使用display: inline-block布局,并控制ul.itemsscrollLeft值,让整个ul滚动起来。布局上需要注意的有以下几点:

超出容器的部分需要隐藏,注意,此处的隐藏是给ul.items的,注意与float: left布局的差别。

.items { overflow: hidden; }

ul.items的内容不能折行,因此

.items { white-space: nowrap; }

需要适配到移动端,因此li.item的宽度就必然会随着设配宽度的变小而变小。

@media (max-width: 780px) {
    .item {
        width: 190px;
    }
}

@media (max-width: 580px) {
    .item {
        width: 160px;
    }
}

html布局中的是为了消除display: inline-block元素之间带来的间隙。

那么无缝滚动的障眼法到底是什么呢?本来用图片描述会更加直观一点,不过这里我想偷个懒,用文字给大家讲述一下,希望大家能看懂。

我们有子元素123456, 一个一个向左滚动,复制一份,就变成123456123456。如果我们在整体移动到第二个1的时候,将整体的位置拉回到第一个1来,也就是初始位置,由于有div.itemsoverflow: hidden在,中间发生的变化我们没办法用肉眼识别出来,就感觉是一直在向左移动,永远都停不下来。

表达能力有限,如果没懂再结合代码理解一下吧,或者留言给我

功能实现

一说到运动,我们常常想到的方法可能是利用setTimeout或者setInterval, 不过呢,html5为我们提供了一个更加高性能的方法requestAnimationFrame

在性能上,requestAnimationFrame > setTimeout > setInterval。具体原因大家可以找找相关的资料了解一下。而setTimeout的最小定时值为100/60,因此,我们在实现运动时,从性能与兼容性两方面考虑,常常会如下声明:

var lastTime = 0,
    nextFrame = window.requestAnimationFrame       ||
                window.webkitRequestAnimationFrame ||
                window.mozRequestAnimationFrame    ||
                window.msRequestAnimationFrame     ||
                function(callback) {
                    var currTime = + new Date,
                        delay = Math.max(1000/60, 1000/60 - (currTime - lastTime));
                    lastTime = currTime + delay;
                    return setTimeout(callback, delay);
                },
    cancelFrame = window.cancelAnimationFrame               ||
                  window.webkitCancelAnimationFrame         ||
                  window.webkitCancelRequestAnimationFrame  ||
                  window.mozCancelRequestAnimationFrame     ||
                  window.msCancelRequestAnimationFrame      ||
                  clearTimeout;

我们需要知道滚动到什么位置回退到0,这个位置刚好就是复制之前所有子元素加一起的总长度。但是子元素的宽度会因为设备宽度的改变而改变,因此配合布局,我们需要作如下处理:

// 单个子元素的宽度
var itemW = 240;
if ($items.children().eq(0).width() == 190) {
    itemW = 190;
}
if ($items.children().eq(0).width() == 160) {
    itemW = 160;
}

// 目标位置
var target = itemW * $items.children().length;

为了实现障眼法,需要复制一份子元素

$items.html( $items.html() + $items.html() );

定义一个运动函数,这里的运动为匀速运动,因此比较简单,只需要一直+1即可。如果需要运动快一点,就多加一点

var timer = null;

function adAni() {
    timer = nextFrame(function() {
        scrollX += 1;

        // 当递增到大于了目标距离,就直接变为0
        if (scrollX >= target) {
            scrollX = 0;
        }
        $items.scrollLeft(scrollX);
        adAni();
    });
}

// 运行这个函数就可以实现无缝滚动啦。
adAni();

这样无缝滚动就已经实现了。不过还有一些其他的需求。比如,鼠标mouseover时,需要停止滚动,离开之后又要重新启动滚动。因为需求的变化,在移动端还需要能够滑动items.ul,手指松开之后继续滚动。因此我们需要一个区别pc于移动端的函数。通过UA的不同来区分。

function isMobile() {
    return /(iphone|ipad|ipod|ios|android|mobile|blackberry|iemobile|mqqbrowser|juc|fennec|wosbrowser|browserng|Webos|symbian|windows phone)/i.test(navigator.userAgent);
}

在pc端,鼠标移入时停止,鼠标移除时继续滚动

if (!isMobile()) {
    $items.on("mouseover", function() {
        cancelFrame(timer);
    }).on("mouseout", function() { adAni(); });
}

在移动端,可以左右滑动,滑动时停止自动滚动,松开之后继续自动滚动。移动端的滑动事件,主要通过touchstart, touchmove, touchend来实现,与pc端的mousedown, mousemove, mouseup类似。

var sX, sL;
$items.on("touchstart", function(e) {
    cancelFrame(timer);
    sX = e.originalEvent.changedTouches[0].pageX;
    sL = $items.scrollLeft();
}).on("touchmove", function(e) {
    var dis = e.originalEvent.changedTouches[0].pageX - sX;
    var nowX = sL - dis;
    if (nowX > target) {
        nowX = 0;
    }
    $items.scrollLeft(nowX);
}).on("touchend", function(e) {
    scrollX = $items.scrollLeft();
    if (scrollX >= target) {
        scrollX = 0;
    }
    adAni();
})

那么到这里,就已经基本搞定啦。虽然是一个比较简单的小例子,但是其中也包含了一些常用的功能,比如使用requestAnimationFrame来实现运动,移动端的滑动事件等。在这里总结一下分享给大家,有疑问欢迎探讨。

实例地址:
http://codepen.io/yangbo5207/...

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/79473.html

相关文章

  • 一分钟教你学会配置eslint,在为风格单双引号,对象末位逗号要不要,引用各种报错烦恼?(建议收

    摘要:最全的配置大全,我已经加了详细中文注释,只需要找到自己想要的配置就可以了,强烈建议收藏用法非常简单,找到下的添加对象即可比如我要把规则原本单引号要变为双引号,那加上注意这个规则如果违反了规则情况下,这里的数字表示不不处理,表示警告,表示错误 最全的eslint配置大全,我已经加了详细中文注释,只需要找到自己想要的配置就可以了,强烈建议收藏! 用法非常简单,找到.eslintrc.js下...

    刘东 评论0 收藏0
  • 表单验证请进!!!

    摘要:每一个项目,都要重写验证规则,验证逻辑,那感觉简直想屎。自从遇见了策略者模式,产经你不是要加验证吗没问题。这个验证规则不行,替换没问题。你这只是一对多的验证规则,当我要提交表单的时候,我还有其他的都需要验证呢。 策略者模式 还在为你表单验证头疼吗?还在为产经无理取闹,要你每个输入框都加验证而感到烦恼吗?还在忙于复制粘贴验证规则而感到厌烦吗? 那么策略者模式是最适合你的。What are...

    Paul_King 评论0 收藏0
  • 「码个蛋」2017年200篇精选干货集合

    摘要:让你收获满满码个蛋从年月日推送第篇文章一年过去了已累积推文近篇文章,本文为年度精选,共计篇,按照类别整理便于读者主题阅读。本篇文章是今年的最后一篇技术文章,为了让大家在家也能好好学习,特此花了几个小时整理了这些文章。 showImg(https://segmentfault.com/img/remote/1460000013241596); 让你收获满满! 码个蛋从2017年02月20...

    wangtdgoodluck 评论0 收藏0
  • 100%移植阿里云移动测试技术,竟仅需1周?! ——移动测试专有云(1)

    摘要:无论是因为移动是企业内部应用无法外部使用,还是要求测试数据绝对私有化,专有云解决方案都可以支持。降低成本自动化测试技术减少手工测试工作量。 摘要: MQC专有云解决方案是MQC测试体系的完全移植版,目标是帮助客户短期内在搭建一套专业的、系统的自动化测试平台,实现测试服务、测试数据的完全本地化、私有化。无论是因为移动App是企业内部应用无法外部使用,还是要求测试数据绝对私有化,MQC专有...

    MiracleWong 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<