点击查看html编辑器说明文档

时钟动画edit icon

|
|
Fork(复制)
|
|
作者:
用户fkxIv10
提交反馈
嵌入
设置
下载
HTML
格式化
支持Emmet,输入 p 后按 Tab键试试吧!
<head> ...
展开
</head>
<body>
            
            <div class="clock" aria-label="00:00:00 AM">
    <div class="clock__block clock__block--delay2" aria-hidden="true" data-time-group>
        <div class="clock__digit-group">
            <div class="clock__digits" data-time="a">00</div>
            <div class="clock__digits" data-time="b">00</div>
        </div>
    </div>
    <div class="clock__colon"></div>
    <div class="clock__block clock__block--delay1" aria-hidden="true" data-time-group>
        <div class="clock__digit-group">
            <div class="clock__digits" data-time="a">00</div>
            <div class="clock__digits" data-time="b">00</div>
        </div>
    </div>
    <div class="clock__colon"></div>
    <div class="clock__block" aria-hidden="true" data-time-group>
        <div class="clock__digit-group">
            <div class="clock__digits" data-time="a">00</div>
            <div class="clock__digits" data-time="b">00</div>
        </div>
    </div>
    <div class="clock__block clock__block--delay2 clock__block--small" aria-hidden="true" data-time-group>
        <div class="clock__digit-group">
            <div class="clock__digits" data-time="a">PM</div>
            <div class="clock__digits" data-time="b">AM</div>
        </div>
    </div>
</div>
        
</body>
CSS
格式化
            
            * {
    border: 0;
    box-sizing: border-box;
    margin: 0;
    padding: 0;
}
:root {
    --hue: 223;
    --bg: hsl(var(--hue),90%,90%);
    --fg: hsl(var(--hue),10%,10%);
    --primary: hsl(var(--hue),90%,55%);
    --trans-dur: 0.3s;
    font-size: calc(16px + (20 - 16) * (100vw - 320px) / (1280 - 320));
}
body {
    background-color: var(--bg);
    color: var(--fg);
    font: 1em/1.5 "DM Sans", sans-serif;
    height: 100vh;
    display: grid;
    place-items: center;
    transition:
        background-color var(--trans-dur),
        color var(--trans-dur);
}

.clock {
    display: flex;
    flex-direction: column;
    flex-wrap: wrap;
    align-items: center;
}
.clock__block {
    background-color: hsl(var(--hue),10%,90%);
    border-radius: 0.5rem;
    box-shadow: 0 1rem 2rem hsla(var(--hue),90%,50%,0.3);
    font-size: 3em;
    line-height: 2;
    margin: 0.75rem;
    overflow: hidden;
    text-align: center;
    width: 6rem;
    height: 6rem;
    transition:
        background-color var(--trans-dur),
        box-shadow var(--trans-dur);
}
.clock__block--small {
    border-radius: 0.25rem;
    box-shadow: 0 0.5rem 2rem hsla(var(--hue),90%,50%,0.3);
    font-size: 1em;
    line-height: 3;
    width: 3rem;
    height: 3rem;
}
.clock__colon {
    display: none;
    font-size: 2em;
    opacity: 0.5;
    position: relative;
}
.clock__colon:before,
.clock__colon:after {
    background-color: currentColor;
    border-radius: 50%;
    content: "";
    display: block;
    position: absolute;
    top: -0.05em;
    left: -0.05em;
    width: 0.1em;
    height: 0.1em;
    transition: background-color var(--trans-dur);
}
.clock__colon:before {
    transform: translateY(-200%);
}
.clock__colon:after {
    transform: translateY(200%);    
}
.clock__digit-group {
    display: flex;
    flex-direction: column-reverse;
}
.clock__digits {
    width: 100%;
    height: 100%;
}
.clock__block--bounce {
    animation: bounce 0.75s; 
}
.clock__block--bounce .clock__digit-group {
    animation: roll 0.75s ease-in-out forwards; 
    transform: translateY(-50%);
}
.clock__block--delay1,
.clock__block--delay1 .clock__digit-group {
    animation-delay: 0.1s;
}
.clock__block--delay2,
.clock__block--delay2 .clock__digit-group {
    animation-delay: 0.2s;
}

