使用 CSS 从头开始​​编写时间线效果

我报名了Goldstone Project Phase 1 Challenge——瓜分100,000奖池,这是我的第7篇文章, 点击查看活动详情

时间线效果介绍

在前端开发中,我们会遇到一些特别适合用时间轴显示的场景。例如,根据日期和时间记录以下事件:

您还可以制作以下更流行的左右对称卡片样式时间线:

如果加一点装饰,还可以有更显高的效果:

时间线效果精华

从上图我们可以看出时间线效果的本质不外乎以下两点:

  • 使用从上到下的分隔线将可视区域分为左右两部分
  • 分割线中间有一些点代表时间节点,左右显示相关信息

只要掌握如何画分割线,在分割线上画出小圆点,其实可以做出各种时间线效果。今天就带领大家实现下面这个可滚动的时间线,在项目中非常实用。学会了以后,你再也不怕对产品有类似的视觉要求了。

从头开始编写时间线

初始状态

为了教学方便,我们先定义如下空白模板:

HTML结构如下:

<身体>
<div类=“容器”>
< div 类 = "调试区域" >
< button onclick = "add()" >添加活动 </ button >
</ div >
< div class = "时间线区域" >
< div 类 = "时间线包装器" >
< div 类 = “时间线” >
</ div >
</ div >
</ div >
</ div >
</ body >
复制代码

最初的 CSS 样式是:

。容器 {
保证金:汽车;
宽度:350 像素;
高度:350px;
溢出:隐藏;
显示:弯曲;
弹性方向:列;
填充:0;
--左:62px;
--颜色:#0d5dff;
}

.调试区域{
边距:30px 0;
}

.timeline-area {
弹性:1;
位置:相对;
显示:弯曲;
弹性方向:列;
溢出-y:自动;
底部填充:10px;
padding-right: 5px;
边框:1px 实心#ccc;
}
复制代码

布局结构没什么好说的。是一个固定宽高的div,通过flex布局分为上下两部分。上方区域放置了一个按钮用于调试,下方区域是用于显示时间线内容的div,并添加了边框方便大家查看。

您可能注意到上面定义了两个变量 - 剩下 - 颜色 ,这是干什么用的?接下来你就知道了,请继续阅读。

添加分隔线

分隔线很关键,应该如何实现?先不急着写CSS代码,先看一下HTML结构:

< div 类 = "时间线包装器" >
< div 类 = “时间线” >
</ div >
</ div >
复制代码

timeline-wrapper 是一个包装容器,它的宽度和高度是固定的,时间线就是真正的时间线。当时间轴的内容超过容器的高度时,可以自由上下滚动。所以添加以下 CSS 代码:

.timeline-wrapper {
弹性:1;
溢出-y:自动;
填充:15px 5px 0 0;
}

。时间线 {
位置:相对;
}
复制代码

最关键的地方就是这里,使用伪元素 实现分频器:

.timeline ::之前{
内容: ””;
位置:绝对;
左:var(--左);
宽度:1px;
顶部:20px;
底部:0;
背景图像:线性渐变(
到底部,
RGBA(144, 156, 173, 0.6) 60%,
RGBA(255, 255, 255, 0) 0%
);
背景位置:左;
背景尺寸:1px 5px;
背景重复:重复-y;
}
复制代码

设置一个绝对定位,然后从左边开始的宽度就是上面的CSS变量 - 剩下 定义距离,将其宽度设置为 1px,然后我们将样式添加到时间轴:

。时间线 {
高度:500px;
宽度:100%;
}
复制代码

可以看到,中间的分界线已经出来了:

可能很多人不知道怎么脱线?实现虚线的效果有两种。第一个使用边框来设置 dotted 或 dashed 属性:

左边框:1px 虚线 rgba(144、156、173、0.6);
复制代码

另一种是使用 background-image 来模拟:

背景图像:线性渐变(
到底部,
RGBA(144, 156, 173, 0.6) 60%,
RGBA(255, 255, 255, 0) 0%
);
背景位置:左;
背景尺寸:1px 5px;
背景重复:重复-y;
复制代码

后者的确定性更强,控制虚线的间距非常方便。

添加点

线出来后,怎么加一个小点?这时候就需要补充HTML结构了。我们必须在时间线分隔线的两侧布置内容。为了方便用 JS 动态插入内容,我们使用模板来定义时间线中每一项的 HTML 结构:

<模板>
< div class = "时间线项目" >
< div class = "时间线-左" ></ div >
< div 类 = “时间线点” ></ div >
< div class = "时间线-右" ></ div >
</ div >
</ template >
复制代码

然后在head中添加add函数:

<脚本>
常量节点 = [] 函数添加 () { 常量 tpl = 文档。 querySelector ('template') const item = tpl.内容 。儿童 [0] 常量时间线 = 文档。 querySelector ('.timeline') 节点。 forEach (it => it.classList.remove ('current')) const node = item. cloneNode ( true ) 节点。类列表。添加(“当前”)节点。推送(节点)时间线。 appendChild(节点)节点。 scrollIntoView ({ 行为 : 'smooth' , block : 'nearest' , inline : 'center' }) }</ script >
复制代码

这时候,每当我们点击上面的添加活动按钮,就可以动态的复制插入上面的DOM了!

可以看出,时间线-项目内容区域分为时间线-左、时间线-点和时间线-右。顾名思义,它们是时间线内容的左、点和右。我们先写点的样式:

.timeline-dot {
左:var(--左);
宽度:7px;
高度:7px;
位置:绝对;
边界半径:50%;
盒子阴影:0 0 0 1px #d8d8d8;
背景:白色;
文本对齐:居中;
顶部:0;
行高:40px;
左边距:- 3.5px;
}
复制代码

然后为当前活动的点添加高亮样式:

.timeline-item .current .timeline-dot {
宽度:10px;
高度:10px;
背景颜色:var(--color);
盒子阴影:0 0 4px var(--color);
边框:1px 纯白色;
左边距: - 5px;
}
复制代码

此时 - 颜色 变量的作用很明确:用来控制主题颜色。我们反复点击添加活动按钮,可以看到小圆点的效果出来了!

设置左右容器

其实到这里,时间线的原型就已经完成了,剩下的就是根据业务来定义左右两边的内容了。对于left区域,我们需要定义一个宽度小于left的值,否则会超过分割线:

.timeline-left {
显示:块;
宽度:计算(var(--left)- 7px);
位置:绝对;
边距顶部: - 5px;
文本对齐:右;
颜色:#8492a5;
}
复制代码

在右边区域,我们需要定义margin-left的值大于left的值,否则会超过分割线:

.timeline-right {
位置:相对;
边距:- 3px 0 10px calc(var(--left) + 15px);
}
复制代码

为了让大家看到效果,暂时给左右两边设置了背景颜色和高度:

.timeline-left {
背景:黄绿色;
高度:50px;
}
.timeline-right {
背景:黄绿色;
高度:50px;
}
复制代码

可以看到效果出来了:

填写左右内容

为了达到初始动画中的效果,我们来丰富模板中的内容:

<模板>
< div class = "时间线项目" >
< div class = "时间线-左" >
< div class = "开始时间" >14:00</ div >
< div 类 = “持续时间” >1h</ div >
</ div >
< div 类 = “时间线点” ></ div >
< div class = "时间线-右" >
< div class = "title" >和詹姆斯打羽毛球 </ div >
<div类=“内容”>
<div类=“信息”>
< div class = "信息-no" >
< img src = "clock.svg" />
< span class = "info-content" >14:00 ~ 15:00</ span >
</ div >
< div 类 = "信息位置" >

< span class = "info-content" >市中心羽毛球场 </ span >
</ div >
</ div >
<div类=“加入”>
< 按钮 >
报名
</ button >
</ div >
</ div >
</ div >
</ div >
</ template >
复制代码

然后添加一些样式:

.timeline-left {
。开始时间 {
字体大小:16px;
}
。期间 {
字体大小:14px;
显示:弯曲;
对齐项目:居中;
证明内容:弹性结束;
}
}

