第 63 题:如何设计实现无缝轮播?

发布于 2022-07-13 17:06:44 字数 353 浏览 201 评论 18

图片轮播过程分析,假设一共有5张图片,克隆第一张图片放在最后,现在一共有6张图。轮播中图片显示的顺序为:

1 2 3 4 5 (6–>1) 2 3 4 5 (6–>1) ….

(6—>1)表示图片显示第6张图片(克隆自第一张图片)后,迅速的将图片容器滚动到开始的位置,即第一张图片,这也是无缝轮播的秘密所在,由于切换为同一张图片,且切换的时间很小,所以人眼看不出来。图片对应锚点的顺序为:

0 1 2 3 4 0 1 2 3 4 0

图片显示时容器的位置:

0 -100% -200% -300% -400% (-500%–>0) -100% -200% -300% -400% (-500%–>0)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(18

盗心人 2022-05-04 13:56:31

参考了18055975947,没有用jquery

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>    
    <style>
        * {
            padding: 0;
            margin: 0;

        }

        .outer {
            position: relative;
            width: 400px;
            height: 200px;
            overflow: hidden;
            margin: 100px auto;
        }

        .innerBox {
            position: absolute;
            width: 9999em;
            height: 200px;
            /* background-color: red; */
        }

        .inner {
            /* position: absolute; */
            float: left;
            width: 400px;
            height: 200px;
            font-size:25px;
            text-align: center;
            line-height: 200px;
        }
    </style>
</head>

<body>
    <div class="outer">
        <div class="innerBox">
            <div class="inner" style="background-color: aqua;">1</div>
            <div class="inner" style="background-color: aquamarine;">2</div>
            <div class="inner" style="background-color: beige;">3</div>
            <div class="inner" style="background-color: red;">4</div>
            <div class="inner" style="background-color: blanchedalmond;">5</div>
        </div>
    </div>
    <script>
        var count = 0;
        var inner = document.getElementsByClassName("inner");
        var timer = null;
        var innerBox = document.getElementsByClassName("innerBox")[0];
        function move() {
            innerBox.style.transition = '2s'
                    innerBox.style.left = -400 * count +'px'
            timer = setInterval(function (times) {
                count++;
                if (count > 4) {
                    count = 0;
                    inner[4].style.position = "absolute"
                    inner[4].style.left = '-400px'
                    inner[4].style.transition = '2s'
                    innerBox.style.left = "0px"
                    innerBox.style.transition = 'none'
                } else {
                    inner[4].style.position = "relative"
                    inner[4].style.left = "0px"
                    // inner[0].style.left = "0px"
                    
                    innerBox.style.transition = '2s'
                    innerBox.style.left = -400 * count +'px'
                }
                
            }, 2000)
        }
        move()
    </script>
</body>

</html>
桃扇骨 2022-05-04 13:56:31
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>

<body>
  <ul class="animation">
  </ul>
</body>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.js"></script>
<script>

  var actionStep = 1.5

  var animationData = ['1', '2']

  var domData = [...animationData, animationData[animationData.length - 1]]

  $('.animation').append($(domData.map((val, index) => {
    if (index === domData.length - 1) return `<!-- 为了撑起 animiation 元素 --><li class="animation-item animation-item-slot">${val}</li>`
    return `<li class="animation-item animation-item-move animation-item-move-${index + 1}">${val}</li>`
  }).join('')))

  var dataLength = animationData.length

  $('head').append(`<style>
    .animation-item-move {
      position: absolute;
      left: 0;
      width: 100%;
      transform: translateY(100%);
      animation: ${dataLength * actionStep}s step-start 0s infinite running slidein;
    }
    ${animationData.map((v, index) => {
    return `.animation-item-move-${index + 1} {
          animation-delay: ${index * actionStep}s;
        }
      `
  }).join('')}

    @keyframes slidein {
      0% {
        transform: translateY(100%);
        animation-timing-function: ease-in-out;
      }

      ${100 / dataLength}% {
        transform: translateY(0%);
        animation-timing-function: ease-in-out;
      }

      ${200 / dataLength}% {
        transform: translateY(-100%);
        animation-timing-function: step-start;
      }
    }

    @keyframes slideinSlot {
      0% {
        transform: translateY(0);
        animation-timing-function: ease-in-out;
      }

      100% {
        transform: translateY(-100%);
        animation-timing-function: step-start;
      }
    }

    .animation-item-slot {
      animation: ${actionStep}s slideinSlot;
      transform: translateY(-100%);
    }

    .animation {
      position: relative;
      padding-left: 0;
      text-align: center;
      background-color: aqua;
      overflow: hidden;
    }
    .animation-item {
      list-style: none;
      line-height: 50px;
      height: 50px;
    }
  </style>`)
</script>

</html>
耳钉梦 2022-05-04 13:56:28

nter从 translateX(-100%) 开始,到 translateX(0) 结束,leave从translateX(0)开始,到translateX(100%)结束

nter从 translateX(-100%) 开始,到 translateX(0) 结束,leave从translateX(0)开始,到translateX(100%)结束

这样是向右移动吧

世界等同你 2022-05-04 13:56:24

这里说一个不需要clone的方案:

<div class="slide">
  <ul>
    <li>图片1</li>
    <li>图片2</li>
   <li>图片3</li>
  </ul>
</div>

1、最外层div.slide定宽、相对定位relative

2、ul 足够宽,最起码li数*li宽度,这里有个技巧,直接 width: 9999em,目的是让里面的所有li一字排开

3、滚动效果通过控制ulleft或者transform来进行滚动效果

4、到了最后一个li,往后看第一个li的时:

4.1、准备继续滚动,把最后一个的li设置为相对定位relativeleft值为此时此刻相对ul的位置(设置的时候不要带缓动效果),目的是让最后一个li不动。

4.2、然后把ul的left或者transform设为0(这步没有缓动效果),

4.3、然后再正常的开始一样出现第一个li的滚动效果(这步有缓动效果)

4.4、最后等无缝的第一个li效果完成后,把最后一个lileft值复原为0

5、到第一个li,往前看最后一个li时,也是和上面同理

具体效果可以看下 汽车之家首页的轮播图效果

4.1应该是把最后一个li的left设置为所有li宽度之和的负值,4.2应该是把ul的left设置为1个li的宽度。

为你鎻心 2022-05-04 13:56:08

演示地址⭐️
github 源码

原理

本例 固定为4张图的轮播图,主要为便于阐述原理.

  1. 首先页面布局,重点实现如上图所示的滚动内容(board)结构

    • 4fake的图片4的复制,1fake的图片1的复制,
  2. 通过设置上述滚动结构(board)的css left和transition 实现滚动效果

  3. 实现无限滚动

    • 当页面滚动到1fake 时,在滚动完成后,将left值设置到1的位置(此处没有动画,用户无法察觉);
    • 同理,当页面滚动到4fake 时,在滚动完成后,将left值设置到4的位置(此处也没有动画);

3 实现无缝轮播

当到达4fake的位置,默默切换到4,到达1fake的位置,默默切换到1

(function () {
    let prev = document.getElementsByClassName("carousel-prev")[0];
    let next = document.getElementsByClassName("carousel-next")[0];
    let board = document.getElementsByClassName("carousel-board")[0];
    let panels = Array.from(document.getElementsByClassName('carousel-board-item'));
    board.style.left = "-400px"; //设置初始的left值
    let index = 1; //设置初始的index值
    prev.addEventListener("click", function () {
        index++
        console.log(index);
        animate(-400);
        //关键点 如果当前在 1fake 的位置
        if (index === panels.length - 1) {
            setTimeout(() => {
                //去掉动画
                board.style.transition = "0s";
                let distance = 4 * 400
                //默默的左移board至 1
                board.style.left = parseInt(board.style.left) + distance + "px"
            }, 600)
            index = 1;
        }

    })
    next.addEventListener("click", () => {
        index--
        console.log(index);
        animate(400);
        //关键点 如果当前在 4fake 的位置
        if (index === 0) {
            setTimeout(() => {
                // 去掉动画
                board.style.transition = "0s";
                let distance = -4 * 400
                //默默的右移board 至 4
                board.style.left = parseInt(board.style.left) + distance + "px"
            }, 600)
            index = 4;
        }
    })

    function animate(width = 400) {
        board.style.transition = "0.5s";
        board.style.left || (board.style.left = 0)
        board.style.left = parseInt(board.style.left) + width + "px";
    }
})()

prev和next反了吧。

九八野马 2022-05-04 13:55:59

用Vue实现无缝轮播好像比较省事:
transition-group来显示图片的位置,
Vue中的过渡有4种状态:
enter -> enter-to, leave ->leave-to
如果向左移动,那么enter从 translateX(-100%) 开始,到 translateX(0) 结束,leave从translateX(0)开始,到translateX(100%)结束,向右移动则反过来。
GIF.gif

<!DOCTYPE html>
<html>
<head>
    <title></title>
    <script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
    <style type="text/css">
        #app {
            display: flex;
            align-items: center;
            margin: auto;
            width: 200px;
        }
        ul  {
            list-style-type: none;  
            position: relative;
            width: 100px;
            height: 100px;
            box-sizing: border-box;
            padding: 0;
            margin: auto;
            overflow: hidden;
        }
        li {
            width: 100px;
            height: 100px;
            position: absolute;
        }
        #one {
            background: yellow;
        }
        #two {
            background: black;
        }
        #three {
            background: pink;
        }
        #four {
            background: orange;
        }

        .right_animation-enter, .left_animation-leave-to {
            transition: all .3s ease;
            transform: translateX(-100px);
        }

        .right_animation-leave-to, .left_animation-enter {
            transition: all .3s ease;
            transform: translateX(100px);
        }

        .right_animation-enter-to, .right_animation-leave, .left_animation-enter-to, .left_animation-leave {
            transition: all .3s ease;
            transform: translateX(0);
        }

    </style>