/* Dark theme */
/* @media (prefers-color-scheme: dark) { */
    :root {
        --bg: hsl(var(--hue),10%,10%);
        --fg: hsl(var(--hue),10%,90%);
    }
    .clock__block {
        background-color: hsl(var(--hue),90%,40%);
        box-shadow: 0 1rem 2rem hsla(var(--hue),90%,60%,0.4);
    }
    .clock__block--small {
        box-shadow: 0 0.5rem 2rem hsla(var(--hue),90%,60%,0.4);
    }
/* } */

/* Beyond mobile */
@media (min-width: 168px) {
    .clock {
        flex-direction: row;
    }
    .clock__colon {
        display: inherit;
    }
}

/* Animations */
@keyframes bounce {
    from,
    to {
        animation-timing-function: ease-in;
        transform: translateY(0);
    }
    50% {
        animation-timing-function: ease-out;
        transform: translateY(15%);
    }
}
@keyframes roll {
    from {
        transform: translateY(-50%);
    }
    to {
        transform: translateY(0);
    }
}
        
JS
格式化
            
            window.addEventListener("DOMContentLoaded",() => {
    const clock = new BouncyBlockClock(".clock");
});

class BouncyBlockClock {
    constructor(qs) {
        this.el = document.querySelector(qs);
        this.time = { a: [], b: [] };
        this.rollClass = "clock__block--bounce";
        this.digitsTimeout = null;
        this.rollTimeout = null;
        this.mod = 0 * 60 * 1000;

        this.loop();
    }
    animateDigits() {
        const groups = this.el.querySelectorAll("[data-time-group]");

        Array.from(groups).forEach((group,i) => {
            const { a, b } = this.time;

            if (a[i] !== b[i]) group.classList.add(this.rollClass);
        });

        clearTimeout(this.rollTimeout);
        this.rollTimeout = setTimeout(this.removeAnimations.bind(this),900);
    }
    displayTime() {
        // screen reader time
        const timeDigits = [...this.time.b];
        const ap = timeDigits.pop();

        this.el.ariaLabel = `${timeDigits.join(":")} ${ap}`;

        // displayed time
        Object.keys(this.time).forEach(letter => {
            const letterEls = this.el.querySelectorAll(`[data-time="${letter}"]`);

            Array.from(letterEls).forEach((el,i) => {
                el.textContent = this.time[letter][i];
            });
        });
    }
    loop() {
        this.updateTime();
        this.displayTime();
        this.animateDigits();
        this.tick();
    }
    removeAnimations() {
        const groups = this.el.querySelectorAll("[data-time-group]");
    
        Array.from(groups).forEach(group => {
            group.classList.remove(this.rollClass);
        });
    }
    tick() {
        clearTimeout(this.digitsTimeout);
        this.digitsTimeout = setTimeout(this.loop.bind(this),1e3);    
    }
    updateTime() {
        const rawDate = new Date();
        const date = new Date(Math.ceil(rawDate.getTime() / 1e3) * 1e3 + this.mod);
        let h = date.getHours();
        const m = date.getMinutes();
        const s = date.getSeconds();
        const ap = h < 12 ? "AM" : "PM";

        if (h === 0) h = 12;
        if (h > 12) h -= 12;

        this.time.a = [...this.time.b];
        this.time.b = [
            (h < 10 ? `0${h}` : `${h}`),
            (m < 10 ? `0${m}` : `${m}`),
            (s < 10 ? `0${s}` : `${s}`),
            ap
        ];

        if (!this.time.a.length) this.time.a = [...this.time.b];
    }
}
        
预览
控制台