共计 4389 个字符,预计需要花费 11 分钟才能阅读完成。
1. 先看成品
codepen 体验
(因为图片大小问题,只截取了一部分。实际效果是始终滚动,不会有动画复位的割裂感。)
2. 问题拆分
首先看到这个需要的时候,感觉比拟难做,次要是上面的起因:须要斜着滚动,且须要有限,再就是 icon 错位排列的问题。
起初思考了下,对这个问题进行了拆分:
- 编写容器及 icon 等的 css
- 排列好 icon 并且丢到一个 wrapper 中
- 将整个 wrapper 歪斜 30 度
- 为 wrapper 增加有限的横向滚动动画
话不多说,开始写代码
3. 开始口头
为了不便看边界,对于一些 dom 增加了边框~
3.1 创立容器
容器是一个固定大小的比拟好写
.box {
height: 666px;
width: 1182px;
border-radius: 36px;
border: 1px solid;
overflow: hidden;
text-align: center;
font-size: 30px;
}
3.2 而后撸个 icon 进去
这里就不应用图片来做了,对立用这个 mock 一下~
.icon {
width: 267px;
height: 267px;
border-radius: calc(267px * 0.23);
background-image: conic-gradient(hsl(360, 100%, 50%),
hsl(315, 100%, 50%),
hsl(270, 100%, 50%),
hsl(225, 100%, 50%),
hsl(180, 100%, 50%),
hsl(135, 100%, 50%),
hsl(90, 100%, 50%),
hsl(45, 100%, 50%),
hsl(0, 100%, 50%)
);
}
3.3 排列 icon 到 wrapper 中
咱们把脖子沿逆时针方向旋转 30 度发现,实际上就是错落有致的摆放着两排 icon。
咱们依照图示动态的摆放一下:
<div class="box">
<div class="lean-box">
<div class="wrapper">
<div class="icon-pair">
<div class="icon">1</div>
<div class="icon">2</div>
</div>
<div class="icon-pair">
<div class="icon">3</div>
<div class="icon">4</div>
</div>
<div class="icon-pair">
<div class="icon">5</div>
<div class="icon">6</div>
</div>
<div class="icon-pair">
<div class="icon">7</div>
<div class="icon">8</div>
</div>
</div>
</div>
</div>
.lean-box {
display: flex;
transform: rotate(-30deg);
}
.wrapper {
margin-top: 180px;
display: flex;
flex-wrap: nowrap;
}
.wrapper .icon:nth-child(even) {
margin-top: 45px;
transform: translate(155px);
}
.icon-pair {margin-left: 45px;}
.icon {
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 66px;
font-weight: bold;
}
这样看起来就很有精力了,接下来咱们须要让它动起来
3.4 增加动画
这里动起来应用的是 animation,于是,编写下列代码并且增加到 wrapper 上:
@keyframes rowup {
from {transform: translateX(0%);
}
to {transform: translateX(-500px);
}
}
.wrapper {
margin-top: 180px;
display: flex;
flex-wrap: nowrap;
animation: rowup 5s linear infinite;
}
这时候动是动起来了,接下来的问题是,怎么有限的进行滚动呢?
有限到是动静增加 dom,销毁 dom,就是在这一组 wrapper 后创立一组截然不同的 wrapper,等本组齐全隐没后销毁。
可是,这个老本会很高不是吗,而且这种实现形式势必须要随工夫去更新 icon 的地位,肯定有更好的办法
3.5 无缝滚动
animation 动画完结后会回归第一帧,假如咱们让第一帧的动画和最初一帧重合,那么是不是就看起来是无缝的了?
于是我将 wrapper 外面的元素从新拷贝一份放在前面(以后组称为 A,拷贝组称为 B),当动画完结时,B 刚好挪动到 A 的初始地位。
咱们来计算一下:
这里一组 8 个 icon 排两排状况,挪动的宽度应该为 4 个 icon 宽度 + 4 个 margin,(267 4) + (45 4) = 1248px,这样 B 就能够刚好挪动到 A 了
更新下动画:
@keyframes rowup {
from {transform: translateX(0%);
}
to {transform: translateX(-1248px);
}
}
html 也相应更新下:
<div class="box">
<div class="lean-box">
<div class="wrapper">
<div class="icon-pair">
<div class="icon">1</div>
<div class="icon">2</div>
</div>
<div class="icon-pair">
<div class="icon">3</div>
<div class="icon">4</div>
</div>
<div class="icon-pair">
<div class="icon">5</div>
<div class="icon">6</div>
</div>
<div class="icon-pair">
<div class="icon">7</div>
<div class="icon">8</div>
</div>
<div class="icon-pair">
<div class="icon">1</div>
<div class="icon">2</div>
</div>
<div class="icon-pair">
<div class="icon">3</div>
<div class="icon">4</div>
</div>
<div class="icon-pair">
<div class="icon">5</div>
<div class="icon">6</div>
</div>
<div class="icon-pair">
<div class="icon">7</div>
<div class="icon">8</div>
</div>
</div>
</div>
</div>
3.6 欠缺一下
如果每次都要依据组件个数去计算的话,的确有点 low 了,其实会搁置两个截然不同的 icons,所以 translateX 的间隔不须要计算,设置为 -50% 就好了,最终代码如下(能够在 codepen 上体验):
<div class="box">
<div class="lean-box">
<div class="wrapper">
<div class="icon-pair">
<div class="icon">1</div>
<div class="icon">2</div>
</div>
<div class="icon-pair">
<div class="icon">3</div>
<div class="icon">4</div>
</div>
<div class="icon-pair">
<div class="icon">5</div>
<div class="icon">6</div>
</div>
<div class="icon-pair">
<div class="icon">7</div>
<div class="icon">8</div>
</div>
<div class="icon-pair">
<div class="icon">1</div>
<div class="icon">2</div>
</div>
<div class="icon-pair">
<div class="icon">3</div>
<div class="icon">4</div>
</div>
<div class="icon-pair">
<div class="icon">5</div>
<div class="icon">6</div>
</div>
<div class="icon-pair">
<div class="icon">7</div>
<div class="icon">8</div>
</div>
</div>
</div>
</div>
@keyframes rowup {
from {transform: translateX(0%);
}
to {transform: translateX(-50%);
}
}
.box {
height: 666px;
width: 1182px;
border-radius: 36px;
border: 1px solid;
overflow: hidden;
text-align: center;
font-size: 30px;
}
.icon {
width: 267px;
height: 267px;
border-radius: calc(267px * 0.23);
background-image: conic-gradient(hsl(360, 100%, 50%),
hsl(315, 100%, 50%),
hsl(270, 100%, 50%),
hsl(225, 100%, 50%),
hsl(180, 100%, 50%),
hsl(135, 100%, 50%),
hsl(90, 100%, 50%),
hsl(45, 100%, 50%),
hsl(0, 100%, 50%)
);
}
.lean-box {
display: flex;
transform: rotate(-30deg);
}
.wrapper {
margin-top: 180px;
display: flex;
flex-wrap: nowrap;
animation: rowup 5s linear infinite;
}
.wrapper .icon:nth-child(even) {
margin-top: 45px;
transform: translate(155px);
}
.icon-pair {margin-left: 45px;}
.icon {
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 66px;
font-weight: bold;
}