</head>
<body>
    <div>
        <button @click="turn('left')">左</button>
        <transition-group tag="ul" :name="direction=='left' ? 'left_animation' : 'right_animation'">
                <li v-for="(img,index) in imgs"
                    :id="img" 
                    :key="index"
                    v-show="current_index===index">
                </li>
        </transition-group>
        <button @click="turn('right')">右</button>
    </div>
</body>
<script>
    new Vue({
        el: '#app',
        data: {
            imgs: ['one', 'two', 'three'],
            direction: 'left',
            current_index: 0
        },
        methods: {
            turn: function (side) {
                this.direction = side
                if (side == 'left') {
                    this.current_index -= 1
                    if (this.current_index < 0) {
                        this.current_index = this.imgs.length - 1 
                    }
                } else {
                    this.current_index += 1
                    if (this.current_index === this.imgs.length) {
                        this.current_index = 0
                    }
                }
            }
        }})
</script>
</html>
凶凌 2022-05-04 13:55:44

演示地址
github 源码

原理

本例 固定为4张图的轮播图,主要为便于阐述原理.

  1. 首先页面布局,重点实现如上图所示的滚动内容(board)结构
    • 4fake的图片4的复制,1fake的图片1的复制,
  2. 通过设置上述滚动结构(board)的css left和transition 实现滚动效果
  3. 实现无限滚动
    • 当页面滚动到1fake 时,在滚动完成后,将left值设置到1的位置(此处没有动画,用户无法察觉);
    • 同理,当页面滚动到4fake 时,在滚动完成后,将left值设置到4的位置(此处也没有动画);

