timeline_example.html
· 6.8 KiB · HTML
Неформатований
<!doctype html>
<html lang="zh-Hant">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>年份時間軸 - Lit 元素</title>
<style>
body {
font-family: "Microsoft JhengHei", Arial, sans-serif;
background-color: #f0f0f0;
padding: 20px;
margin: 0;
}
</style>
</head>
<body>
<timeline-element></timeline-element>
<script type="module">
import {
html,
css,
LitElement,
} from "https://unpkg.com/lit@2.8.0?module";
class TimelineElement extends LitElement {
static styles = css`
:host {
--timeline-width: 4px;
--timeline-color: #ddd;
--event-bg-color: #fff;
--event-shadow: 0 3px 10px rgba(0, 0, 0, 0.1);
--event-hover-shadow: 0 5px 15px rgba(0, 0, 0, 0.2);
--dot-size: 16px;
--dot-border-color: #4caf50;
--dot-bg-color: #fff;
--dot-hover-bg-color: #4caf50;
display: block;
max-width: 800px;
margin: 0 auto;
position: relative;
}
.timeline {
display: flex;
flex-direction: column;
position: relative;
}
.timeline::before {
content: "";
position: absolute;
top: 0;
left: 50%;
width: var(--timeline-width);
height: 100%;
background-color: var(--timeline-color);
transform: translateX(-50%);
}
@media (max-width: 768px) {
.timeline::before {
left: 30px;
}
}
`;
render() {
return html`
<div class="timeline" role="list">
${this.events.map(
(event, index) => html`
<event-element
.title=${event.title}
.description=${event.description}
?is-odd=${index % 2 === 0}
role="listitem"
></event-element>
`,
)}
</div>
`;
}
static properties = {
events: { type: Array },
};
constructor() {
super();
this.events = [
{ title: "1989", description: "這是 1989 年的事件。" },
{ title: "1995", description: "這是 1995 年的重要事件。" },
{ title: "2001", description: "2001 年的事件發生了顯著影響。" },
{ title: "2010", description: "2010 年的事件標誌著新的時代開始。" },
];
}
}
class EventElement extends LitElement {
static styles = css`
:host {
display: flex;
width: 100%;
margin: 20px 0;
position: relative;
justify-content: flex-end;
}
:host([is-odd]) {
justify-content: flex-start;
}
.event-content {
width: 40%;
padding: 15px;
background-color: var(--event-bg-color);
border-radius: 10px;
box-shadow: var(--event-shadow);
position: relative;
transition: all 0.3s ease;
}
:host([is-odd]) .event-content {
margin-right: 1%;
}
:host(:not([is-odd])) .event-content {
margin-left: 1%;
}
.event-content:hover {
transform: translateY(-5px);
box-shadow: var(--event-hover-shadow);
}
.event-content::before {
content: "";
position: absolute;
top: 20px;
width: 0;
height: 0;
border-style: solid;
transition: all 0.3s ease;
}
:host([is-odd]) .event-content::before {
right: -10px;
border-width: 10px 0 10px 10px;
border-color: transparent transparent transparent var(--event-bg-color);
}
:host(:not([is-odd])) .event-content::before {
left: -10px;
border-width: 10px 10px 10px 0;
border-color: transparent var(--event-bg-color) transparent transparent;
}
.event-content:hover::before {
border-color: transparent transparent transparent #f8f8f8;
}
:host(:not([is-odd])) .event-content:hover::before {
border-color: transparent #f8f8f8 transparent transparent;
}
h3 {
margin-top: 0;
color: #333;
}
:host::after {
content: "";
position: absolute;
top: 20px;
width: var(--dot-size);
height: var(--dot-size);
background-color: var(--dot-bg-color);
border: 4px solid var(--dot-border-color);
border-radius: 50%;
left: 50%;
transform: translateX(-50%);
transition: all 0.3s ease;
}
:host(:hover)::after {
background-color: var(--dot-hover-bg-color);
}
@media (max-width: 768px) {
:host,
:host([is-odd]) {
justify-content: flex-start;
}
.event-content {
width: calc(100% - 60px);
margin-left: 60px !important;
margin-right: 0 !important;
}
:host::after {
left: 30px;
}
.event-content::before {
left: -10px !important;
border-width: 10px 10px 10px 0 !important;
border-color: transparent var(--event-bg-color) transparent transparent !important;
}
.event-content:hover::before {
border-color: transparent #f8f8f8 transparent transparent !important;
}
}
`;
static properties = {
title: { type: String },
description: { type: String },
isOdd: { type: Boolean, reflect: true, attribute: "is-odd" },
};
render() {
return html`
<div class="event-content" tabindex="0" @click=${this._handleClick}>
<h3>${this.title}</h3>
<p>${this.description}</p>
</div>
`;
}
_handleClick() {
this.dispatchEvent(new CustomEvent('event-click', {
bubbles: true,
composed: true,
detail: {
title: this.title,
description: this.description
}
}));
}
}
customElements.define("timeline-element", TimelineElement);
customElements.define("event-element", EventElement);
</script>
</body>
</html>
| 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 |