优雅翻页时钟与倒计时项目说明

优雅翻页时钟与倒计时项目说明

项目概述

这是一个基于纯前端技术栈开发的优雅翻页时钟与倒计时应用,具有现代化的UI设计和流畅的动画效果,具备屏幕常亮属性。

技术栈

  • HTML5
  • CSS3
  • JavaScript (原生)
  • Web Wake Lock API (用于控制屏幕常亮)

主要功能

  1. 实时时钟显示

    • 24小时制时间显示
    • 数字翻页动画效果
  2. 倒计时功能

    • 支持多个预设时间选项(10/20/30/45/60/90分钟)
    • 倒计时结束自动停止
    • 数字翻页动画效果
  3. 屏幕常亮功能

    • 使用Web Wake Lock API
    • 状态指示器显示
    • 页面切换自动重新请求

关键代码实现

1. 翻页动画效果

@keyframes pulse {
    0% { transform: scale(1); }
    50% { transform: scale(1.05); }
    100% { transform: scale(1); }
}

.flip-animation {
    animation: pulse 0.5s ease;
}

2. 屏幕常亮实现

async function requestWakeLock() {
    try {
        wakeLock = await navigator.wakeLock.request('screen');
        updateWakeLockStatus(true);
        console.log('屏幕常亮已启用');
    } catch (err) {
        console.error('启用屏幕常亮失败:', err);
        updateWakeLockStatus(false);
    }
}

3. 倒计时核心逻辑

function updateCountdown() {
    if (!endTime) return;
    const now = new Date();
    const timeDiff = endTime - now;
    
    if (timeDiff <= 0) {
        clearInterval(countdownInterval);
        endTime = null;
        // 更新显示和释放屏幕常亮...
        return;
    }
    
    const hours = String(Math.floor(timeDiff / (1000 * 60 * 60))).padStart(2, '0');
    const minutes = String(Math.floor((timeDiff % (1000 * 60 * 60)) / (1000 * 60))).padStart(2, '0');
    const seconds = String(Math.floor((timeDiff % (1000 * 60)) / 1000)).padStart(2, '0');
    
    // 更新显示...
}

UI设计特点

  1. 毛玻璃效果背景
  2. 渐变背景色
  3. 悬浮效果
  4. 响应式设计
  5. 优雅的动画过渡

运行截图

在这里插入图片描述

实时时钟显示界面
在这里插入图片描述

倒计时功能界面

项目亮点

  1. 纯前端实现,无需后端支持
  2. 现代化UI设计
  3. 流畅的动画效果
  4. 支持屏幕常亮
  5. 响应式设计,支持移动端

使用说明

  1. 打开网页即可看到实时时钟显示
  2. 点击倒计时按钮可启动对应时长的倒计时
  3. 倒计时开始后屏幕会保持常亮
  4. 倒计时结束后自动关闭常亮

浏览器兼容性

  • Chrome 84+
  • Edge 84+
  • Safari 14+
  • Firefox 79+

注:屏幕常亮功能需要浏览器支持Web Wake Lock API

完整代码