3 实现无缝轮播

当到达4fake的位置,默默切换到4,到达1fake的位置,默默切换到1

(function () {
    let prev = document.getElementsByClassName("carousel-prev")[0];
    let next = document.getElementsByClassName("carousel-next")[0];
    let board = document.getElementsByClassName("carousel-board")[0];
    let panels = Array.from(document.getElementsByClassName('carousel-board-item'));
    board.style.left = "-400px"; //设置初始的left值
    let index = 1; //设置初始的index值
    prev.addEventListener("click", function () {
        index++
        console.log(index);
        animate(-400);
        //关键点 如果当前在 1fake 的位置
        if (index === panels.length - 1) {
            setTimeout(() => {
                //去掉动画
                board.style.transition = "0s";
                let distance = 4 * 400
                //默默的左移board至 1
                board.style.left = parseInt(board.style.left) + distance + "px"
            }, 600)
            index = 1;
        }

    })
    next.addEventListener("click", () => {
        index--
        console.log(index);
        animate(400);
        //关键点 如果当前在 4fake 的位置
        if (index === 0) {
            setTimeout(() => {
                // 去掉动画
                board.style.transition = "0s";
                let distance = -4 * 400
                //默默的右移board 至 4
                board.style.left = parseInt(board.style.left) + distance + "px"
            }, 600)
            index = 4;
        }
    })

    function animate(width = 400) {
        board.style.transition = "0.5s";
        board.style.left || (board.style.left = 0)
        board.style.left = parseInt(board.style.left) + width + "px";
    }
})()
凑诗 2022-05-04 13:55:17

