今天我们实现的效果如下👇
这里,我们使用 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;
}
简单滚动
我们通过 CSS 动画来实现 .jimmy-demo-container-swiper 元素从右侧未显示缓慢向左侧移动,在左侧消逝后,又从右侧开始播放,无限循环这种效果。
我们结合 animaton 和 transform 来实现。
疑问:我们为什么不用 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;
}
实现的效果如下图所示👇
这个时候,你会发现,在黄色区块滚动的过程中,左侧和右侧都是有空白的地方,给人的感觉就比较突兀。那么我们可以通过以下的方法来解决这个问题。
连续滚动
通过 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
那么这样,就会出现循环滚动的方式了。
上面,我们使用的是 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 这个成熟的插件实现。