生成AI+スマホで作成「WordPress(ブログ)で使えるJavaScript」 ボールを浮かせるアニメーション

コードエディター 本文

<div class="wrapper">
    <div id="stage">
        <div id="shadow"></div>
        <div id="ball"></div>
    </div>
    <button id="floatButton">PUSH TO FLOAT</button>
</div>

カスタムJavaScript

    const ball = document.getElementById('ball');
    const shadow = document.getElementById('shadow');
    const stage = document.getElementById('stage');
    const btn = document.getElementById('floatButton');

    let yPos = 0;
    let vY = 0;
    let time = 0;
    let isPressing = false;

    const gravity = 0.25;
    const lift = 0.7;
    const friction = 0.96;

    function createBubble() {
        const bubble = document.createElement('div');
        bubble.className = 'bubble';
        const size = Math.random() * 6 + 3 + 'px'; // 少し小さめに
        bubble.style.width = size;
        bubble.style.height = size;
        // 320pxの幅に合わせて発生範囲を調整
        bubble.style.left = (stage.offsetWidth / 2 + (Math.random() - 0.5) * 30) + 'px';
        bubble.style.bottom = (yPos + 30) + 'px';
        stage.appendChild(bubble);

        let bY = yPos + 30;
        let bX = 0;
        function animateBubble() {
            bY += 2.5;
            bX += Math.sin(bY * 0.06);
            bubble.style.transform = `translate(${bX}px, ${-bY + yPos + 30}px)`;
            bubble.style.opacity = 1 - (bY / 400);

            if (bY < 400) {
                requestAnimationFrame(animateBubble);
            } else {
                bubble.remove();
            }
        }
        animateBubble();
    }

    const startFloating = () => isPressing = true;
    const stopFloating = () => isPressing = false;
btn.addEventListener('mousedown', startFloating);
    btn.addEventListener('mouseup', stopFloating);
    btn.addEventListener('mouseleave', stopFloating);
    btn.addEventListener('touchstart', (e) => { e.preventDefault(); startFloating(); });
    btn.addEventListener('touchend', stopFloating);

    function update() {
        time += 0.04;

        if (isPressing) {
            vY += lift;
            if (Math.random() > 0.85) createBubble();
        } else {
            vY -= gravity;
        }

        vY *= friction;
        yPos += vY;

        if (yPos < 0) { yPos = 0; vY *= -0.3; }
        // ボールのサイズ(50px)に合わせて天井を調整
        if (yPos > 295) { yPos = 295; vY *= -0.3; }

        // 左右のゆらぎ幅を320pxに合わせて少し抑えめ(10px)に設定
        let driftX = Math.sin(time * 0.8) * 10;
        let driftY = Math.sin(time * 1.2) * 4;
        let stretch = 1 + Math.abs(vY) * 0.015;
        let squash = 1 / stretch;

        ball.style.transform = `translate(calc(-50% + ${driftX}px), ${-yPos - driftY}px) scale(${squash}, ${stretch})`;

        let shadowOpacity = Math.max(0.1, 0.4 - (yPos / 400));
        let shadowScale = 1 + (yPos / 250);
        shadow.style.opacity = shadowOpacity;
        shadow.style.transform = `translateX(-50%) scale(${shadowScale})`;

        requestAnimationFrame(update);
    }

    update();

カスタムCSS

        .wrapper {
            display: flex;
            flex-direction: column;
            align-items: center; /* 中のボタンを中央寄せ */
            width: 320px; 
        }
        
        #stage {
            width: 320px;
            height: 400px;
            background: linear-gradient(to bottom, #4fc3f7, #0288d1);
            position: relative;
            overflow: hidden;
            border-radius: 15px;
            border: 6px solid #fff;
            box-shadow: 0 8px 20px rgba(0,0,0,0.15);
            margin-bottom: 20px;
        }

        #shadow {
            position: absolute; bottom: 20px; left: 50%;
            width: 50px; height: 12px; background: rgba(0, 0, 0, 0.3);
            border-radius: 50%; transform: translateX(-50%); filter: blur(3px);
        }

        #ball {
            width: 50px; height: 50px;
            background: radial-gradient(circle at 30% 30%, #ff8a65, #e64a19);
            border-radius: 50%; position: absolute; bottom: 30px; left: 50%; z-index: 10;
        }

        .bubble {
            position: absolute; background: rgba(255, 255, 255, 0.4);
            border: 1px solid rgba(255, 255, 255, 0.6); border-radius: 50%; pointer-events: none;
        }

        button {
            padding: 12px 24px; font-size: 16px; font-weight: bold; color: white;
            background: #00796b; border: none; border-radius: 50px; cursor: pointer;
            box-shadow: 0 4px #004d40; user-select: none;
            transition: all 0.1s;
        }
        button:active { transform: translateY(2px); box-shadow: 0 2px #004d40; }