改变了以往写轮播的方式,尝试了一下纯操控节点增删来实现无缝连接轮播图。https://github.com/yaodongyi/javascript

弥枳 2022-05-04 13:54:20

代码千万种,这里主要说一下两种实现思想:

  • 每次轮播元素动画执行到末尾的时候迅速让其位置恢复原位,造成视觉上的无缝轮播
  • 将轮播元素复制一份,第一个item元素轮播执行完后将其删除后添加在整个轮播列表的最后,造成循环轮播

自己写的react-native的轮播组件,欢迎评鉴:https://github.com/lvzhiyi/react-naitve-SeamlessScroll

趁微风不噪 2022-05-04 13:53:45

一上来clone一个

醉酒的小男人 2022-05-04 13:48:10
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>轮播</title>
    <style>
        *{
            padding: 0;
            margin: 0;
            list-style: none;
        }
        #continer{
            width: 300px;
            height: 200px;
            position: relative;
            margin: 20px auto;
            border: 1px solid;
            overflow: hidden;
        }
        #lunbo{
            width: 9999em;
            height: 100%;
            position: absolute;
            top: 0;
            left: 0;
        }
        li{
            float: left;
            width: 300px;
            height: 200px;
            text-align: center;
            line-height: 200px;
            color: brown;
            font-size: 30px;
        }
    </style>
</head>
<body>
    <div>
        <ul class="ul">
            <li class="list">1</li>
            <li class="list">2</li>
            <li class="list">3</li>
            <li class="list">4</li>
            <li class="list">5</li> 
        </ul>
    </div>
</body>
<script src="./js/jquery3.0.min.js"></script>
<script>
    let selectNum = 0
    function lunboFun (selectNum, time) {
        $('#lunbo').animate({
            'left': -1 * selectNum * 300
        }, time, () => {
            selectNum++
            setTimeout(() => {
                if (selectNum > 4) {
                    selectNum = 0
                    $('ul li:last').css({
                        'position': 'absolute'
                    }, 0)
                    $('ul li:last').animate({
                        'left': -300
                    }, 0)
                    $('#lunbo').animate({
                        'left': 300
                    }, 0)
                } else if (selectNum <3) {
                    $('ul li:last').css({
                        'position': 'relative',
                        'left': 0
                    }, 0)
                }
                lunboFun(selectNum, 1000)
            }, 2000)
        });
    }
    lunboFun(selectNum, 1000)

