AI先生のロボットキャラクター
第1章 - セクション9

ホバー時に削除文字を消す消しゴムアニメーション削除ボタン

消しゴムアイコンが左から右へゴシゴシと動いて文字を消すリアルなアニメーション付き削除ボタン。ホバー時に上から下へ文字が消えます。

リアルな消しゴムアニメーションの魅力

削除ボタンは多くのWebサイトやアプリケーションで使われる基本的なUIパーツですが、ただの「削除」ボタンでは味気ないと感じることはありませんか?

消しゴムアイコンが実際に左から右へゴシゴシと動いて文字を消すアニメーションを実装することで、削除アクションを直感的かつ楽しく表現できます。ユーザーに「本当に消える」という視覚的なフィードバックを提供し、より親しみやすいUIを実現します。

作成した消しゴムアニメーション削除ボタン

HTML
<!-- ボタンここから -->
    <button class="delete-btn" aria-label="削除">
        <!-- テキスト (順序は逆でもCSS positionで制御) -->
        <span class="text">削除</span>
        <!-- アイコン -->
        <div class="icon-wrapper">
            <svg viewbox="0 0 32 32" xmlns="http://www.w3.org/2000/svg">
                <!-- 消しゴム本体(赤い部分) -->
                <rect x="9" y="3" width="14" height="18" rx="1.5" fill="currentColor" transform="rotate(25 16 16)"></rect>
                <!-- 消す部分(白い部分) - ボーダー分を内側に -->
                <rect x="9.4" y="20.4" width="13.2" height="6.2" rx="0.5" fill="#ffffff" stroke="currentColor" stroke-width="0.8" transform="rotate(25 16 16)"></rect>
            </svg>
        </div>
    </button>
    <!-- ボタンここまで -->
