元素内容垂直方向循环滚动


原理及实现方式如下:

方式一

css 代码

.vertical-scroll {
    height: 300px;
    overflow: hidden;
}

html代码

<div class="vertical-scroll">
    <div class="vs-content">
        <div style="height: 30px;">content</div>
        <!-- 固定高度 -->
        <div style="height: 400px;background: url(https://cdn.pixabay.com/photo/2020/02/18/06/25/harvest-4858574_960_720.jpg) no-repeat center/cover;"></div>
        <!-- 不固定高度 -->
        <!-- <img class="imgTag" src="https://cdn.pixabay.com/photo/2020/02/18/06/25/harvest-4858574_960_720.jpg"alt="no"> -->
    </div>
</div>

js代码

function setScrollAnimate() {
    /**
     * 滚动原理:将 .vs-content(视口,其高度要小于内容高度) 复制一份,将两份内容垂直排列,同时利用 css animation translateY 向上移动,
     * 当移动距离达到一个内容的高度,两个元素再同时复位到原始位置,以此重复循环(ps:被复制的内容不会在视口中完整的滚动显示一次,因为其顶部到达视口顶部时,
     * 刚好两个元素都滑动一个内容高度,此时非复制内容的顶部复位到了视口顶部,刚好与复制内容复位前一刻位置重合,以此达到循环滚动效果,所以,复制元素不必完整显示)
    */
    const content = document.querySelector('.vs-content');
    document.querySelector('.vertical-scroll').appendChild(content.cloneNode(true));
    /**
     * 获取内容高度
     * 如果内容中有图片一定确保图片加载完成,否则获取内容高度不准确
     * 或者采用将图片设置为背景,在window.onload 中触发插入样式,让其滚动
    */
    const imgEle = document.querySelector('.imgTag');

    imgEle.addEventListener('load', (e) => {
        insertStyle(content.clientHeight)
    })
}

function insertStyle(h) {
    /**
     * 这里利用js插入样式的原因是要动态获取类容高度
     * 如果内容高度可以确定,可在style样式中直接写如下内容,不需用js注入
     */
    const t = 20000;
    const eleStyle = document.createElement('style');
    eleStyle.innerHTML =
        '.vs-content{padding: 1px 0;height:'+h+'px;will-change:transform;animation: scrollAnimate ' + t + 'ms linear infinite;}' +
        '@keyframes scrollAnimate{from{transform: translateY(0);}to{transform: translateY(-' + h + 'px);}}';
    document.querySelector('head').appendChild(eleStyle);
}

/* 如果需要动态获取内容高度 */
setScrollAnimate()

window.onload = function() {
    /* 固定高度 */
    // insertStyle(432)
}

方式二

CSS

ul,li {  padding: 0; margin: 0 }
.roll-box {
      height: 400px;
      background: #007acc;
      overflow: hidden;
      color: #fff;
 }
#roll li {
      height: 30px;
      border-bottom: 1px solid #ddd
}

HTML

<div class="roll-box">
    <ul id="roll">
    </ul>
</div>

JS

function getData() {
    var htmlStr = '';
    for (var i = 0; i < 20; i++) {
        htmlStr += '<li id="i-' + i + '">' + 'this is ' + i + '</li>'
    }

    return htmlStr;
}

(function roll() {
    var UL_HEIGHT = 400, LI_HEIGHT = 30;

    var ulObj = document.getElementById('roll');
    ulObj.innerHTML = getData();

    var height = ulObj.offsetHeight;
    var move = 0, oneSteep = 0.5;
    var clearIn = '', mouseOut = true;

    // 添加鼠标操作相关事件
    ulObj.addEventListener('mouseenter', function () {
        mouseOut = false
    });
    ulObj.addEventListener('mouseleave', function () {
        mouseOut = true
        animationRoll()
    });
    ulObj.addEventListener('click', function (e) {
        if (e.target.nodeName === 'LI') {
            alert('id is:' + e.target.id)
        }
    });

    // 滚动步骤
    // 1.将外部 ul 移动一个li的高度的距离 
    // 2.将移出的li元素放到最后,实现循环,并复原ul的移动距离。
    function animationRoll() {
        function moveFn () {
            if (mouseOut) {
                move += oneSteep;
                ulObj.setAttribute('style', 'margin-top:-' + move + 'px');
                if (move >= LI_HEIGHT) {
                    move = 0;
                    ulObj.setAttribute('style', 'margin-top:-' + move + 'px');
                    var temp = ulObj.children[0];
                    ulObj.removeChild(temp)
                    ulObj.appendChild(temp)
                }
                clearIn = window.requestAnimationFrame(moveFn)
            } else {
                window.cancelAnimationFrame(clearIn)
            }
        }
        window.cancelAnimationFrame(clearIn)
        clearIn = window.requestAnimationFrame(moveFn)
    }

    if (height > UL_HEIGHT) {
        animationRoll()
    } else {
        console.log(ulObj.offsetHeight);
    }
})()

欢迎交流 Githubopen in new window

Last Updated:
Contributors: Warren