这篇文章的诞生的原因是来自前段时间有个网友叫小编帮忙写一个图片列表水平滚动效果,不能出现闪跳,影响用户体验。

于是小编思考良久,查阅相关资料和官方文档MDN,想找一些相应滚动案例和动画来参考,调研后发现,除了官网 animation 讲的很认真,其他的讲的都是概念,含糊其辞,或者上来就是一个不知道去哪里粘贴来的源码,不知其所以然,后面放弃了。还得自己动手!!!

经过深思熟虑,采用了animation动画来滚动元素,因为能用关键帧控制动画属性,包括动画方向,动画执行时间和缓动函数。如果对动画animation不太理解的伙伴,可以去官方MDN上查阅。

参考资源

滚动原理解析

考虑布局和滚动关系,于是乎就有了下方的结构图,所有代码就是围绕这个图来实现的。

提示💡

思路:用一个div作为容器parent-container,包括ul作为滚动列表scroll-container,容器宽度设置为max-width和min-width为50rem,设置溢出隐藏,配置相对定位用于实现左右遮罩效果,图中没体现,但源码里有,这里遮罩用的是伪类before和after。滚动容器内部的li放置图片,可以是任意多个,在js中克隆一遍现有元素li,此时容器内部就有两倍的图片数量,这样就可以看到不间断的滚动效果了,元素过多时可以去掉js中克隆的操作,避免多余元素,影响效果。最后使用animation+@keyframes来执行动画,可以通过父元素的data-reverse属性来改变动画向右滚动,默认是向左滚动的。当然,后面的开启动画属性也可以在css中写死,只是通过参数配置显得更加灵活。如果还需要控制其他属性比如动画时间快慢,也可以加到parent-container容器上,通过css变量来改变它。

原理讲完了,图也看完了,这哈上源码,由于微信不支持大图且录制的gif看起来有怪怪的, 所以直接放截图。可以把源码放到html文件中查看效果,那才是实际的丝滑效果。

CSS源码

<style>
 body {
    --bgcolor--: #001C30;
    min-block-size: 100vh;
    display: grid;
    place-items: center;
    background-color: var(--bgcolor--);
    overflow: hidden;
}
.contianer {
    max-width: 50rem;
    min-width: 50rem;
    overflow: hidden;
    position: relative;
}
.contianer::before, .contianer::after {
    content: "";
    width: 100px;
    height: 100%;
    position: absolute;
    z-index: 1;
}
.contianer::before {
    background: linear-gradient(-90deg, transparent,var(--bgcolor--) 80%, var(--bgcolor--) 20%, transparent);
    left: -5%;
    top: 0%;
}
.contianer::after {
    background: linear-gradient(90deg, transparent,var(--bgcolor--) 80%, var(--bgcolor--) 20%, transparent);
    right: -5%;
    top: 0%;
}
.ul-list {
    margin: 0;
    padding-inline: 0;
    list-style: none;
    display: flex;
    flex-wrap: wrap;
    gap: 1rem;
}
.ul-list li {
    line-height: 1;
    width: 12rem;
    background: #10ac84;
    padding: 1rem;
    border-radius: 0.5rem;
    color: #fff;
    text-align: center;
}
.ul-list li img {
    border-radius: 0.5rem;
    width: 100%;
    aspect-ratio: 1 / 1;
    object-fit: cover;
}
.contianer[data-animated='true'] .scroll_item {
    width: max-content;
    flex-wrap: nowrap;
    animation: scroll var(--animation-duration, 30s)
            var(--animation-direction, forwards) linear infinite;
}
.contianer[data-reverse='true'] {
    --animation-direction: reverse;
}
@keyframes scroll {
    to {
        transform: translate(calc(-50% - 0.5rem));
    }
}
</style>

HTML源码

<div class="contianer" data-reverse="true">
    <h2 style="color: #fff;">布依前端--图片列表滚动案例</h2>
    <ul class="ul-list scroll_item">
        <li><img src="https://picsum.photos/id/37/367/267" alt="" srcset=""></li>
        <li><img src="https://picsum.photos/id/55/367/267" alt="" srcset=""></li>
        <li><img src="https://picsum.photos/id/57/367/267" alt="" srcset=""></li>
        <li><img src="https://picsum.photos/id/58/367/267" alt="" srcset=""></li>
        <li><img src="https://picsum.photos/id/55/367/267" alt="" srcset=""></li>
        <li><img src="https://picsum.photos/id/60/367/267" alt="" srcset=""></li>
    </ul>
</div>

JavaScript源码

<script>
const scroller = document.querySelector(".contianer");
excutionAnimate();
function excutionAnimate() {
    if (scroller) {
        scroller.setAttribute("data-animated", true);
        const scrollerInner = 
        scroller.querySelector(".scroll_item");
        const children = Array.from(scrollerInner.children);
        children.forEach((item) => {
            const Item = item.cloneNode(true);
            Item.setAttribute("aria-hidden", true);
            scrollerInner.appendChild(Item);
        });
    }
}
</script>

最终动态效果在文章开头视频查阅。

案例扩展

可以把图片元素替换成文字,像这样。

修改样式表。

 .ul-list li {
    line-height: 1;
    width: 12rem;
    background: #10ac84;
    padding: 1rem;
    border-radius: 0.5rem;
    color: #fff;
    text-align: center;
}

替换li内容为文本。

 <ul class="ul-list scroll_item">
    <li>HTML</li>
    <li>CSS</li>
    <li>JavaScript</li>
    <li>Transtion</li>
    <li>Canvas</li>
    <li>animation</li>
    <li>SVG </li>
</ul>

为了更好体验,粘贴代码到HTML文件,用浏览器打开该文件,最终静态效果图如下: