惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

WordPress大学
WordPress大学
Engineering at Meta
Engineering at Meta
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
Martin Fowler
Martin Fowler
雷峰网
雷峰网
Recent Announcements
Recent Announcements
博客园 - 叶小钗
F
Full Disclosure
C
Check Point Blog
A
About on SuperTechFans
L
LangChain Blog
Vercel News
Vercel News
T
The Blog of Author Tim Ferriss
博客园 - 司徒正美
C
Cybersecurity and Infrastructure Security Agency CISA
C
CXSECURITY Database RSS Feed - CXSecurity.com
Cisco Talos Blog
Cisco Talos Blog
Hugging Face - Blog
Hugging Face - Blog
Recorded Future
Recorded Future
MongoDB | Blog
MongoDB | Blog
Project Zero
Project Zero
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
D
Darknet – Hacking Tools, Hacker News & Cyber Security
Know Your Adversary
Know Your Adversary
D
Docker
U
Unit 42
酷 壳 – CoolShell
酷 壳 – CoolShell
T
The Exploit Database - CXSecurity.com
L
Lohrmann on Cybersecurity
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Latest news
Latest news
V
Visual Studio Blog
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
L
LINUX DO - 最新话题
Google DeepMind News
Google DeepMind News
小众软件
小众软件
NISL@THU
NISL@THU
GbyAI
GbyAI
H
Heimdal Security Blog
Jina AI
Jina AI
I
InfoQ
PCI Perspectives
PCI Perspectives
P
Privacy International News Feed
P
Proofpoint News Feed
N
News and Events Feed by Topic
C
CERT Recently Published Vulnerability Notes
aimingoo的专栏
aimingoo的专栏
SecWiki News
SecWiki News
B
Blog RSS Feed
阮一峰的网络日志
阮一峰的网络日志

gorpeln的个人博客 - gorpeln

免费领取Synology C2的30G对象存储空间 | gorpeln 用HuggingFace搭建100G超大图床 | gorpeln 毫无破绽的自我批评 | gorpeln's Blog 时光本的故事 - 始于热爱,归于珍藏 | gorpeln's Blog 给博客图片添加水印 | gorpeln's Blog 博客CDN流量异常 | gorpeln's Blog 一路生花 | gorpeln's Blog 10 周年:时光为证,与你共赴新程 | gorpeln's Blog 时光本 - 数据导出 | gorpeln's Blog 时光本 - 下线公告 | gorpeln's Blog 整蛊代码:你屏幕上有根毛 | gorpeln's Blog 博客阅读进度条 | gorpeln's Blog 手机真的在监听你的谈话? | gorpeln's Blog 使用Serv00部署在线工具箱 | gorpeln's Blog 自定义博客文章卡片 | gorpeln's Blog 个人博客可以尝试的 100 件事 | gorpeln's Blog MathJax的基本使用 | gorpeln's Blog Mac应用发布错误:ITMS-91109 | gorpeln's Blog 春节灯笼 | gorpeln's Blog 彩带、樱花背景 | gorpeln's Blog
节日倒计时与时间进度条 | gorpeln's Blog
gorpeln · 2024-12-21 · via gorpeln的个人博客 - gorpeln

不仅能够精准显示距离特定节日的剩余时间,满足用户对重要节庆的期待与关注,还能直观展示今日、本周、本月乃至本年已悄然流逝的时间比例,帮助用户实时洞察时间的流转,让每一刻都能在时间的刻度中清晰呈现,实现对时间的高效感知与管理。

效果
20241221151857321

方法
1、 countdown.html

<div class="countdownNav" id="countdownNav">
    <div class="card-widget card-countdown">
        <div class="item-headline"><i></i><span></span></div>
        <div class="item-content">
            <div class="cd-count-left">
                <span class="cd-text">距离</span>
                <span class="cd-name" id="eventName">节日</span>
                <span class="cd-time" id="daysUntil">000</span>
                <span class="cd-date" id="eventDate"></span>
            </div>
            <div id="countRight" class="cd-count-right">
                <div id="countRight" class="cd-count-right">
                    <div class="cd-count-item">
                        <div class="cd-item-name">今日</div>
                        <div class="cd-item-progress"></div>
                    </div>
                    <div class="cd-count-item">
                        <div class="cd-item-name">本周</div>
                        <div class="cd-item-progress"></div>
                    </div>
                    <div class="cd-count-item">
                        <div class="cd-item-name">本月</div>
                        <div class="cd-item-progress"></div>
                    </div>
                    <div class="cd-count-item">
                        <div class="cd-item-name">本年</div>
                        <div class="cd-item-progress"></div>
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>

2、 countdown-1.js

const CountdownTimer = (() => {
    const config = {
        targetDate: "2025-10-01",
        targetName: "国庆",
        units: {
            day: { text: "今日", unit: "小时" },
            week: { text: "本周", unit: "天" },
            month: { text: "本月", unit: "天" },
            year: { text: "本年", unit: "天" }
        }
    };

    const calculators = {
        day: () => {
            const hours = new Date().getHours();
            return {
                remaining: 24 - hours,
                percentage: (hours / 24) * 100
            };
        },
        week: () => {
            const day = new Date().getDay();
            const passed = day === 0 ? 6 : day - 1;
            return {
                remaining: 6 - passed,
                percentage: ((passed + 1) / 7) * 100
            };
        },
        month: () => {
            const now = new Date();
            const total = new Date(now.getFullYear(), now.getMonth() + 1, 0).getDate();
            const passed = now.getDate() - 1;
            return {
                remaining: total - passed,
                percentage: (passed / total) * 100
            };
        },
        year: () => {
            const now = new Date();
            const start = new Date(now.getFullYear(), 0, 1);
            const total = 365 + (now.getFullYear() % 4 === 0 ? 1 : 0);
            const passed = Math.floor((now - start) / 86400000);
            return {
                remaining: total - passed,
                percentage: (passed / total) * 100
            };
        }
    };

    function updateCountdown() {
        const elements = ['eventName', 'eventDate', 'daysUntil', 'countRight']
           .map(id => document.getElementById(id));

        if (elements.some(el => !el)) return;

        const [eventName, eventDate, daysUntil, countRight] = elements;
        const now = new Date();
        const target = new Date(config.targetDate);

        eventName.textContent = config.targetName;
        eventDate.textContent = config.targetDate;
        daysUntil.textContent = Math.round((target - now.setHours(0, 0, 0, 0)) / 86400000);

        countRight.innerHTML = Object.entries(config.units)
           .map(([key, { text, unit }]) => {
                const { remaining, percentage } = calculators[key]();
                return `
                    <div class="cd-count-item">
                        <div class="cd-item-name">${text}</div>
                        <div class="cd-item-progress">
                            <div class="cd-progress-bar" style="width: ${percentage}%; opacity: ${percentage}"></div>
                            <span class="cd-percentage ${percentage >= 46 ? 'cd-many' : ''}">${percentage.toFixed(2)}%</span>
                            <span class="cd-remaining ${percentage >= 60 ? 'cd-many' : ''}">
                                <span class="cd-tip">还剩</span>${remaining}<span class="cd-tip">${unit}</span>
                            </span>
                        </div>
                    </div>
                `;
            }).join('');
    }

    let timer;
    const start = () => {
        updateCountdown();
        timer = setInterval(updateCountdown, 600000);
    };

    ['pjax:complete', 'DOMContentLoaded'].forEach(event => document.addEventListener(event, start));
    document.addEventListener('pjax:send', () => timer && clearInterval(timer));

    return { start, stop: () => timer && clearInterval(timer) };
})();

优化后代码,实现倒计时包含多个时间,自动识别最近的期望时间,如果现在时间超过了所有的期望时间,就以最后一个期望时间为准。两个js二选一即可。
countdown-2.js