CSS
:root {
            --primary: #ff4757;
            --bg: #f1f2f6;
            --surface: #ffffff;
            --text: #2f3542;
        }

        body {
            display: flex;
            justify-content: center;
            align-items: center;
            min-height: 100vh;
            background-color: var(--bg);
            margin: 0;
            font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
        }

        /* 
         * ----------------------------------------
         *  DELETE BUTTON COMPONENT
         * ----------------------------------------
         */
        .delete-btn {
            /* Layout & Size */
            position: relative;
            width: 160px;
            height: 56px;
            border: none;
            border-radius: 50px;
            
            /* Appearance */
            background: var(--surface);
            color: var(--primary);
            box-shadow: 0 10px 25px -10px rgba(255, 71, 87, 0.3);
            cursor: pointer;
            overflow: hidden;
            
            /* Typography */
            font-size: 18px;
            font-weight: 700;
            letter-spacing: 0.05em;
            
            /* Transition */
            transition: transform 0.2s ease, box-shadow 0.2s ease;
        }

        .delete-btn:hover {
            transform: translateY(-2px);
            box-shadow: 0 15px 30px -10px rgba(255, 71, 87, 0.5);
        }

        .delete-btn:active {
            transform: translateY(1px);
        }

        /* 
         * Text Element 
         * 中央より少し右に配置してアイコンとのバランスを取る
         */
        .delete-btn .text {
            position: absolute;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            padding-left: 24px; /* アイコン分のスペース */
            white-space: nowrap;
            display: inline-block;
            transition: opacity 0.3s ease;
            z-index: 1;
        }

        /* Hover Animation: Text Fade Out - 上から下へ消える */
        .delete-btn:hover .text {
            animation: textScrubFade 0.8s ease-out forwards;
        }

        @keyframes textScrubFade {
            0% { 
                opacity: 1; 
                filter: blur(0);
                clip-path: inset(0 0 0 0);
            }
            15% { 
                opacity: 1; 
                clip-path: inset(0 0 0 0);
            }
            30% { 
                /* 上部から消え始める */
                opacity: 0.9; 
                filter: blur(0.5px);
                clip-path: inset(15% 0 0 0);
                transform: translate(-50%, -50%) skewX(-5deg);
            }
            45% { 
                /* 中間まで消える */
                opacity: 0.7; 
                filter: blur(1.5px);
                clip-path: inset(40% 0 0 0);
                transform: translate(-50%, -50%) skewX(5deg);
            }
            60% { 
                /* さらに下まで消える */
                opacity: 0.4; 
                filter: blur(3px);
                clip-path: inset(65% 0 0 0);
                transform: translate(-50%, -50%) skewX(-5deg);
            }
            80% { 
                /* ほぼ消える */
                opacity: 0.1; 
                filter: blur(6px);
                clip-path: inset(90% 0 0 0);
            }
            100% { 
                /* 完全に消える */
                opacity: 0; 
                filter: blur(8px);
                clip-path: inset(100% 0 0 0);
            }
        }

        /* 
         * Eraser Icon (SVG)
         */
        .delete-btn .icon-wrapper {
            position: absolute;
            top: 50%;
            left: 32px; /* 初期位置:左側 */
            width: 28px;
            height: 28px;
            transform: translateY(-50%) rotate(0deg);
            pointer-events: none;
            z-index: 2;
        }

        .delete-btn svg {
            width: 100%;
            height: 100%;
            fill: var(--primary);
            filter: drop-shadow(0 2px 3px rgba(0,0,0,0.1));
        }

        /* 粉塵エフェクト(疑似要素で表現) */
        .delete-btn .icon-wrapper::after {
            content: "";
            position: absolute;
            bottom: -5px;
            left: 50%;
            transform: translateX(-50%);
            width: 0;
            height: 0;
            background: var(--primary);
            border-radius: 50%;
            opacity: 0;
            box-shadow: 
                -10px 0 0 -2px var(--primary),
                10px 0 0 -2px var(--primary),
                -5px -8px 0 -2px var(--primary);
        }

        /* Hover Animation: Eraser Scrubbing */
        .delete-btn:hover .icon-wrapper {
            animation: eraserScrub 1.2s cubic-bezier(0.4, 0, 0.6, 1) forwards;
        }
        
        /* 粉塵のアニメーション */
        .delete-btn:hover .icon-wrapper::after {
            animation: dustParticles 1.2s ease-out forwards;
        }

        /* 
         * リアルなゴシゴシ・アニメーション
         * 上から下へ横移動しながら徐々に下がる
         */
        @keyframes eraserScrub {
            0% {
                left: 32px;
                top: 50%;
                transform: translateY(-50%) rotate(0deg);
            }
            10% {
                /* 上部右へ移動 */
                left: 115px;
                top: 30%;
                transform: translateY(-50%) rotate(25deg) scale(1.1);
            }
            20% {
                /* 左へゴシッ (1回目 - 上部) */
                left: 55px;
                top: 30%;
                transform: translateY(-50%) rotate(-20deg) scale(1.15);
            }
            30% {
                /* 右へゴシッ (少し下へ) */
                left: 110px;
                top: 40%;
                transform: translateY(-50%) rotate(20deg) scale(1.15);
            }
            40% {
                /* 左へゴシッ (2回目 - 中間) */
                left: 50px;
                top: 45%;
                transform: translateY(-50%) rotate(-18deg) scale(1.15);
            }
            50% {
                /* 右へゴシッ (さらに下へ) */
                left: 105px;
                top: 55%;
                transform: translateY(-50%) rotate(18deg) scale(1.15);
            }
            60% {
                /* 左へゴシッ (3回目 - 下部) */
                left: 48px;
                top: 60%;
                transform: translateY(-50%) rotate(-15deg) scale(1.15);
            }
            70% {
                /* 右へゴシッ (最下部) */
                left: 100px;
                top: 68%;
                transform: translateY(-50%) rotate(15deg) scale(1.15);
            }
            80% {
                /* 仕上げの払い */
                left: 80px;
                top: 50%;
                transform: translateY(-50%) rotate(5deg) scale(1.2);
            }
            100% {
                /* 中央に着地 */
                left: 50%;
                top: 50%;
                transform: translate(-50%, -50%) rotate(0deg) scale(1.3);
            }
        }

        @keyframes dustParticles {
            0%, 15% { opacity: 0; width: 0; height: 0; }
            20% { opacity: 0.8; width: 4px; height: 4px; } /* 1回目 */
            25% { opacity: 0; width: 8px; height: 8px; transform: translateX(-50%) translateY(10px); }
            35% { opacity: 0.7; width: 5px; height: 5px; transform: translateX(-50%) translateY(0); } /* 2回目 */
            40% { opacity: 0; width: 9px; height: 9px; transform: translateX(-50%) translateY(12px); }
            50% { opacity: 0.6; width: 4px; height: 4px; transform: translateX(-50%) translateY(0); } /* 3回目 */
            55% { opacity: 0; width: 8px; height: 8px; transform: translateX(-50%) translateY(10px); }
            65% { opacity: 0.5; width: 5px; height: 5px; transform: translateX(-50%) translateY(0); } /* 4回目 */
            72% { opacity: 0; width: 10px; height: 10px; transform: translateX(-50%) translateY(15px); }
            100% { opacity: 0; }
        }

