【JavaScript】ドラッグで削るスクラッチカードのクイズの作り方

簡単なクイズやテストを、ユニークなスクラッチカード形式で実装する方法。
CSSとJavaScriptを用いて、「ドラッグで削れるシルバーの領域」を再現します。コピペで簡単に使えます!

スクラッチカードのクイズ実装サンプル

漢字クイズ

「鰈」は何と読む?
答え:かれい

シルバーの領域をドラッグすると、スクラッチが削れるよ!

地理クイズ

富士山は静岡県と山梨県にまたがる

スクラッチ箇所は、2箇所でも3箇所でも設定できます。

ことわざクイズ

の子は
意味:子は親に似る

FontAwesomeのアイコンフォントや、画像を仕込むこともできます。

ソースコード

JavaScriptコード

<script>
(function() {
    function createScratchEffect(scratchElement) {
        const overlay = document.createElement('div');
        overlay.className = 'scratch-overlay';
        scratchElement.appendChild(overlay);

        const canvas = document.createElement('canvas');
        const ctx = canvas.getContext('2d');
        overlay.appendChild(canvas);

        let isDrawing = false;
        let lastX = 0;
        let lastY = 0;

        function initCanvasSize() {
            const rect = scratchElement.getBoundingClientRect();
            canvas.width = rect.width;
            canvas.height = rect.height;

            const gradient = ctx.createLinearGradient(0, 0, canvas.width, canvas.height);
            gradient.addColorStop(0, '#eeeeee');
            gradient.addColorStop(1, '#bbbbbb');
            ctx.fillStyle = gradient;
            ctx.fillRect(0, 0, canvas.width, canvas.height);
        }

        initCanvasSize();

        function startDrawing(e) {
            isDrawing = true;
            [lastX, lastY] = getPosition(e);
            draw(e);
        }

        function stopDrawing() {
            isDrawing = false;
        }

        function draw(e) {
            if (!isDrawing) return;
            e.preventDefault();

            const [x, y] = getPosition(e);
            ctx.globalCompositeOperation = 'destination-out';
            ctx.beginPath();
            ctx.moveTo(lastX, lastY);
            ctx.lineTo(x, y);
            ctx.lineWidth = 15;
            ctx.lineCap = 'round';
            ctx.stroke();

            lastX = x;
            lastY = y;
        }

        function getPosition(e) {
            const rect = canvas.getBoundingClientRect();
            const clientX = e.clientX || (e.touches && e.touches[0].clientX);
            const clientY = e.clientY || (e.touches && e.touches[0].clientY);
            return [
                clientX - rect.left,
                clientY - rect.top
            ];
        }

        canvas.addEventListener('mousedown', startDrawing);
        document.addEventListener('mousemove', draw);
        document.addEventListener('mouseup', stopDrawing);

        canvas.addEventListener('touchstart', function(e) {
            e.preventDefault();
            startDrawing(e);
        }, { passive: false });

        document.addEventListener('touchmove', function(e) {
            if (isDrawing) {
                e.preventDefault();
                draw(e);
            }
        }, { passive: false });

        document.addEventListener('touchend', stopDrawing);
        document.addEventListener('touchcancel', stopDrawing);
    }

    function initializeScratchEffects() {
        document.querySelectorAll('.scratch').forEach(createScratchEffect);
    }

    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', initializeScratchEffects);
    } else {
        initializeScratchEffects();
    }
})();
</script>

JavaScriptコードは</body>の前に入れます。

49行目のctx.lineWidth = 15;はスクラッチの描画サイズの直径です。要は、シルバーを削るときの指の太さみたいなものです。
30に設定すれば、ひとかきでザックリ削れるようになります。5に設定すれば、チマチマした削り方になります。お好みで調節してね!

HTMLコード

<div class="mondai">「鰈」は何と読む?<br />
答え:<span class="scratch"><span class="scratch-content">かれい</span></span></div>

スクラッチの部分は<span class="scratch"><span class="scratch-content">文字列</span></span>を追加することで増やせます。

クイズのサンプルでは<div class="mondai"></div>でカードを作り、その中でスクラッチを表示しましたが、スクラッチ部分を単独で使うこともできます。

CSSコード

.mondai {
  max-width: 350px; /* カードの最大幅 */
  font-size: 28px; /* テキストのサイズ */
  background: #ff0; /* カードの背景色 */
  color: #000; /* テキストの色 */
  border: solid 3px #000; /* カードの枠線 */
  padding: 15px; /* カード内の余白 */
  border-radius: 15px; /* カードの角の丸み */
  margin: 20px auto; /* カードの外側の余白 */
  box-shadow: 1px 1px 1px rgba(0, 0, 0, 0.3); /* カードの影 */
  box-sizing: border-box;
}

.mondai, .scratch {
  user-select: none;
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
}

.scratch {
    display: inline-block;
    background: #fff;/* 削ったスクラッチの背景色 */
    padding: 5px 20px;
    margin: 0 3px;
    border-radius: 30px;
    position: relative;
}

.scratch-content {
    position: relative;
    z-index: 1;
    color: #008;/* 削ったスクラッチの文字色 */
    font-weight: bold;
}

.scratch-overlay {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    border-radius: inherit;
    overflow: hidden;
    z-index: 2;
}

canvas {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    cursor: pointer;
    background: transparent;
}

9行目のmargin: 20px auto;margin: 20px 0;に変更するとカードの中央揃えが解除されます。

-JavaScript