Naposledy aktivní 10 months ago

這段 HTML + Lit 程式碼使用 Web Components 技術,建立了一個 年份時間軸 (<timeline-element>),用於 以視覺化方式顯示歷史事件或時間序列。它包含 多個事件 (<event-element>),每個事件都有標題和描述,並根據奇偶數自動 排列在時間軸的左右兩側。此時間軸支援 動態效果(懸停陰影、點擊事件),並針對 手機響應式調整顯示方式,適用於 歷史紀錄、產品發展時間軸、事件展示 等場景,提供 互動性高、現代化的時間軸視覺呈現。

timeline_example.html Raw
1<!doctype html>
2<html lang="zh-Hant">
3 <head>
4 <meta charset="UTF-8" />
5 <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6 <title>年份時間軸 - Lit 元素</title>
7 <style>
8 body {
9 font-family: "Microsoft JhengHei", Arial, sans-serif;
10 background-color: #f0f0f0;
11 padding: 20px;
12 margin: 0;
13 }
14 </style>
15 </head>
16 <body>
17 <timeline-element></timeline-element>
18
19 <script type="module">
20 import {
21 html,
22 css,
23 LitElement,
24 } from "https://unpkg.com/lit@2.8.0?module";
25
26 class TimelineElement extends LitElement {
27 static styles = css`
28 :host {
29 --timeline-width: 4px;
30 --timeline-color: #ddd;
31 --event-bg-color: #fff;
32 --event-shadow: 0 3px 10px rgba(0, 0, 0, 0.1);
33 --event-hover-shadow: 0 5px 15px rgba(0, 0, 0, 0.2);
34 --dot-size: 16px;
35 --dot-border-color: #4caf50;
36 --dot-bg-color: #fff;
37 --dot-hover-bg-color: #4caf50;
38
39 display: block;
40 max-width: 800px;
41 margin: 0 auto;
42 position: relative;
43 }
44 .timeline {
45 display: flex;
46 flex-direction: column;
47 position: relative;
48 }
49 .timeline::before {
50 content: "";
51 position: absolute;
52 top: 0;
53 left: 50%;
54 width: var(--timeline-width);
55 height: 100%;
56 background-color: var(--timeline-color);
57 transform: translateX(-50%);
58 }
59 @media (max-width: 768px) {
60 .timeline::before {
61 left: 30px;
62 }
63 }
64 `;
65
66 render() {
67 return html`
68 <div class="timeline" role="list">
69 ${this.events.map(
70 (event, index) => html`
71 <event-element
72 .title=${event.title}
73 .description=${event.description}
74 ?is-odd=${index % 2 === 0}
75 role="listitem"
76 ></event-element>
77 `,
78 )}
79 </div>
80 `;
81 }
82
83 static properties = {
84 events: { type: Array },
85 };
86
87 constructor() {
88 super();
89 this.events = [
90 { title: "1989", description: "這是 1989 年的事件。" },
91 { title: "1995", description: "這是 1995 年的重要事件。" },
92 { title: "2001", description: "2001 年的事件發生了顯著影響。" },
93 { title: "2010", description: "2010 年的事件標誌著新的時代開始。" },
94 ];
95 }
96 }
97
98 class EventElement extends LitElement {
99 static styles = css`
100 :host {
101 display: flex;
102 width: 100%;
103 margin: 20px 0;
104 position: relative;
105 justify-content: flex-end;
106 }
107 :host([is-odd]) {
108 justify-content: flex-start;
109 }
110 .event-content {
111 width: 40%;
112 padding: 15px;
113 background-color: var(--event-bg-color);
114 border-radius: 10px;
115 box-shadow: var(--event-shadow);
116 position: relative;
117 transition: all 0.3s ease;
118 }
119 :host([is-odd]) .event-content {
120 margin-right: 1%;
121 }
122 :host(:not([is-odd])) .event-content {
123 margin-left: 1%;
124 }
125 .event-content:hover {
126 transform: translateY(-5px);
127 box-shadow: var(--event-hover-shadow);
128 }
129 .event-content::before {
130 content: "";
131 position: absolute;
132 top: 20px;
133 width: 0;
134 height: 0;
135 border-style: solid;
136 transition: all 0.3s ease;
137 }
138 :host([is-odd]) .event-content::before {
139 right: -10px;
140 border-width: 10px 0 10px 10px;
141 border-color: transparent transparent transparent var(--event-bg-color);
142 }
143 :host(:not([is-odd])) .event-content::before {
144 left: -10px;
145 border-width: 10px 10px 10px 0;
146 border-color: transparent var(--event-bg-color) transparent transparent;
147 }
148 .event-content:hover::before {
149 border-color: transparent transparent transparent #f8f8f8;
150 }
151 :host(:not([is-odd])) .event-content:hover::before {
152 border-color: transparent #f8f8f8 transparent transparent;
153 }
154 h3 {
155 margin-top: 0;
156 color: #333;
157 }
158 :host::after {
159 content: "";
160 position: absolute;
161 top: 20px;
162 width: var(--dot-size);
163 height: var(--dot-size);
164 background-color: var(--dot-bg-color);
165 border: 4px solid var(--dot-border-color);
166 border-radius: 50%;
167 left: 50%;
168 transform: translateX(-50%);
169 transition: all 0.3s ease;
170 }
171 :host(:hover)::after {
172 background-color: var(--dot-hover-bg-color);
173 }
174 @media (max-width: 768px) {
175 :host,
176 :host([is-odd]) {
177 justify-content: flex-start;
178 }
179 .event-content {
180 width: calc(100% - 60px);
181 margin-left: 60px !important;
182 margin-right: 0 !important;
183 }
184 :host::after {
185 left: 30px;
186 }
187 .event-content::before {
188 left: -10px !important;
189 border-width: 10px 10px 10px 0 !important;
190 border-color: transparent var(--event-bg-color) transparent transparent !important;
191 }
192 .event-content:hover::before {
193 border-color: transparent #f8f8f8 transparent transparent !important;
194 }
195 }
196 `;
197
198 static properties = {
199 title: { type: String },
200 description: { type: String },
201 isOdd: { type: Boolean, reflect: true, attribute: "is-odd" },
202 };
203
204 render() {
205 return html`
206 <div class="event-content" tabindex="0" @click=${this._handleClick}>
207 <h3>${this.title}</h3>
208 <p>${this.description}</p>
209 </div>
210 `;
211 }
212
213 _handleClick() {
214 this.dispatchEvent(new CustomEvent('event-click', {
215 bubbles: true,
216 composed: true,
217 detail: {
218 title: this.title,
219 description: this.description
220 }
221 }));
222 }
223 }
224
225 customElements.define("timeline-element", TimelineElement);
226 customElements.define("event-element", EventElement);
227 </script>
228 </body>
229</html>
230