我算是 CSS 初级使用者,在项目中遇到一些比较有意思的效果图时,就先会去 Codepen 中找一些类似的案例参考分析,然后再去细读其中用到的一些 CSS 样式和写法,以此来完成目标效果,譬如下图:
对此设计图共有以下两点说明:
- 每一个条目信息具有两种状态和各自的样式 —— 激活与未激活 (finished or unfinished)
- 为了保留 HTML 的基本结构,我们在这里采取的是列表(有序无序都可以)
- 每一个小圆点和连接线都有两种状态,并且小圆点需要居中,在第一个和最后一个分别没有上半部分线条和下半部分线条
目前实现效果可以在 My Codepen 预览。
实现思路
在实现上,完全通过 HTML 和 CSS 来实现,没有使用一行的 JS 逻辑代码。主要是用了一些伪类和 CSS 属性来实现。
HTML 部分<ul class='time-line'>
<li class='timeline-stone is-completed'>
<div class='list-content'>
<div class='title'>HeloWorld</div>
<span class='time'>1993.08.03</span>
</div>
</li>
<li class='timeline-stone is-completed'>
<div class='list-content'>
<div class='title'>HeloWorld</div>
</div>
</li>
<li class='timeline-stone'>
<div class='list-content'>
<div class='title'>HeloWorld</div>
</div>
</li>
</ul>
这部分没什么讲的,主要是一些 <ul>
无序列表和子项 <li>
以及各自的 css 样式组成的基本结构。这里只是使用 <div>
作为条目内容做结构,内部使用了 flex-box 盒子模型来做了 title 和 time 的左右布局。
CSS 部分(核心)
为了方便起见,直接将重用多次的几个值作为变量放到头部。$purpleBText: #7d5ae7;
$darkText: #666666;
$lightText: #999999;
$darkLine: #eaeaea;
.time-line {
list-style: none; // 不显示列表默认样式(即就是左侧的小黑点或数字)
margin: 0;
position: relative;
top: -18px;
// 列表项内容
.list-content {
display: flex;
font-size: 12px;
padding: 12px 10px 12px 0;
margin-left: 10px;
position: relative;
bottom: -18px;
.title {
flex-grow: 1;
}
.time {
font-weight: 200;
font-size: 12px;
color: $lightText;
}
}
.timeline-stone {
border-left: $darkLine 1px solid;
position: relative;
margin-left: -23px;
font-weight: 200;
color: $darkText;
&::before {
content: '';
width: 5px;
height: 5px;
background-color: $darkLine;
border-radius: 50%;
position: absolute;
bottom: 0;
left: -3px;
}
&.is-completed {
border-left-color: $purpleBText;
z-index: 1;
color: $purpleBText;
font-weight: 600;
&:first-child {
border-left: transparent 1px solid;
}
&::before {
width: 11px;
height: 11px;
background-color: $purpleBText;
opacity: 0.4;
left: -6px;
bottom: -5px;
}
&::after {
content: '';
width: 7px;
height: 7px;
border-radius: 50%;
position: absolute;
background-color: $purpleBText;
left: -4px;
bottom: -3px;
}
}
}
}
圆点居中
图中绿色的表示<li>
每一个子项,而红色则表示条目内容区。可以看到,这里的实现方式是在list-content
内容区采用相对布局将自身向下移动一半列表项高度,将圆点以绝对布局的方式定位到<li>
的底部。圆点的绘制
这里的圆点均采用 pseudo-class 伪类的方式来添加元素,只需要将 content 设置为空串即可代替常规的 HTML 结点。
共有如下两种状态:- unfinished: 就是一个小灰点,只需要在
timeline-stone
样式上给<li>
加 before 伪类即可。 - finished:在
is-completed
样式中,使用 before 和 after 搭配绝对布局的方式来绘制已激活状态的小圆点。
- unfinished: 就是一个小灰点,只需要在
小竖线
小竖线同样和小圆点一样都具有两种状态,而它的渲染我们采取了使用小圆点所在的<li>
元素的左边框作为时间线,并且通过 first-child 或者 last-child 这两个伪类来实现对第一个或者最后一个边框样式的控制。
后记
绝对布局
只会在相对布局的容器元素中生效,也就是说会以相对布局的父元素为参考系进行绝对定位,且与文档流完全无关,相当于浮在参考系的其他子元素之上,但是在定位未指定(这里指 top,right, bottom,left 四个属性) 且可能会影响其布局(尤其是 top 和 left)的属性没有给值时默认以文档流的位置布局,不过并不占用文档位置
react-native 中的绝对布局
相比 CSS 中标准的绝对布局,RN 中的绝地布局相对简单一些。绝对布局的元素的参考系直接就是父元素(默认 position 就是 relative),并且声明在普通元素之后的话,会在层级上覆盖前面的元素,反之则会被覆盖。CSS 中则一贯让绝对布局的元素在 z 轴上最高,如有需要可以通过 z-index 来控制它们的层叠关系。