<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>优雅翻页时钟与倒计时</title>
    <style>
        body {
            background: linear-gradient(135deg, #1a1a1a, #2d2d2d);
            color: #fff;
            font-family: 'Helvetica Neue', Arial, sans-serif;
            display: flex;
            flex-direction: column;
            align-items: center;
            min-height: 100vh;
            margin: 0;
            padding: 20px;
            overflow-x: hidden;
            overflow-y: hidden;
        }

        .clock-container, .countdown-container {
            background: rgba(255, 255, 255, 0.05);
            backdrop-filter: blur(10px);
            border-radius: 20px;
            padding: 40px;
            box-shadow: 0 15px 35px rgba(0, 0, 0, 0.2);
            margin: 20px 0;
            width: 100%;
            max-width: 800px;
        }

        .countdown-container {
            display: flex;
            flex-direction: column;
            gap: 20px;
        }

        .flip-clock, .countdown-row {
            display: flex;
            gap: 15px;
            justify-content: center;
        }

        .flip-unit {
            position: relative;
            background: rgba(0, 0, 0, 0.3);
            border-radius: 12px;
            padding: 25px 15px;
            min-width: 120px;
            box-shadow: 0 8px 16px rgba(0, 0, 0, 0.2);
            transition: transform 0.3s ease;
        }

        .flip-unit:hover {
            transform: translateY(-5px);
        }

        .flip-card {
            font-size: 64px;
            font-weight: 300;
            color: #fff;
            text-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
            display: flex;
            justify-content: center;
            align-items: center;
        }

        .countdown-row .flip-card {
            font-size: 48px;
        }

        .label {
            text-align: center;
            font-size: 16px;
            color: rgba(255, 255, 255, 0.6);
            margin-top: 10px;
            text-transform: uppercase;
            letter-spacing: 2px;
        }

        .separator {
            display: flex;
            align-items: center;
            font-size: 54px;
            padding: 0 5px;
            color: rgba(255, 255, 255, 0.3);
        }

        .countdown-title {
            text-align: center;
            font-size: 24px;
            color: rgba(255, 255, 255, 0.8);
            margin-bottom: 10px;
        }

        .countdown-buttons {
            display: flex;
            gap: 10px;
            justify-content: center;
            margin-bottom: 20px;
            flex-wrap: wrap;
        }

        .timer-button {
            background: rgba(255, 255, 255, 0.1);
            border: none;
            color: #fff;
            padding: 10px 20px;
            border-radius: 8px;
            cursor: pointer;
            transition: all 0.3s ease;
        }

        .timer-button:hover {
            background: rgba(255, 255, 255, 0.2);
            transform: translateY(-2px);
        }

        .timer-button.active {
            background: rgba(255, 255, 255, 0.3);
        }

        @keyframes pulse {
            0% { transform: scale(1); }
            50% { transform: scale(1.05); }
            100% { transform: scale(1); }
        }

        .flip-animation {
            animation: pulse 0.5s ease;
        }

        @media (max-width: 768px) {
            .flip-unit {
                min-width: 80px;
                padding: 15px 10px;
            }

            .flip-card {
                font-size: 40px;
            }

            .countdown-row .flip-card {
                font-size: 32px;
            }

            .separator {
                font-size: 32px;
            }
        }

        /* 添加常亮状态指示器样式 */
        .wake-lock-status {
            position: fixed;
            bottom: 20px;
            right: 20px;
            background: rgba(255, 255, 255, 0.1);
            padding: 8px 15px;
            border-radius: 20px;
            font-size: 14px;
            display: flex;
            align-items: center;
            gap: 8px;
        }

        .wake-lock-status.active {
            background: rgba(76, 175, 80, 0.2);
        }

        .status-dot {
            width: 8px;
            height: 8px;
            border-radius: 50%;
            background: rgba(255, 255, 255, 0.5);
        }

        .wake-lock-status.active .status-dot {
            background: #4CAF50;
        }
    </style>
</head>
<body>
    <div class="clock-container">
        <div class="flip-clock">
            <div class="flip-unit">
                <div class="flip-card" id="hours">00</div>
                <div class="label"></div>
            </div>
            <div class="separator">:</div>
            <div class="flip-unit">
                <div class="flip-card" id="minutes">00</div>
                <div class="label"></div>
            </div>
            <div class="separator">:</div>
            <div class="flip-unit">
                <div class="flip-card" id="seconds">00</div>
                <div class="label"></div>
            </div>
        </div>
    </div>

    <div class="countdown-container">
        <div class="countdown-title">倒计时</div>
        <div class="countdown-buttons">
            <button class="timer-button" data-minutes="10">10分钟</button>
            <button class="timer-button" data-minutes="20">20分钟</button>
            <button class="timer-button" data-minutes="30">30分钟</button>
            <button class="timer-button" data-minutes="45">45分钟</button>
            <button class="timer-button" data-minutes="60">60分钟</button>
            <button class="timer-button" data-minutes="90">90分钟</button>
        </div>
        <div class="countdown-row" id="countdown">
            <div class="flip-unit">
                <div class="flip-card" id="countdown-hours">00</div>
                <div class="label"></div>
            </div>
            <div class="separator">:</div>
            <div class="flip-unit">
                <div class="flip-card" id="countdown-minutes">00</div>
                <div class="label"></div>
            </div>
            <div class="separator">:</div>
            <div class="flip-unit">
                <div class="flip-card" id="countdown-seconds">00</div>
                <div class="label"></div>
            </div>
        </div>
    </div>

    <div class="wake-lock-status">
        <span class="status-dot"></span>
        <span class="status-text">常亮已关闭</span>
    </div>

    <script>
        let countdownInterval;
        let endTime = null;
        let wakeLock = null;

        function updateClock() {
            const now = new Date();
            const hours = String(now.getHours()).padStart(2, '0');
            const minutes = String(now.getMinutes()).padStart(2, '0');
            const seconds = String(now.getSeconds()).padStart(2, '0');

            updateTimeDisplay('hours', hours);
            updateTimeDisplay('minutes', minutes);
            updateTimeDisplay('seconds', seconds);
        }

        function updateCountdown() {
            if (!endTime) return;

            const now = new Date();
            const timeDiff = endTime - now;

            if (timeDiff <= 0) {
                clearInterval(countdownInterval);
                endTime = null;
                updateTimeDisplay('countdown-hours', '00');
                updateTimeDisplay('countdown-minutes', '00');
                updateTimeDisplay('countdown-seconds', '00');
                
                // 释放屏幕常亮
                if (wakeLock) {
                    wakeLock.release();
                    wakeLock = null;
                }
                return;
            }

            const hours = String(Math.floor(timeDiff / (1000 * 60 * 60))).padStart(2, '0');
            const minutes = String(Math.floor((timeDiff % (1000 * 60 * 60)) / (1000 * 60))).padStart(2, '0');
            const seconds = String(Math.floor((timeDiff % (1000 * 60)) / 1000)).padStart(2, '0');

            updateTimeDisplay('countdown-hours', hours);
            updateTimeDisplay('countdown-minutes', minutes);
            updateTimeDisplay('countdown-seconds', seconds);
        }

        function updateTimeDisplay(elementId, value) {
            const element = document.getElementById(elementId);
            if (element.textContent !== value) {
                element.classList.remove('flip-animation');
                void element.offsetWidth; // 触发重绘
                element.classList.add('flip-animation');
                element.textContent = value;
            }
        }

        function startCountdown(minutes) {
            clearInterval(countdownInterval);
            const now = new Date();
            endTime = new Date(now.getTime() + minutes * 60000);
            updateCountdown();
            countdownInterval = setInterval(updateCountdown, 1000);

            // 启用屏幕常亮
            requestWakeLock();

            // 更新按钮状态
            document.querySelectorAll('.timer-button').forEach(button => {
                button.classList.remove('active');
                if (parseInt(button.dataset.minutes) === minutes) {
                    button.classList.add('active');
                }
            });
        }

        // 请求屏幕常亮
        async function requestWakeLock() {
            try {
                wakeLock = await navigator.wakeLock.request('screen');
                updateWakeLockStatus(true);
                
                wakeLock.addEventListener('release', () => {
                    updateWakeLockStatus(false);
                });
                
                console.log('屏幕常亮已启用');
            } catch (err) {
                console.error('启用屏幕常亮失败:', err);
                updateWakeLockStatus(false);
            }
        }

        // 更新常亮状态显示
        function updateWakeLockStatus(isActive) {
            const statusElement = document.querySelector('.wake-lock-status');
            const statusText = document.querySelector('.status-text');
            
            if (isActive) {
                statusElement.classList.add('active');
                statusText.textContent = '常亮已开启';
            } else {
                statusElement.classList.remove('active');
                statusText.textContent = '常亮已关闭';
            }
        }

        // 监听页面可见性变化
        document.addEventListener('visibilitychange', async () => {
            if (wakeLock !== null && document.visibilityState === 'visible') {
                await requestWakeLock();
            }
        });

        // 设置倒计时按钮事件
        document.querySelectorAll('.timer-button').forEach(button => {
            button.addEventListener('click', () => {
                const minutes = parseInt(button.dataset.minutes);
                startCountdown(minutes);
            });
        });

        setInterval(updateClock, 1000);
        updateClock();
    </script>
</body>
</html>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值