← back to Model Arena

data/artifacts/176d578251e3/kimi.html

312 lines

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Radial Spring Menu</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        body {
            width: 100vw;
            height: 100vh;
            background: radial-gradient(circle at center, #1e293b 0%, #0f172a 100%);
            overflow: hidden;
            font-family: system-ui, -apple-system, sans-serif;
            display: flex;
            justify-content: center;
            align-items: center;
        }

        .menu-container {
            position: relative;
            width: 400px;
            height: 400px;
            display: flex;
            justify-content: center;
            align-items: center;
        }

        .center-btn {
            position: relative;
            width: 72px;
            height: 72px;
            border-radius: 50%;
            background: linear-gradient(135deg, #ffffff 0%, #e2e8f0 100%);
            border: none;
            cursor: pointer;
            z-index: 10;
            display: flex;
            justify-content: center;
            align-items: center;
            box-shadow: 0 10px 40px -10px rgba(0,0,0,0.5),
                        0 0 0 1px rgba(255,255,255,0.1) inset;
            transition: transform 0.2s cubic-bezier(0.175, 0.885, 0.32, 1.275),
                        box-shadow 0.2s ease;
        }

        .center-btn:active {
            transform: scale(0.92);
            box-shadow: 0 5px 20px -5px rgba(0,0,0,0.4),
                        0 0 0 1px rgba(255,255,255,0.1) inset;
        }

        .center-btn svg {
            width: 32px;
            height: 32px;
            fill: none;
            stroke: #0f172a;
            stroke-width: 2.5;
            stroke-linecap: round;
            stroke-linejoin: round;
            transition: transform 0.4s cubic-bezier(0.68, -0.55, 0.265, 1.55);
        }

        .center-btn.open svg {
            transform: rotate(135deg);
        }

        .satellite {
            position: absolute;
            width: 56px;
            height: 56px;
            border-radius: 50%;
            background: rgba(255, 255, 255, 0.08);
            backdrop-filter: blur(12px);
            border: 1px solid rgba(255, 255, 255, 0.15);
            cursor: pointer;
            display: flex;
            justify-content: center;
            align-items: center;
            box-shadow: 0 8px 32px rgba(0,0,0,0.3);
            transform: translate(var(--x, 0px), var(--y, 0px));
            will-change: transform;
            z-index: 5;
        }

        .satellite-inner {
            width: 100%;
            height: 100%;
            display: flex;
            justify-content: center;
            align-items: center;
            border-radius: 50%;
            transition: transform 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275),
                        background 0.3s ease;
        }

        .satellite:hover .satellite-inner {
            transform: scale(1.15);
            background: rgba(255,255,255,0.15);
        }

        .satellite:active .satellite-inner {
            transform: scale(0.95);
        }

        .satellite svg {
            width: 24px;
            height: 24px;
            fill: none;
            stroke: currentColor;
            stroke-width: 2;
            stroke-linecap: round;
            stroke-linejoin: round;
            pointer-events: none;
        }

        .satellite:nth-child(1) { color: #f472b6; }
        .satellite:nth-child(2) { color: #60a5fa; }
        .satellite:nth-child(3) { color: #34d399; }
        .satellite:nth-child(4) { color: #fbbf24; }
        .satellite:nth-child(5) { color: #a78bfa; }
        .satellite:nth-child(6) { color: #f87171; }

        .glow {
            position: absolute;
            width: 100%;
            height: 100%;
            border-radius: 50%;
            opacity: 0;
            transition: opacity 0.3s ease;
            pointer-events: none;
        }

        .satellite:hover .glow {
            opacity: 0.4;
        }

        .satellite:nth-child(1) .glow { background: radial-gradient(circle, rgba(244,114,182,0.6) 0%, transparent 70%); }
        .satellite:nth-child(2) .glow { background: radial-gradient(circle, rgba(96,165,250,0.6) 0%, transparent 70%); }
        .satellite:nth-child(3) .glow { background: radial-gradient(circle, rgba(52,211,153,0.6) 0%, transparent 70%); }
        .satellite:nth-child(4) .glow { background: radial-gradient(circle, rgba(251,191,36,0.6) 0%, transparent 70%); }
        .satellite:nth-child(5) .glow { background: radial-gradient(circle, rgba(167,139,250,0.6) 0%, transparent 70%); }
        .satellite:nth-child(6) .glow { background: radial-gradient(circle, rgba(248,113,113,0.6) 0%, transparent 70%); }

    </style>
</head>
<body>

<div class="menu-container">
    <button class="center-btn" id="centerBtn" aria-label="Toggle menu">
        <svg viewBox="0 0 24 24">
            <line x1="12" y1="5" x2="12" y2="19"></line>
            <line x1="5" y1="12" x2="19" y2="12"></line>
        </svg>
    </button>

    <div class="satellites" id="satellites">
        <button class="satellite" aria-label="Home">
            <div class="glow"></div>
            <div class="satellite-inner">
                <svg viewBox="0 0 24 24"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"></path><polyline points="9 22 9 12 15 12 15 22"></polyline></svg>
            </div>
        </button>
        <button class="satellite" aria-label="Search">
            <div class="glow"></div>
            <div class="satellite-inner">
                <svg viewBox="0 0 24 24"><circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line></svg>
            </div>
        </button>
        <button class="satellite" aria-label="Profile">
            <div class="glow"></div>
            <div class="satellite-inner">
                <svg viewBox="0 0 24 24"><path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"></path><circle cx="12" cy="7" r="4"></circle></svg>
            </div>
        </button>
        <button class="satellite" aria-label="Favorites">
            <div class="glow"></div>
            <div class="satellite-inner">
                <svg viewBox="0 0 24 24"><path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z"></path></svg>
            </div>
        </button>
        <button class="satellite" aria-label="Notifications">
            <div class="glow"></div>
            <div class="satellite-inner">
                <svg viewBox="0 0 24 24"><path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9"></path><path d="M13.73 21a2 2 0 0 1-3.46 0"></path></svg>
            </div>
        </button>
        <button class="satellite" aria-label="Settings">
            <div class="glow"></div>
            <div class="satellite-inner">
                <svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="3"></circle><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"></path></svg>
            </div>
        </button>
    </div>
</div>

<script>
    class Spring {
        constructor() {
            this.x = 0;
            this.y = 0;
            this.vx = 0;
            this.vy = 0;
            this.targetX = 0;
            this.targetY = 0;
            this.stiffness = 0.12;
            this.damping = 0.75;
            this.mass = 1;
        }

        update() {
            const forceX = (this.targetX - this.x) * this.stiffness;
            const forceY = (this.targetY - this.y) * this.stiffness;
            
            const ax = forceX / this.mass;
            const ay = forceY / this.mass;
            
            this.vx += ax;
            this.vy += ay;
            
            this.vx *= this.damping;
            this.vy *= this.damping;
            
            this.x += this.vx;
            this.y += this.vy;
        }

        setTarget(x, y) {
            this.targetX = x;
            this.targetY = y;
        }
    }

    const centerBtn = document.getElementById('centerBtn');
    const satellites = document.querySelectorAll('.satellite');
    const springs = [];
    
    const count = 6;
    const radius = 140;
    const startAngle = -Math.PI * 5/6;
    const endAngle = -Math.PI / 6;
    const step = (endAngle - startAngle) / (count - 1);
    
    const targets = [];
    for (let i = 0; i < count; i++) {
        const angle = startAngle + step * i;
        targets.push({
            x: Math.cos(angle) * radius,
            y: Math.sin(angle) * radius
        });
        springs.push(new Spring());
    }

    let isOpen = false;
    let animationId = null;

    function animate() {
        let active = false;
        
        satellites.forEach((sat, i) => {
            const spring = springs[i];
            spring.update();
            
            const dx = Math.abs(spring.targetX - spring.x);
            const dy = Math.abs(spring.targetY - spring.y);
            const velocity = Math.abs(spring.vx) + Math.abs(spring.vy);
            
            if (dx > 0.01 || dy > 0.01 || velocity > 0.01) {
                active = true;
            }
            
            sat.style.setProperty('--x', spring.x + 'px');
            sat.style.setProperty('--y', spring.y + 'px');
        });
        
        if (active) {
            animationId = requestAnimationFrame(animate);
        } else {
            animationId = null;
        }
    }

    function toggleMenu() {
        isOpen = !isOpen;
        centerBtn.classList.toggle('open', isOpen);
        
        springs.forEach((spring, i) => {
            if (isOpen) {
                spring.setTarget(targets[i].x, targets[i].y);
            } else {
                spring.setTarget(0, 0);
            }
        });
        
        if (!animationId) {
            animate();
        }
    }

    centerBtn.addEventListener('click', toggleMenu);

    // Initialize
    animate();
</script>

</body>
</html>