メインコンテンツへスキップ
見出し画像

教室アプリ15:フラッシュカード


    画像
    • 言葉を入れる:白い箱に、練習したい言葉をスペースで区切って入れます。

    • 速さを決める:スライダーを動かして、切り替わる速さを調節します。

    • ボタンを選ぶ:順番通りか、バラバラに出すかを選びます。

    • 「開始」を押す:3秒の合図(音と数字)のあと、大きく縦書きで表示されます。

    <!DOCTYPE html>
    <html lang="ja">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>教室アプリ:フラッシュカード</title>
        <style>
            :root {
                --bg-color: #e8f5e9 ;
                --main-color: #1b5e20 ;
                --accent-color: #d84315 ;
                --count-color: #ffc107 ;
            }
            body {
                font-family: "Hiragino Kaku Gothic ProN", "Hiragino Sans", "BIZ UDPGothic", "Meiryo", sans-serif;
                background-color: var(--bg-color);
                margin: 0;
                display: flex;
                justify-content: center;
                align-items: center;
                min-height: 100vh;
                overflow: hidden;
            }
            .app-container {
                width: 100vw;
                height: 100vh;
                background: white;
                display: flex;
                flex-direction: column;
                justify-content: center;
                align-items: center;
                text-align: center;
            }
    
            /* 入力画面 */
            #input -screen { width: 90%; max-width: 800px; }
            textarea {
                width: 100%;
                height: 120px;
                font-size: 1.5rem;
                padding: 15px;
                border: 4px solid #ccc ;
                border-radius: 15px;
                margin-bottom: 20px;
            }
    
            .settings-grid {
                display: flex;
                flex-direction: column;
                gap: 30px;
                margin-bottom: 40px;
            }
    
            .mode-settings {
                display: flex;
                justify-content: center;
                gap: 15px;
            }
            .btn-option {
                padding: 15px 30px;
                border: 3px solid #ddd ;
                border-radius: 15px;
                background: #f8f9fa ;
                cursor: pointer;
                font-size: 1.2rem;
                font-weight: bold;
            }
            .btn-option.active {
                background: var(--main-color);
                color: white;
                border-color: var(--main-color);
            }
    
            .speed-slider-container {
                width: 80%;
                margin: 0 auto;
            }
            .slider-labels {
                display: flex;
                justify-content: space-between;
                margin-bottom: 10px;
                font-weight: bold;
                color: var(--main-color);
                font-size: 1.2rem;
            }
            input[type="range"] {
                width: 100%;
                cursor: pointer;
                height: 25px;
            }
    
            /* カウントダウン */
            #countdown -overlay {
                display: none;
                font-size: 25rem;
                color: var(--count-color);
                font-weight: 900;
                z-index: 10;
            }
    
            /* フラッシュ表示 */
            #flash -screen { 
                display: none; 
                width: 100%; 
                height: 100%; 
                background: #fff ;
                justify-content: center;
                align-items: center;
            }
            #card -display {
                width: 100%;
                height: 100%;
                display: flex;
                justify-content: center;
                align-items: center;
                writing-mode: vertical-rl;
                -webkit-writing-mode: vertical-rl;
                font-weight: 900;
                line-height: 1.1;
                white-space: nowrap;
            }
    
            .big-btn {
                font-size: 2.5rem;
                padding: 20px 80px;
                background: var(--accent-color);
                color: white;
                border: none;
                border-radius: 100px;
                cursor: pointer;
                box-shadow: 0 8px 0 #870000 ;
            }
            .big-btn:active { transform: translateY(5px); box-shadow: none; }
    
            #back -btn {
                display: none;
                position: fixed;
                bottom: 40px;
                right: 40px;
                z-index: 100;
                font-size: 1.8rem;
                padding: 15px 40px;
            }
    
            ruby rt { font-size: 0.3em; writing-mode: vertical-rl; }
        </style>
    </head>
    <body>
    
    <div class="app-container">
        <div id="input-screen">
            <h1>フラッシュカード</h1>
            <textarea id="word-input" placeholder="たけやぶ やけた なのはな"></textarea>
            
            <div class="settings-grid">
                <div class="mode-settings">
                    <button class="btn-option active" id="mode-normal" onclick="setMode('normal')">じゅんばん</button>
                    <button class="btn-option" id="mode-random" onclick="setMode('random')">ばらばら</button>
                </div>
    
                <div class="speed-slider-container">
                    <div class="slider-labels">
                        <span>ゆっくり</span>
                        <span id="speed-display">1.0秒</span>
                        <span>はやい</span>
                    </div>
                    <input type="range" id="speed-range" min="500" max="3000" step="100" value="1000">
                    <p style="margin-top:5px; color:#666; font-size:0.9rem;">右に動かすとはやくなります(最速0.5秒)</p>
                </div>
            </div>
            <button class="big-btn" onclick="startCountdown()">開始!</button>
        </div>
    
        <div id="countdown-overlay"></div>
    
        <div id="flash-screen">
            <div id="card-display"></div>
            <button id="back-btn" class="big-btn" onclick="resetApp()">もどる</button>
        </div>
    </div>
    
    <script>
        let words = [];
        let isRandom = false;
        let timer;
        let audioCtx = null;
    
        const slider = document.getElementById('speed-range');
        const speedDisplay = document.getElementById('speed-display');
    
        // スライダーの反転計算(右に行くほどはやい=待ち時間が短い)
        function getActualMs() {
            const val = parseInt(slider.value);
            const min = parseInt(slider.min);
            const max = parseInt(slider.max);
            return (max + min) - val;
        }
    
        slider.addEventListener('input', () => {
            const ms = getActualMs();
            speedDisplay.innerText = (ms / 1000).toFixed(1) + "秒";
        });
    
        window.onload = () => {
            const saved = localStorage.getItem('v_flash_words_05s');
            if (saved) document.getElementById('word-input').value = saved;
            speedDisplay.innerText = (getActualMs() / 1000).toFixed(1) + "秒";
        };
    
        function playBeep(freq, duration) {
            if (!audioCtx) audioCtx = new (window.AudioContext || window.webkitAudioContext)();
            const osc = audioCtx.createOscillator();
            const gain = audioCtx.createGain();
            osc.type = "sine";
            osc.frequency.value = freq;
            gain.gain.setValueAtTime(0.1, audioCtx.currentTime);
            gain.gain.exponentialRampToValueAtTime(0.001, audioCtx.currentTime + duration);
            osc.connect(gain);
            gain.connect(audioCtx.destination);
            osc.start();
            osc.stop(audioCtx.currentTime + duration);
        }
    
        function setMode(mode) {
            isRandom = (mode === 'random');
            document.getElementById('mode-normal').classList.toggle('active', !isRandom);
            document.getElementById('mode-random').classList.toggle('active', isRandom);
        }
    
        function startCountdown() {
            const text = document.getElementById('word-input').value.trim();
            let rawWords = text.split(/\s+/).filter(w => w !== "");
            if (rawWords.length === 0) return alert("言葉を入力してね");
    
            localStorage.setItem('v_flash_words_05s', text);
            words = isRandom ? [...rawWords].sort(() => Math.random() - 0.5) : [...rawWords];
    
            document.getElementById('input-screen').style.display = 'none';
            const overlay = document.getElementById('countdown-overlay');
            overlay.style.display = 'block';
    
            let count = 3;
            overlay.innerText = count;
            playBeep(880, 0.1);
    
            const countTimer = setInterval(() => {
                count--;
                if (count > 0) {
                    overlay.innerText = count;
                    playBeep(880, 0.1);
                } else {
                    clearInterval(countTimer);
                    playBeep(1760, 0.3);
                    overlay.style.display = 'none';
                    startFlash();
                }
            }, 1000);
        }
    
        function startFlash() {
            document.getElementById('flash-screen').style.display = 'flex';
            document.getElementById('back-btn').style.display = 'none';
            runFlash(0, getActualMs());
        }
    
        function runFlash(index, interval) {
            const display = document.getElementById('card-display');
            if (index < words.length) {
                const word = words[index];
                display.innerText = word;
                
                const length = word.length;
                if (length > 2) {
                    const newSize = 85 / length;
                    display.style.fontSize = newSize + "vh";
                } else {
                    display.style.fontSize = "40vh";
                }
                
                timer = setTimeout(() => runFlash(index + 1, interval), interval);
            } else {
                finish();
            }
        }
    
        function finish() {
            document.getElementById('back-btn').style.display = 'block';
        }
    
        function resetApp() {
            document.getElementById('input-screen').style.display = 'block';
            document.getElementById('flash-screen').style.display = 'none';
            document.getElementById('card-display').innerText = "";
        }
    </script>
    
    </body>
    </html>
     
     
    小学校教師/教職30年/小学校の教室で使うアプリをAIで作っています

    あなたへのおすすめ