左右交錯時間軸

用CSS做左右交錯時間軸

Written by Astro Learner

Published on: Sun Jun 28

astro

css

blogging

核心概念

左右交錯時間軸的核心概念可以拆成四個部分:

  1. 中間一條垂直線
  2. 每個事件(Item)
  3. 左右交錯排列
  4. RWD(手機版改成單欄)

第一步:建立 Timeline 元件

建立

src/components/Timeline.astro

先準備資料:

---
const allPosts = Object.values(import.meta.glob('../pages/posts/**/*.md', {eager:true}))
// 1. 先過濾掉「沒有寫 pubDate」的文章,避免後續噴錯
const validPosts = allPosts.filter(post => post.frontmatter && post.frontmatter.pubDate);
// 2. 依照日期由新到舊排序 (若要由舊到新,將 b 和 a 的位置互換即可)
const sortedPosts = validPosts.sort((a, b) => {
return new Date(b.frontmatter.pubDate).getTime() - new Date(a.frontmatter.pubDate).getTime();
});
---

第二步:建立 HTML

每個事件都會有:

Timeline
├── item
│ ├── circle
│ └── card
├── item
├── item

HTML

<div class="timeline">
{sortedPosts.map((post:any) => (
<div class="item">
<a href={post.url}>
<div class="content">
<img src={post.frontmatter.image.url} alt={post.frontmatter.image.alt} class="poster"/>
<span>{post.frontmatter.pubDate.toString().slice(0,10)}</span>
<h3>{post.frontmatter.title}</h3>
<p>{post.frontmatter.description}</p>
</div>
</a>
</div>
))}
</div>

現在只有資料,接下來都是css


第三步:畫中間那條線

先讓 timeline 有相對定位。

.timeline{
position:relative;
max-width:1000px;
margin:auto;
}

接著用 ::before 畫線。

.timeline::before{
content:"";
position:absolute;
left:50%;
top:0;
bottom:0;
width:4px;
background:#ccc;
transform:translateX(-50%);
}

畫面概念


第四步:每個 item 只佔一半

.item{
position:relative;
width:50%;
padding:20px 40px;
box-sizing:border-box;
}

目前全部都在左邊。

□□□□
□□□□

第五步:奇數在左、偶數在右

利用 CSS:

.item:nth-child(odd){
left:0;
}
.item:nth-child(even){
left:50%;
}

結果:

□□□ │
│ □□□
□□□ │
│ □□□

這就是交錯排列。


第六步:加入卡片&海報

.content{
background:white;
padding:20px;
border-radius:10px;
box-shadow:0 4px 12px rgba(0,0,0,.15);
width: 100%;
max-width: 360px;
}
.poster {
width: 100%;
aspect-ratio: 2 / 3;
object-fit: cover;
}
a {
text-decoration: none;
color: inherit;
}

變成

┌────────┐
│2022 │
│開始學習 │
└────────┘

第七步:加入圓點

利用 ::before

.item::before{
content:"";
position:absolute;
width:18px;
height:18px;
border-radius:50%;
background:#3b82f6;
top:35px;
}

左邊:

.item:nth-child(odd)::before{
right:-9px;
}

右邊:

.item:nth-child(even)::before{
left:-9px;
}

畫面:

┌──────┐ ●
│2022 │ │
└──────┘ │
● ┌──────┐
│2023 │
└──────┘

第八步:加入箭頭(可選)

(實作這塊,有點跑掉,不加也挺好看的,所以先放著了哈哈)

左邊:

.item:nth-child(odd) .content::after{
content:"";
position:absolute;
right:-10px;
top:30px;
border-width:10px;
border-style:solid;
border-color:transparent transparent transparent white;
}

右邊:

.item:nth-child(even) .content::after{
content:"";
position:absolute;
left:-20px;
top:30px;
border-width:10px;
border-style:solid;
border-color:transparent white transparent transparent;
}

效果

┌───────┐▶ ●
│ Card │ │
└───────┘ │
● ◀┌───────┐
│ Card │
└───────┘

第九步:手機版

手機不要左右交錯。

@media(max-width:768px){
.timeline::before{
left:25px;
}
.item{
width:100%;
left:0 !important;
padding-left:60px;
padding-right:20px;
}
.item::before{
left:16px !important;
}
}

手機會變成

●──────── Card
●──────── Card
●──────── Card

最終結構

Timeline
├── 中央線
├── 左卡片
│ ●
├──────────────
├──────●
│ 右卡片
├──────────────
├── 左卡片
│ ●
├──────●
│ 右卡片

只要新增資料,版面就會自動依序左右排列。


最後成果在首頁 Homepage

一些感想

原本是打算看網路上有沒有做好的模板,但找了一陣子沒看到,只好直接跟AI說需求,然後照著貼上去,出來的結果竟然還不錯有滿足我的需求。