Demo

Analog Clock

Experience a smooth, animated analog clock built with LemonadeJS that updates in real-time. Watch as the hour, minute, and second hands move fluidly across the clock face with mathematical precision.


<html>
<script src="https://cdn.jsdelivr.net/npm/lemonadejs/dist/lemonade.min.js"></script>
<div id='root'></div>
<script>
function Clock() {
    const self = this;

    const updateClock = () => {
        const now = new Date();
        self.second = now.getSeconds() * 6;
        self.minute = now.getMinutes() * 6 + now.getSeconds() * 0.1;
        self.hour = now.getHours() * 30 + now.getMinutes() * 0.5;
    }

    let marks = [];
    for (let i = 0; i < 12; i++) {
        marks.push({ index: i*30 });
    }
    self.marks = marks;

    updateClock();
    setInterval(updateClock, 1000);

    return `<div class="clock">
        <div class="face">
            <div class="marks" :loop="self.marks">
                <div class="mark" style="transform: rotate({{self.index}}deg)"></div>
            </div>
            <div class="hand hour" style="transform: rotate({{self.hour}}deg)"></div>
            <div class="hand minute" style="transform: rotate({{self.minute}}deg)"></div>
            <div class="hand second" style="transform: rotate({{self.second}}deg)"></div>
        </div>
    </div>`;
}

lemonade.render(Clock, document.getElementById('root'));
</script>

<style>
.clock {
    width: 300px;
    height: 300px;
    margin: 40px auto;
}

.face {
    position: relative;
    width: 100%;
    height: 100%;
    border-radius: 50%;
    border: 4px solid #0891b2;
    background: #fff;
    box-shadow: 0 4px 20px rgba(0,0,0,0.1);
}

.face::after {
    content: '';
    position: absolute;
    width: 12px;
    height: 12px;
    background: #0891b2;
    border-radius: 50%;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    z-index: 10;
}

.marks {
    position: relative;
    width: 100%;
    height: 100%;
}

.mark {
    position: absolute;
    width: 100%;
    height: 100%;
}

.mark::after {
    content: '';
    position: absolute;
    width: 3px;
    height: 15px;
    background: #6b7280;
    left: 50%;
    transform: translateX(-50%);
    border-radius: 2px;
}

.hand {
    position: absolute;
    bottom: 50%;
    left: 50%;
    transform-origin: bottom;
    border-radius: 10px;
}

.hour {
    width: 6px;
    height: 25%;
    background: #111827;
    margin-left: -3px;
}

.minute {
    width: 4px;
    height: 35%;
    background: #374151;
    margin-left: -2px;
}

.second {
    width: 2px;
    height: 40%;
    background: #0891b2;
    margin-left: -1px;
}
</style>
</html>
import lemonade from 'lemonadejs';

export default function Clock() {
    const self = this;

    const updateClock = () => {
        const now = new Date();
        self.second = now.getSeconds() * 6;
        self.minute = now.getMinutes() * 6 + now.getSeconds() * 0.1;
        self.hour = now.getHours() * 30 + now.getMinutes() * 0.5;
    }

    let marks = [];
    for (let i = 0; i < 12; i++) {
        marks.push({ index: i*30 });
    }
    self.marks = marks;

    updateClock();
    setInterval(updateClock, 1000);

    return `<div class="clock">
        <div class="face">
            <div class="marks" :loop="self.marks">
                <div class="mark" style="transform: rotate({{self.index}}deg)"></div>
            </div>
            <div class="hand hour" style="transform: rotate({{self.hour}}deg)"></div>
            <div class="hand minute" style="transform: rotate({{self.minute}}deg)"></div>
            <div class="hand second" style="transform: rotate({{self.second}}deg)"></div>
        </div>
    </div>`;
}
import React, { useState, useEffect } from 'react';
import './Clock.css';

export default function Clock() {
    const [time, setTime] = useState({
        second: 0,
        minute: 0,
        hour: 0
    });

    const marks = Array.from({ length: 12 }, (_, i) => i * 30);

    useEffect(() => {
        const updateClock = () => {
            const now = new Date();
            setTime({
                second: now.getSeconds() * 6,
                minute: now.getMinutes() * 6 + now.getSeconds() * 0.1,
                hour: now.getHours() * 30 + now.getMinutes() * 0.5
            });
        };

        updateClock();
        const interval = setInterval(updateClock, 1000);

        return () => clearInterval(interval);
    }, []);

    return (
        <div className="clock">
            <div className="face">
                <div className="marks">
                    {marks.map((rotation, index) => (
                        <div key={index} className="mark" style={{ transform: `rotate(${rotation}deg)` }}></div>
                    ))}
                </div>
                <div className="hand hour" style={{ transform: `rotate(${time.hour}deg)` }}></div>
                <div className="hand minute" style={{ transform: `rotate(${time.minute}deg)` }}></div>
                <div className="hand second" style={{ transform: `rotate(${time.second}deg)` }}></div>
            </div>
        </div>
    );
}
<template>
    <div class="clock">
        <div class="face">
            <div class="marks">
                <div v-for="(rotation, index) in marks" :key="index"
                     class="mark" :style="{ transform: `rotate(${rotation}deg)` }"></div>
            </div>
            <div class="hand hour" :style="{ transform: `rotate(${time.hour}deg)` }"></div>
            <div class="hand minute" :style="{ transform: `rotate(${time.minute}deg)` }"></div>
            <div class="hand second" :style="{ transform: `rotate(${time.second}deg)` }"></div>
        </div>
    </div>
</template>

<script>
export default {
    name: 'Clock',
    data() {
        return {
            time: {
                second: 0,
                minute: 0,
                hour: 0
            },
            marks: Array.from({ length: 12 }, (_, i) => i * 30),
            interval: null
        }
    },
    mounted() {
        this.updateClock();
        this.interval = setInterval(this.updateClock, 1000);
    },
    beforeUnmount() {
        if (this.interval) {
            clearInterval(this.interval);
        }
    },
    methods: {
        updateClock() {
            const now = new Date();
            this.time = {
                second: now.getSeconds() * 6,
                minute: now.getMinutes() * 6 + now.getSeconds() * 0.1,
                hour: now.getHours() * 30 + now.getMinutes() * 0.5
            };
        }
    }
}
</script>

<style scoped>
.clock {
    width: 300px;
    height: 300px;
    margin: 40px auto;
}

.face {
    position: relative;
    width: 100%;
    height: 100%;
    border-radius: 50%;
    border: 4px solid #0891b2;
    background: #fff;
    box-shadow: 0 4px 20px rgba(0,0,0,0.1);
}

.face::after {
    content: '';
    position: absolute;
    width: 12px;
    height: 12px;
    background: #0891b2;
    border-radius: 50%;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    z-index: 10;
}

.marks {
    position: relative;
    width: 100%;
    height: 100%;
}

.mark {
    position: absolute;
    width: 100%;
    height: 100%;
}

.mark::after {
    content: '';
    position: absolute;
    width: 3px;
    height: 15px;
    background: #6b7280;
    left: 50%;
    transform: translateX(-50%);
    border-radius: 2px;
}

.hand {
    position: absolute;
    bottom: 50%;
    left: 50%;
    transform-origin: bottom;
    border-radius: 10px;
}

.hour {
    width: 6px;
    height: 25%;
    background: #111827;
    margin-left: -3px;
}

.minute {
    width: 4px;
    height: 35%;
    background: #374151;
    margin-left: -2px;
}

.second {
    width: 2px;
    height: 40%;
    background: #0891b2;
    margin-left: -1px;
}
</style>