.timeline-right {
。标题 {
字体大小:15px;
字体粗细:粗体;
@扩展.省略号2;
}
。内容 {
显示:弯曲;
弹性方向:行;
填充:5px 0;
.info {
字体大小:15px;
颜色:rgba($颜色:#1f3858,$alpha:0.6);
弹性:1;
图像{
右边距:5px;
边距顶部: - 2px;
高度:15px;
}
.info-不,
.info-位置{
显示:弯曲;
对齐项目:居中;
边距:5px 0;
.info-icon {
右边距:5px;
}
.info-内容{
弹性:1;
@扩展.省略号2;
}
& .隐藏{
显示:无;
}
}
}
。加入 {
显示:弯曲;
证明内容:弹性结束;
对齐项目:弹性开始;
& .隐藏{
显示:无;
}
}
}
}
复制代码

注意上面的代码是SCSS,这是为了方便使用嵌套语法写,如果想看转换后的CSS源码,可以使用下面的命令全局安装sass预处理器,然后生成上面的SCSS代码到 CSS 中:

$ yarn global 添加 sass
$ sass 时间线.scss > 时间线.css
复制代码

加点修饰,你终于可以达到最初的效果了!

完整代码

HTML 代码:


< html lang = "en" >

<头>
<元字符集=“UTF-8”>
< meta http-equiv = "X-UA-Compatible" 内容 = "IE=edge" >
<元名称=“视口”内容=“宽度=设备宽度,初始比例=1.0”>
时间线</ title ><br> <链接 rel = "样式表" href = "./timeline.css" ><br> <脚本><br> 常量节点 = [] 函数添加 () { 常量 tpl = 文档。 querySelector ('template') const item = tpl.内容 。儿童 [0] 常量时间线 = 文档。 querySelector ('.timeline') 节点。 forEach (it => it.classList.remove ('current')) const node = item. cloneNode ( true ) 节点。类列表。添加(“当前”)节点。推送(节点)控制台。日志(节点,节点。classList)时间线。 appendChild(节点)节点。 scrollIntoView ({ 行为 : 'smooth' , block : 'nearest' , inline : 'center' }) }</ script ><br> </ head ></p> <p><身体><br> <div类=“容器”><br> < div 类 = "调试区域" ><br> < button onclick = "add()" >添加活动 </ button ><br> </ div ><br> < div class = "时间线区域" ><br> < div 类 = "时间线包装器" ><br> < div 类 = “时间线” ><br> </ div ><br> </ div ><br> </ div ><br> </ div ><br> </ body ></p> <p><模板><br> < div class = "时间线项目" ><br> < div class = "时间线-左" ><br> < div class = "开始时间" >14:00</ div ><br> < div 类 = “持续时间” >1h</ div ><br> </ div ><br> < div 类 = “时间线点” ></ div ><br> < div class = "时间线-右" ><br> < div class = "title" >和詹姆斯打羽毛球 </ div ><br> <div类=“内容”><br> <div类=“信息”><br> < div class = "信息-no" ><br> < img src = "clock.svg" /><br> < span class = "info-content" >14:00 ~ 15:00</ span ><br> </ div ><br> < div 类 = "信息位置" ><br> <img src = "location.svg" /><br> < span class = "info-content" >市中心羽毛球场 </ span ><br> </ div ><br> </ div ><br> <div类=“加入”><br> < 按钮 ><br> 报名<br> </ button ><br> </ div ><br> </ div ><br> </ div ><br> </ div ><br> </ template ></p> <p></ html ><br> 复制代码</p> <p>SCSS代码:</p> <p>.省略号 {<br> 溢出:隐藏;<br> 文本溢出:省略号;<br> 空白:nowrap;<br> }<br> .省略号2 {<br> 溢出:隐藏;<br> 空白:nowrap;<br> 文本溢出:省略号;<br> @supports (-webkit-line-clamp: 2) {<br> 空白:初始;<br> 显示:-webkit-box;<br> -webkit-line-clamp:2;<br> -webkit-box-orient:垂直;<br> }<br> }</p> <p>按钮 {<br> 显示:弯曲;<br> 证明内容:中心;<br> 对齐项目:居中;<br> 填充:0 12px;<br> 字体大小:14px;<br> 字体粗细:粗体;<br> 高度:30px;<br> 边框样式:实心;<br> 背景:#0d5dff;<br> 边框颜色:透明;<br> 白颜色;<br> 光标:指针;<br> }</p> <p>。容器 {<br> 保证金:汽车;<br> 宽度:350 像素;<br> 高度:350px;<br> 溢出:隐藏;<br> 显示:弯曲;<br> 弹性方向:列;<br> 填充:0;<br> --左:60px;<br> --颜色:#0d5dff;<br> }</p> <p>.调试区域{<br> 边距:30px 0;<br> }</p> <p>.timeline-area {<br> 弹性:1;<br> 位置:相对;<br> 显示:弯曲;<br> 弹性方向:列;<br> 溢出-y:自动;<br> 底部填充:10px;<br> padding-right: 5px;<br> 边框:1px 实心#ccc;<br> }</p> <p>.timeline-area ::之前{<br> 内容: ””;<br> 显示:块;<br> 位置:绝对;<br> z指数:2;<br> 左:0;<br> 右:12px;<br> 顶部:0;<br> 高度:25px;<br> 背景:线性渐变(<br> 到底部,<br> RGBA(255、255、255、1)、<br> RGBA(255、255、255、0)<br> );<br> }</p> <p>.timeline-area::after {<br> 内容: ””;<br> 显示:块;<br> 位置:绝对;<br> z指数:2;<br> 左:0;<br> 右:12px;<br> 底部:10px;<br> 高度:25px;<br> 背景:线性渐变(<br> 到达顶点,<br> RGBA(255、255、255、1)、<br> RGBA(255、255、255、0)<br> );<br> }</p> <p>.timeline-wrapper {<br> 弹性:1;<br> 溢出-y:自动;<br> 填充:15px 5px 0 0;<br> }</p> <p>.timeline-wrapper::-webkit-scrollbar {<br> 宽度:6px;<br> 背景颜色:透明;<br> }</p> <p>.timeline-wrapper::-webkit-scrollbar-track-piece {<br> 边距:20px;<br> }</p> <p>.timeline-wrapper::-webkit-scrollbar-thumb {<br> 背景颜色:rgba($color: #000000, $alpha: 0.08);<br> }</p> <p>。时间线 {<br> 位置:相对;<br> }</p> <p>.timeline ::之前{<br> 内容: ””;<br> 位置:绝对;<br> 左:var(--左);<br> 宽度:1px;<br> 顶部:20px;<br> 底部:0;<br> 背景图像:线性渐变(<br> 到底部,<br> RGBA(144, 156, 173, 0.6) 60%,<br> RGBA(255, 255, 255, 0) 0%<br> );<br> 背景位置:左;<br> 背景尺寸:1px 5px;<br> 背景重复:重复-y;<br> }</p> <p>.时间线项目{<br> 位置:相对;<br> 显示:内联块;<br> 宽度:100%;<br> 边距顶部:15px;<br> }</p> <p>.timeline-dot {<br> 左:var(--左);<br> 宽度:7px;<br> 高度:7px;<br> 位置:绝对;<br> 边界半径:50%;<br> 盒子阴影:0 0 0 1px #d8d8d8;<br> 背景:白色;<br> 文本对齐:居中;<br> 顶部:0;<br> 行高:40px;<br> 左边距:- 3.5px;<br> }</p> <p>.timeline-item .current .timeline-dot {<br> 宽度:10px;<br> 高度:10px;<br> 背景颜色:var(--color);<br> 盒子阴影:0 0 4px var(--color);<br> 边框:1px 纯白色;<br> 左边距: - 5px;<br> }</p> <p>.timeline-left {<br> 显示:块;<br> 宽度:计算(var(--left)- 7px);<br> 位置:绝对;<br> 边距顶部: - 5px;<br> 文本对齐:右;<br> 颜色:#8492a5;<br> }</p> <p>.timeline-right {<br> 位置:相对;<br> 边距:- 3px 0 10px calc(var(--left) + 15px);<br> }</p> <p>.timeline-item .current .title {<br> 颜色: var(--color);<br> }</p> <p>.timeline-left {<br> 。开始时间 {<br> 字体大小:16px;<br> }<br> 。期间 {<br> 字体大小:14px;<br> 显示:弯曲;<br> 对齐项目:居中;<br> 证明内容:弹性结束;<br> }<br> }</p> <p>.timeline-right {<br> 。标题 {<br> 字体大小:15px;<br> 字体粗细:粗体;<br> @扩展.省略号2;<br> }<br> 。内容 {<br> 显示:弯曲;<br> 弹性方向:行;<br> 填充:5px 0;<br> .info {<br> 字体大小:15px;<br> 颜色:rgba($颜色:#1f3858,$alpha:0.6);<br> 弹性:1;<br> 图像{<br> 右边距:5px;<br> 边距顶部: - 2px;<br> 高度:15px;<br> }<br> .info-不,<br> .info-位置{<br> 显示:弯曲;<br> 对齐项目:居中;<br> 边距:5px 0;<br> .info-icon {<br> 右边距:5px;<br> }<br> .info-内容{<br> 弹性:1;<br> @扩展.省略号2;<br> }<br> & .隐藏{<br> 显示:无;<br> }<br> }<br> }<br> 。加入 {<br> 显示:弯曲;<br> 证明内容:弹性结束;<br> 对齐项目:弹性开始;<br> & .隐藏{<br> 显示:无;<br> }<br> }<br> }<br> }<br> 复制代码</p> <p>这里有很多细节与时间线无关,我在文章中没有解释,但其实很实用。例如,ellipsis和ellipsis2用于设置内容超过1或2行时的溢出效果,-webkit-scrollbar,--webkit-scrollbar-track-piece和-webkit-scrollbar-thumb用于自定义滚动条,有兴趣的读者可以参考我以前的<a href="https://juejin.cn/post/7139049177656819749" target="_blank"> 文章</a> .</p> <p>版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议。转载请附上原文出处链接和本声明。</p> <p>这篇文章的链接:<a href="https://homecpp.art/5223/10409/1834" target="_blank"> https://homecpp.art/5223/10409/1834</a></p> <p>版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明</p> <p>本文链接:<a href="https://www.qanswer.top/39024/38092411" target="_blank">https://www.qanswer.top/39024/38092411</a></p>

posted @ 2022-09-24 11:38  哈哈哈来了啊啊啊  阅读(1528)  评论(0)    收藏  举报