我算是 CSS 初级使用者,在项目中遇到一些比较有意思的效果图时,就先会去 Codepen 中找一些类似的案例参考分析,然后再去细读其中用到的一些 CSS 样式和写法,以此来完成目标效果,譬如下图:

对此设计图共有以下两点说明:

  1. 每一个条目信息具有两种状态和各自的样式 —— 激活与未激活 (finished or unfinished)
  2. 为了保留 HTML 的基本结构,我们在这里采取的是列表(有序无序都可以)
  3. 每一个小圆点和连接线都有两种状态,并且小圆点需要居中,在第一个和最后一个分别没有上半部分线条和下半部分线条

目前实现效果可以在 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 搭配绝对布局的方式来绘制已激活状态的小圆点。
  • 小竖线
    小竖线同样和小圆点一样都具有两种状态,而它的渲染我们采取了使用小圆点所在的<li>元素的左边框作为时间线,并且通过 first-child 或者 last-child 这两个伪类来实现对第一个或者最后一个边框样式的控制。

后记

绝对布局
只会在相对布局的容器元素中生效,也就是说会以相对布局的父元素为参考系进行绝对定位,且与文档流完全无关,相当于浮在参考系的其他子元素之上,但是在定位未指定(这里指 top,right, bottom,left 四个属性) 且可能会影响其布局(尤其是 top 和 left)的属性没有给值时默认以文档流的位置布局,不过并不占用文档位置

react-native 中的绝对布局
相比 CSS 中标准的绝对布局,RN 中的绝地布局相对简单一些。绝对布局的元素的参考系直接就是父元素(默认 position 就是 relative),并且声明在普通元素之后的话,会在层级上覆盖前面的元素,反之则会被覆盖。CSS 中则一贯让绝对布局的元素在 z 轴上最高,如有需要可以通过 z-index 来控制它们的层叠关系。

donation