先看效果:
再看代码(查看更多):
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>计时器</title>
<style>
* {
padding: 0;
margin: 0;
box-sizing: border-box;
}
body {
background: #98ede0;
transition: background 500ms;
font-family: ui-rounded, "Hiragino Maru Gothic ProN", Quicksand, Comfortaa, Manjari, "Arial Rounded MT", "Arial Rounded MT Bold", Calibri, source-sans-pro, sans-serif;
}
.content {
margin: 0 auto;
max-width: 500px;
height: 100vh;
height: 100svh;
min-height: 400px;
display: flex;
flex-direction: column;
padding: 40px 40px 80px;
justify-content: center;
}
a {
transition: color 200ms;
color: rgba(0, 0, 0, 0.5);
border-radius: 5px;
text-decoration: none;
margin: 40px auto 0;
}
a:hover {
color: black;
text-decoration: underline;
}
a:focus-visible {
color: black;
outline: 3px solid rgba(255, 255, 255, 0.3);
outline-offset: 5px;
}
.pagination {
display: flex;
gap: 10px;
height: 4px;
min-height: 4px;
}
.pagination-item {
border-radius: 100px;
height: 100%;
flex: auto;
background: rgba(0, 0, 0, 0.08);
overflow: hidden;
border: 0;
cursor: pointer;
}
@keyframes progress {
from {
width: 0;
}
to {
width: 100%;
}
}
.pagination-progress {
flex: auto;
background: #333;
height: 100%;
width: 0;
}
.pagination-item--running .pagination-progress {
animation: progress 3s linear forwards;
}
.pagination-item--done .pagination-progress {
width: 100%;
}
.pagination--paused .pagination-progress {
animation-play-state: paused;
}
:-moz-window-inactive .pagination-progress {
animation-play-state: paused;
}
.controls {
display: flex;
justify-content: center;
gap: 12px;
}
.control {
display: flex;
align-items: center;
justify-content: center;
border-radius: 100px;
border: none;
outline: none;
height: 40px;
flex: 40px 0 0;
max-width: 100px;
background: rgba(255, 255, 255, 0.3);
transition: transform 200ms, background-color 200ms;
will-change: transform, background-color;
font-size: 20px;
}
.control:hover {
cursor: pointer;
transform: scale(1.15);
background: rgba(255, 255, 255, 0.45);
}
.control:focus {
background: rgba(255, 255, 255, 0.6);
}
.icon {
width: 20px;
}
.icon--play {
margin-right: -2px;
}
.pagination--paused ~ .controls .icon--pause,
.pagination:not(.pagination--paused) ~ .controls .icon--play {
display: none;
}
.state {
font-size: 120px;
text-align: center;
}
</style>
</head>
<body>
<div class="content">
<div class="pagination">
</div>
<div class="state">
</div>
<div class="controls">
<button class="control control--prev" aria-label="Previous">
<svg class="icon" viewBox="0 0 32 32">
<path d="M20 28a1 1 0 0 1-.521-.146l-18-11a1.002 1.002 0 0 1 0-1.708l18-11A.999.999 0 0 1 21 5v6.11a1 1 0 0 1-.479.854L13.918 16l6.603 4.035c.297.182.479.506.479.854V27a1 1 0 0 1-1 1zM3.918 16 19 25.217V21.45l-7.521-4.596a1 1 0 0 1 0-1.707L19 10.55V6.783L3.918 16z" />
<path d="M30 28a1 1 0 0 1-.521-.146l-18-11a1.002 1.002 0 0 1 0-1.708l18-11A.999.999 0 0 1 31 5v22a1 1 0 0 1-1 1zM13.918 16 29 25.217V6.783L13.918 16z" />
</svg>
</button>
<button class="control control--play-pause" aria-label="Play/Pause">
<svg class="icon icon--play" viewBox="0 0 32 32">
<path d="M7 28a.999.999 0 0 1-1-1V5a1 1 0 0 1 1.521-.854l18 11a1.001 1.001 0 0 1 0 1.708l-18 11A1 1 0 0 1 7 28zM8 6.783v18.434L23.082 16 8 6.783z" />
</svg>
<svg class="icon icon--pause" viewBox="0 0 32 32">
<path d="M13 28H7a1 1 0 0 1-1-1V5a1 1 0 0 1 1-1h6a1 1 0 0 1 1 1v22a1 1 0 0 1-1 1zm-5-2h4V6H8v20zM25 28h-6a1 1 0 0 1-1-1V5a1 1 0 0 1 1-1h6a1 1 0 0 1 1 1v22a1 1 0 0 1-1 1zm-5-2h4V6h-4v20z" />
</svg>
</button>
<button class="control control--next" aria-label="Next">
<svg class="icon" viewBox="0 0 32 32">
<path d="M12 28a1 1 0 0 1-1-1v-6.111c0-.348.182-.672.479-.854L18.082 16l-6.603-4.035A1.001 1.001 0 0 1 11 11.11V5a1 1 0 0 1 1.521-.854l18 11a1.002 1.002 0 0 1 0 1.708l-18 11A1 1 0 0 1 12 28zm1-6.55v3.767L28.082 16 13 6.783v3.767l7.521 4.596a1.001 1.001 0 0 1 0 1.708L13 21.45z" />
<path d="M2 28a1 1 0 0 1-1-1V5a1 1 0 0 1 1.521-.854l18 11a1.002 1.002 0 0 1 0 1.708l-18 11A1 1 0 0 1 2 28zM3 6.783v18.434L18.082 16 3 6.783z" />
</svg>
</button>
</div>
<a target="_blank" href="https://blog.csdn.net/qq_35241329?type=blog">博客文章</a>
</div>
</body>
<script>
"use strict";
function getItem(index) {
const item = document.createElement('button');
item.classList.add('pagination-item');
item.addEventListener('animationend', next);
item.addEventListener('click', () => update(index));
const progress = document.createElement('div');
progress.classList.add('pagination-progress');
item.appendChild(progress);
return item;
}
function createItems(itemsCount) {
const items = [];
for (let i = 0; i < itemsCount; i++) {
items.push(getItem(i));
}
return items;
}
function jumpTo(item) {
if (isPaused) {
item.classList.remove(classNames.RUNNING);
item.classList.add(classNames.DONE);
}
else {
item.classList.add(classNames.RUNNING);
item.classList.remove(classNames.DONE);
}
let sibling = item;
while ((sibling = sibling.previousSibling)) {
sibling.classList.remove(classNames.RUNNING);
sibling.classList.add(classNames.DONE);
}
sibling = item;
while ((sibling = sibling.nextSibling)) {
sibling.classList.remove(classNames.RUNNING, classNames.DONE);
}
}
function update(index) {
activeIndex = index;
jumpTo(items[activeIndex]);
// 更新幻灯片和背景颜色
$state.innerHTML = activeIndex + 1;
document.body.style.backgroundColor = colors[activeIndex];
}
function prev() {
if (activeIndex > 0) {
update(activeIndex - 1);
}
}
function next() {
if (activeIndex < ITEMS_COUNT - 1) {
update(activeIndex + 1);
}
}
function playPause() {
$pagination.classList.toggle(classNames.PAUSED);
isPaused = !isPaused;
///如果当前幻灯片已完成,跳到下一张
if (!isPaused && items[activeIndex].classList.contains(classNames.DONE)) {
next();
}
}
const colors = ['#98ede0', '#74b9ff', '#a29bfe', '#fd79a8', '#ffeaa7'];
const classNames = {
RUNNING: 'pagination-item--running',
DONE: 'pagination-item--done',
PAUSED: 'pagination--paused',
};
let activeIndex = 0;
let isPaused = false;
const ITEMS_COUNT = 5;
const items = createItems(ITEMS_COUNT);
const $pagination = document.querySelector('.pagination');
const $state = document.querySelector('.state');
const $prev = document.querySelector('.control--prev');
const $next = document.querySelector('.control--next');
const $playPause = document.querySelector('.control--play-pause');
$pagination.replaceChildren(...items);
$prev.addEventListener('click', prev);
$next.addEventListener('click', next);
$playPause.addEventListener('click', playPause);
update(activeIndex);
</script>
</html>