このボタンの特徴

  • リアルなゴシゴシ動作 消しゴムが左右に往復しながら上から下へ移動し、実際に消している感覚を再現
  • 粉塵エフェクト 消しゴムが動くたびに粉塵が舞うアニメーションで、よりリアルな表現
  • 文字が上から下へ消える clip-pathblurを組み合わせて、文字が徐々に消されていく様子を表現
  • SVGアイコン 拡大・縮小しても美しい消しゴムアイコンをSVGで実装
  • 視覚的フィードバック ホバー時にボタンが浮き上がり、削除アクションを強調

コードのポイント

消しゴムのゴシゴシ動作:
@keyframes eraserScrub {
    0% {
        left: 32px;
        top: 50%;
        transform: translateY(-50%) rotate(0deg);
    }
    20% {
        /* 左へゴシッ (1回目 - 上部) */
        left: 55px;
        top: 30%;
        transform: translateY(-50%) rotate(-20deg) scale(1.15);
    }
    40% {
        /* 左へゴシッ (2回目 - 中間) */
        left: 50px;
        top: 45%;
        transform: translateY(-50%) rotate(-18deg) scale(1.15);
    }
    /* ...繰り返し... */
}
  • 消しゴムが左右に往復しながら徐々に下へ移動
  • top値を変化させて上から下への動きを表現
  • rotateで左右の傾きをつけてゴシゴシ感を演出
  • scaleで拡大してアクションを強調
文字が消えるアニメーション:
@keyframes textScrubFade {
    0% { 
        opacity: 1; 
        clip-path: inset(0 0 0 0);
    }
    30% { 
        opacity: 0.9; 
        filter: blur(0.5px);
        clip-path: inset(15% 0 0 0); /* 上から15%消える */
        transform: translate(-50%, -50%) skewX(-5deg);
    }
    60% { 
        opacity: 0.4; 
        filter: blur(3px);
        clip-path: inset(65% 0 0 0); /* 上から65%消える */
        transform: translate(-50%, -50%) skewX(-5deg);
    }
    100% { 
        opacity: 0; 
        clip-path: inset(100% 0 0 0); /* 完全に消える */
    }
}
  • clip-path: inset()で上から徐々に文字を切り取る
  • filter: blur()でぼかしながら消える効果
  • skewX()で微妙な歪みを加えて、消されている感を演出
  • opacityと組み合わせて自然なフェードアウト
粉塵エフェクトの実装:
.delete-btn .icon-wrapper::after {
    content: "";
    position: absolute;
    background: var(--primary);
    border-radius: 50%;
    opacity: 0;
    box-shadow: 
        -10px 0 0 -2px var(--primary),
        10px 0 0 -2px var(--primary),
        -5px -8px 0 -2px var(--primary);
}

@keyframes dustParticles {
    20% { opacity: 0.8; width: 4px; height: 4px; }
    25% { opacity: 0; transform: translateY(10px); }
    /* ...各動作ごとに粉塵が出る... */
}
  • ::after疑似要素で粉塵を表現
  • box-shadowで複数の粉塵パーティクルを生成
  • 消しゴムの各動作に合わせて粉塵が出現・消滅

まとめ

消しゴムアニメーション削除ボタンは、削除という破壊的なアクションを視覚的に楽しく表現したパーツです。複数の@keyframesアニメーションを組み合わせることで、消しゴムの動き、文字の消え方、粉塵の舞い方を同期させ、リアルな体験を生み出しています。

消しゴムアニメーション削除ボタン作成のポイント
  • 複雑なアニメーションの同期 消しゴム、文字、粉塵の3つのアニメーションを同時再生してリアルさを実現
  • clip-pathで上から下へ消す inset()を使って文字が徐々に消される様子を表現
  • 回転とスケールでゴシゴシ感 左右の傾きと拡大縮小で実際に消している感覚を再現
  • 疑似要素で粉塵表現 ::afterbox-shadowを使って軽量に粉塵エフェクトを実装
  • SVGでスケーラブル アイコンをSVGで実装することで、どんなサイズでも美しく表示

学習チェック

このレッスンを理解できたら「完了」をクリックしてください。
後で見直したい場合は「未完了に戻す」で進捗をリセットできます。

レッスン完了!🎉

お疲れさまでした!