const CountdownTimer = (() => {
    const config = {
        events: [
            { targetDate: "2025-04-04", targetName: "清明节" },
            { targetDate: "2025-05-01", targetName: "劳动节" },
            { targetDate: "2025-05-31", targetName: "端午节" },
            { targetDate: "2025-10-01", targetName: "国庆节" },    
            { targetDate: "2026-02-17", targetName: "春节" }            
        ],
        units: {
            day: { text: "今日", unit: "小时" },
            week: { text: "本周", unit: "天" },
            month: { text: "本月", unit: "天" },
            year: { text: "本年", unit: "天" }
        }
    };

    const calculators = {
        day: () => {
            const hours = new Date().getHours();
            return {
                remaining: 24 - hours,
                percentage: (hours / 24) * 100
            };
        },
        week: () => {
            const day = new Date().getDay();
            const passed = day === 0 ? 6 : day - 1;
            return {
                remaining: 6 - passed,
                percentage: ((passed + 1) / 7) * 100
            };
        },
        month: () => {
            const now = new Date();
            const total = new Date(now.getFullYear(), now.getMonth() + 1, 0).getDate();
            const passed = now.getDate() - 1;
            return {
                remaining: total - passed,
                percentage: (passed / total) * 100
            };
        },
        year: () => {
            const now = new Date();
            const start = new Date(now.getFullYear(), 0, 1);
            const total = 365 + (now.getFullYear() % 4 === 0 ? 1 : 0);
            const passed = Math.floor((now - start) / 86400000);
            return {
                remaining: total - passed,
                percentage: (passed / total) * 100
            };
        }
    };

    function getNextEvent() {
        const now = new Date();
        for (let i = 0; i < config.events.length; i++) {
            const eventDate = new Date(config.events[i].targetDate);
            if (eventDate > now) {
                return config.events[i];
            }
        }
        return config.events[config.events.length - 1];
    }

    function updateCountdown() {
        const elements = ['eventName', 'eventDate', 'daysUntil', 'countRight']
           .map(id => document.getElementById(id));

        if (elements.some(el => !el)) return;

        const [eventName, eventDate, daysUntil, countRight] = elements;
        const now = new Date();
        const nextEvent = getNextEvent();
        const target = new Date(nextEvent.targetDate);

        eventName.textContent = nextEvent.targetName;
        eventDate.textContent = nextEvent.targetDate;
        daysUntil.textContent = Math.round((target - now.setHours(0, 0, 0, 0)) / 86400000);

        countRight.innerHTML = Object.entries(config.units)
           .map(([key, { text, unit }]) => {
                const { remaining, percentage } = calculators[key]();
                return `
                    <div class="cd-count-item">
                        <div class="cd-item-name">${text}</div>
                        <div class="cd-item-progress">
                            <div class="cd-progress-bar" style="width: ${percentage}%; opacity: ${percentage}"></div>
                            <span class="cd-percentage ${percentage >= 46 ? 'cd-many' : ''}">${percentage.toFixed(2)}%</span>
                            <span class="cd-remaining ${percentage >= 60 ? 'cd-many' : ''}">
                                <span class="cd-tip">还剩</span>${remaining}<span class="cd-tip">${unit}</span>
                            </span>
                        </div>
                    </div>
                `;
            }).join('');
    }

    let timer;
    const start = () => {
        updateCountdown();
        timer = setInterval(updateCountdown, 600000);
    };

    ['pjax:complete', 'DOMContentLoaded'].forEach(event => document.addEventListener(event, start));
    document.addEventListener('pjax:send', () => timer && clearInterval(timer));

    return { start, stop: () => timer && clearInterval(timer) };
})();

3、 style.css

/*倒计时*/
.countdownNav {
    margin-top: 1em;
    background-color: var(--box-color-light);
    -moz-border-radius: 10px;
    -webkit-border-radius: 10px;
    overflow: hidden;
    width: 100%;
}
.card-countdown {
    margin: 0.75em 1.5em;
}

.card-countdown .item-content {
    display: flex;
}

.cd-count-left {
    position: relative;
    display: flex;
    flex-direction: column;
    margin-right: 0.8rem;
    line-height: 1.5;
    align-items: center;
    justify-content: center;
}

.cd-count-left .cd-text {
    font-size: 14px;
}

.cd-count-left .cd-name {
    font-weight: bold;
    font-size: 18px;
}

.cd-count-left .cd-time {
    font-size: 30px;
    font-weight: bold;
    color: #dad9e6;
}

.cd-count-left .cd-date {
    font-size: 12px;
    opacity: 0.6;
}

.cd-count-left::after {
    content: "";
    position: absolute;
    right: -0.8rem;
    width: 2px;
    height: 80%;
    background-color: #dad9e6;
    opacity: 0.5;
}

.cd-count-right {
    flex: 1;
    margin-left: .8rem;
    display: flex;
    flex-direction: column;
    justify-content: space-between;
}

.cd-count-item {
    display: flex;
    flex-direction: row;
    align-items: center;
    height: 24px;
}

.cd-item-name {
    font-size: 14px;
    margin-right: 0.8rem;
    white-space: nowrap;
}

.cd-item-progress {
    position: relative;
    display: flex;
    flex-direction: row;
    align-items: center;
    justify-content: space-between;
    height: 100%;
    width: 100%;
    border-radius: 8px;
    background-color: var(--background-color-light);
    overflow: hidden;
}

.cd-progress-bar {
    height: 100%;
    border-radius: 8px;
    background-color: #dad9e6;
}

.cd-percentage,
.cd-remaining {
    position: absolute;
    font-size: 12px;
    margin: 0 6px;
    transition: opacity 0.3s ease-in-out, transform 0.3s ease-in-out;
}

.cd-many {
    color: #fff;
}

.cd-remaining {
    opacity: 0;
    transform: translateX(10px);
}

.card-countdown .item-content:hover .cd-remaining {
    transform: translateX(0);
    opacity: 1;
}

.card-countdown .item-content:hover .cd-percentage {
    transform: translateX(-10px);
    opacity: 0;
}