CSS 实现循环滚动效果

377 阅读2分钟

今天我们实现的效果如下👇

这里,我们使用 React 来实现这效果。

简单布局

我们有以下的布局:

import React from "react";

const prefixCls = "jimmy-demo";

const Test = function() {
  return <div className={prefixCls}>
    <div className={`${prefixCls}-container`}>
      <div className={`${prefixCls}-container-swiper`}>
        <h1>Demo</h1>
      </div>
    </div>
  </div>;
}

// ... other content

Ok.👌 样式添加如下:

.jimmy-demo {
  width: 900px;
  height: 300px;
  &-container {
    width: 100%;
    height: 100%;
    background: blue;
    overflow: hidden;
    &-swiper {
      width: 100%;
      height: 100%;
      background: yellow;
    }
  }
}

初始效果

咦,布局的时候,我们发现了个问题,黄色的内容应该是要完全占据蓝色的区间才对。

这是由于 BFC(Block Formatting Context) 区块格式化上下文造成的,这里的区间底部溢出是由于 h1 元素的默认外边距引起的。我们可以在其父组件上添加 overflow: hidden; 来解决。

 &-swiper {
   // ... other
   overflow: hidden;
 }

解决BFC

简单滚动

我们通过 CSS 动画来实现 .jimmy-demo-container-swiper 元素从右侧未显示缓慢向左侧移动,在左侧消逝后,又从右侧开始播放,无限循环这种效果。

我们结合 animatontransform 来实现。

疑问:我们为什么不用 position, left 来实现移动,而是 transform 呢?

点击查看答案 因为使用 position, left, top 等属性会造成 DOM 元素的重绘回流,而 transform 是使用的硬件加速,在 composite 阶段完成,性能更佳。
// 关键帧
@keyframes HorizontalScroll {
  0% {
    transform: translateX(100%);
  }
  100% {
    transform: translateX(-100%);
  }
}

然后在元素 .jimmy-demo-container-swiper 中使用:

&-swiper {
  animation: HorizontalScroll 12s linear infinite;
}

实现的效果如下图所示👇

swiper-normal.gif

这个时候,你会发现,在黄色区块滚动的过程中,左侧和右侧都是有空白的地方,给人的感觉就比较突兀。那么我们可以通过以下的方法来解决这个问题。

连续滚动

通过 CSS:before:after 的伪类元素来实现。

首先,我们需要将 h1 父元素的容器的 overflow: hidden; 给移除,允许元素超出范围展示,然后对 h1 元素进行改造:

&-swiper {
  // overflow: hidden; // 屏蔽,允许溢出能够看到
  // other content
  h1 {
    width: 100%;
    height: 100%;
    padding: 0;
    margin: 0;
    position: relative;
    &:before, &:after {
      display: inline-block;
      content: "Demo",
      width: 100%,
      height: "100%",
      background: green;
      position: absolute;
      top: 0;
    }
    &:before {
      left: -100%;
    }
    &:after {
      left: 100%;
    }
  }
}
// keyframes

那么这样,就会出现循环滚动的方式了。

swiper-normal-before-after.gif

上面,我们使用的是 content 的值来实现简单的内容。如果是复杂的内容,我们还是需要借助 HTML 来创建元素。

import React from "react";

const prefixCls = "jimmy-demo";

const Test = function() {
  return <div className={prefixCls}>
    <div className={`${prefixCls}-container`}>
     {
       
       <div className={`${prefixCls}-container-swiper`}>
         ["before", "middle", "after"].map((item, index) => {
           <div className={classNames(`${prefixCls}-container-swiper-item`, `${prefixCls}-container-swiper-item--${item}`)} key={index}>
             <h1>Title</h1>
             <p>Description</p>
             <!-- other content -->
           </div>
         })
       </div>
     }
    </div>
  </div>;
}
// ...

通过 CSS 实现,把对应的 ::before::after 改成相关的 .jimmy-demo-container-swiper-item.jimmy-demo-container-swiper-item--before.jimmy-demo-container-swiper-item--middle.jimmy-demo-container-swiper-item--after 四个类的样式内容即可。感兴趣的读者可以参考实现。

当然,如果是更加复杂的场景,建议使用 swiper 这个成熟的插件实现。

参考