</script>
</html>
盛装女皇 2022-05-04 13:00:09

No description provided.
占楼大哥~~

凉栀 2022-05-04 03:19:17

这里说一个不需要clone的方案:

<div class="slide">
  <ul>
    <li>图片1</li>
    <li>图片2</li>
   <li>图片3</li>
  </ul>
</div>

1、最外层div.slide定宽、相对定位relative

2、ul 足够宽,最起码li数*li宽度,这里有个技巧,直接 width: 9999em,目的是让里面的所有li一字排开

3、滚动效果通过控制ulleft或者transform来进行滚动效果

4、到了最后一个li,往后看第一个li的时:

4.1、准备继续滚动,把最后一个的li设置为相对定位relativeleft值为此时此刻相对ul的位置(设置的时候不要带缓动效果),目的是让最后一个li不动。

4.2、然后把ul的left或者transform设为0(这步没有缓动效果),

4.3、然后再正常的开始一样出现第一个li的滚动效果(这步有缓动效果)

4.4、最后等无缝的第一个li效果完成后,把最后一个lileft值复原为0

5、到第一个li,往前看最后一个li时,也是和上面同理

具体效果可以看下 汽车之家首页的轮播图效果

枕花眠 2022-05-02 18:53:01

克隆第一张和最后一张作为过渡,在切换时就会显得流畅一些

佞臣 2022-05-02 03:39:30

前一阵使用angular写了一个无缝轮播的组件,与大家思路都差不多
https://github.com/zmh3788/AElfWebsite-Angular/blob/master/src/app/components/app.carousel.component/app.carousel.component.ts

漫雪独思 2022-05-01 19:35:42

无限轮播基本插件都可以做到,不过要使用原生代码实现无缝滚动的话我可以提点思路,
因为轮播图基本都在ul盒子里面的li元素,
首先获取第一个li元素和最后一个li元素,
克隆第一个li元素,和最后一个li元素,
分别插入到lastli的后面和firstli的前面,
然后监听滚动事件,如果滑动距离超过x或-x,让其实现跳转下一张图或者跳转上一张,(此处最好设置滑动距离),
然后在滑动最后一张实现最后一张和克隆第一张的无缝转换,当到克隆的第一张的时候停下的时候,,让其切入真的第一张,则实现无线滑动,向前滑动同理

层林尽染 2022-04-28 08:08:49

最近刚好做一个轮播的公告组件,强答一波:

简单来说,无缝轮播的核心是制造一个连续的效果。最简单的方法就是复制一个轮播的元素,当复制元素将要滚到目标位置后,把原来的元素进行归位的操作,以达到无缝的轮播效果。

贴一段轮播的核心代码:

  // scroll the notice
  useEffect(() => {
    const requestAnimationFrame =
      window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame
    const cancelAnimationFrame =
      window.cancelAnimationFrame || window.webkitCancelAnimationFrame || window.mozCancelAnimationFrame

    const scrollNode = noticeContentEl.current
    const distance = scrollNode.clientWidth / 2

    scrollNode.style.left = scrollNode.style.left || 0
    window.__offset = window.__offset || 0

    let requestId = null
    const scrollLeft = () => {
      const speed = 0.5
      window.__offset = window.__offset + speed
      scrollNode.style.left = -window.__offset + 'px'
      // 关键行:当距离小于偏移量时,重置偏移量
      if (distance <= window.__offset) window.__offset = 0
      requestId = requestAnimationFrame(scrollLeft)
    }
    requestId = requestAnimationFrame(scrollLeft)

    if (pause) cancelAnimationFrame(requestId)
    return () => cancelAnimationFrame(requestId)
  }, [notice, pause])
~没有更多了~

关于作者

0 文章
0 评论
23 人气
更多

推荐作者

胡图图

文章 0 评论 0

zt006

文章 0 评论 0

z祗昰~

文章 0 评论 0

冰葑

文章 0 评论 0

野の

文章 0 评论 0

天空

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文