<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Webapps &#8211; デジタル未来 (Dejitaru Mirai)</title>
	<atom:link href="https://dejitarumirai.com/webapps/feed" rel="self" type="application/rss+xml" />
	<link>https://dejitarumirai.com</link>
	<description></description>
	<lastBuildDate>Sat, 05 Jul 2025 14:59:07 +0000</lastBuildDate>
	<language>ja</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.8.1</generator>
	<item>
		<title>おみくじ 順番</title>
		<link>https://dejitarumirai.com/archives/4230</link>
					<comments>https://dejitarumirai.com/archives/4230#respond</comments>
		
		<dc:creator><![CDATA[deji]]></dc:creator>
		<pubDate>Sat, 05 Jul 2025 14:59:07 +0000</pubDate>
				<category><![CDATA[Webapps]]></category>
		<guid isPermaLink="false">https://dejitarumirai.com/?p=4230</guid>

					<description><![CDATA[運命のホイール 運命のホイール [&#8230;]]]></description>
										<content:encoded><![CDATA[<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>運命のホイール</title>
    <script src="https://cdn.tailwindcss.com"></script>
    <link rel="preconnect" href="https://fonts.googleapis.com">
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
    <link href="https://fonts.googleapis.com/css2?family=Noto+Sans+JP:wght@400;500;700;900&display=swap" rel="stylesheet">
    <style>
        body {
            font-family: 'Noto Sans JP', sans-serif;
            background-color: #111827; /* gray-900 */
            color: #d1d5db; /* gray-300 */
        }
        #wheel-container {
            position: relative;
            width: 100%;
            max-width: 500px;
            height: auto;
            aspect-ratio: 1 / 1;
            margin: 0 auto;
        }
        #wheel-canvas {
            width: 100%;
            height: 100%;
            transition: transform 8s cubic-bezier(0.25, 0.1, 0.25, 1);
            border-radius: 50%;
        }
        #pointer {
            position: absolute;
            top: 50%;
            right: -10px;
            transform: translateY(-50%);
            width: 0;
            height: 0;
            border-top: 20px solid transparent;
            border-bottom: 20px solid transparent;
            border-left: 30px solid #facc15; /* yellow-400 */
            z-index: 10;
        }
        #wheel-overlay {
            position: absolute;
            top: 0;
            left: 0;
            right: 0;
            bottom: 0;
            display: flex;
            align-items: center;
            justify-content: center;
            text-align: center;
            font-size: 2rem;
            font-weight: 900;
            color: white;
            text-shadow: 0 0 10px rgba(0,0,0,0.5);
            pointer-events: none; /* Allows clicking through to the canvas */
            opacity: 1;
            transition: opacity 0.3s;
        }
        #wheel-overlay.hidden {
            opacity: 0;
        }
        /* Custom scrollbar for textarea */
        #entries-input::-webkit-scrollbar {
            width: 8px;
        }
        #entries-input::-webkit-scrollbar-track {
            background: #1f2937; /* gray-800 */
        }
        #entries-input::-webkit-scrollbar-thumb {
            background: #4b5563; /* gray-600 */
            border-radius: 10px;
        }
        #entries-input::-webkit-scrollbar-thumb:hover {
            background: #6b7280; /* gray-500 */
        }
    </style>
</head>
<body class="p-4 md:p-8">

    <div class="container mx-auto max-w-6xl">
        <!-- Header -->
        <header class="text-center mb-8">
            <h1 class="text-4xl md:text-5xl font-black text-white">運命のホイール</h1>
            <p class="text-gray-400 mt-2 text-lg">すべての決定を楽しく！</p>
        </header>

        <main class="grid grid-cols-1 lg:grid-cols-2 gap-8">
            <!-- Left Column: Wheel -->
            <section class="flex flex-col items-center justify-center space-y-6">
                <div id="wheel-container">
                    <div id="pointer"></div>
                    <canvas id="wheel-canvas" width="500" height="500"></canvas>
                    <div id="wheel-overlay">
                        <p>クリックして回す</p>
                    </div>
                </div>
                <button id="spin-btn" class="bg-gradient-to-br from-yellow-400 to-orange-500 text-gray-900 font-bold py-4 px-16 rounded-full text-xl transition-all transform hover:scale-105 shadow-lg hover:shadow-yellow-400/50 disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:scale-100">
                    回す
                </button>
                <div id="result-display" class="mt-4 text-center h-12 text-2xl font-bold">
                    <!-- Result will be shown here -->
                </div>
            </section>

            <!-- Right Column: Customization -->
            <section class="bg-gray-800 p-6 rounded-2xl shadow-lg border border-gray-700 flex flex-col">
                <h2 class="text-2xl font-bold mb-4 text-center text-white">選択肢</h2>
                
                <!-- Action Buttons -->
                <div class="flex items-center space-x-2 mb-4">
                     <button id="shuffle-btn" class="flex-1 bg-gray-600 hover:bg-gray-700 text-white font-semibold py-2 px-4 rounded-lg transition text-sm">シャッフル</button>
                     <button id="sort-btn" class="flex-1 bg-gray-600 hover:bg-gray-700 text-white font-semibold py-2 px-4 rounded-lg transition text-sm">並べ替え</button>
                </div>

                <!-- Custom Input -->
                <div>
                    <label for="entries-input" class="block mb-2 text-sm font-medium text-gray-400">選択肢を入力 (1行に1つ):</label>
                    <textarea id="entries-input" rows="12" class="block p-3 w-full text-sm text-gray-200 bg-gray-700 rounded-lg border border-gray-600 focus:ring-yellow-500 focus:border-yellow-500"></textarea>
                </div>
                
                <!-- Update Button -->
                <div class="mt-4">
                     <button id="update-wheel-btn" class="w-full bg-blue-600 hover:bg-blue-700 text-white font-bold py-3 px-4 rounded-lg transition">
                        ホイールを更新
                    </button>
                </div>

                <!-- Presets -->
                <div class="mt-6 border-t border-gray-700 pt-6">
                    <label for="preset-select" class="block mb-2 text-sm font-medium text-gray-400">または、プリセットリストを読み込む:</label>
                    <select id="preset-select" class="bg-gray-700 border border-gray-600 text-white text-sm rounded-lg focus:ring-yellow-500 focus:border-yellow-500 block w-full p-2.5">
                        <!-- Options will be populated by JS -->
                    </select>
                </div>
            </section>
        </main>
    </div>

    <!-- Winner Modal -->
    <div id="winner-modal" class="fixed inset-0 bg-black bg-opacity-75 flex items-center justify-center z-50 hidden">
        <div class="bg-gray-800 rounded-2xl shadow-2xl p-8 text-center max-w-md w-full border border-yellow-400 transform transition-all scale-95 opacity-0">
            <h2 class="text-2xl font-bold text-gray-400 mb-2">当選者は...</h2>
            <p id="winner-text" class="text-5xl font-black text-yellow-400 my-6 break-words"></p>
            <button id="close-modal-btn" class="mt-4 bg-yellow-500 text-gray-900 font-bold py-2 px-8 rounded-lg">閉じる</button>
        </div>
    </div>


    <script>
        // --- PRESET DATA ---
        const presets = {
            '-- カスタム入力 --': [],
            'ランチのアイデア': ['ピザ', 'ハンバーガー', '寿司', 'タコス', 'パスタ', 'サラダ', 'ベトナムのフォー', 'ラーメン'],
            '色を選ぶ': ['黒', '白', '青', '灰色', 'ベージュ', '茶色', '緑', 'パステルピンク'],
            'チーム分け': ['チームA', 'チームB', 'チームC', 'チームD'],
            'トレーニングメニュー': ['腕立て伏せ', 'スクワット', 'プランク', 'ランジ', 'バーピー', 'ジャンピングジャック', 'クランチ', '休憩日'],
            '映画のジャンル': ['コメディ', 'ホラー', 'SF', 'アクション', 'ドラマ', 'スリラー', 'ドキュメンタリー', 'アニメ'],
            '賞品ルーレット': ['10%割引', '20%割引', '50%割引', '1つ買うと1つ無料', '送料無料', '500円券', '1000円券', 'もう一度！'],
        };

        // --- DOM ELEMENTS ---
        const canvas = document.getElementById('wheel-canvas');
        const ctx = canvas.getContext('2d');
        const spinBtn = document.getElementById('spin-btn');
        const resultDisplay = document.getElementById('result-display');
        const presetSelect = document.getElementById('preset-select');
        const entriesInput = document.getElementById('entries-input');
        const updateWheelBtn = document.getElementById('update-wheel-btn');
        const shuffleBtn = document.getElementById('shuffle-btn');
        const sortBtn = document.getElementById('sort-btn');
        const wheelOverlay = document.getElementById('wheel-overlay');
        const winnerModal = document.getElementById('winner-modal');
        const winnerText = document.getElementById('winner-text');
        const closeModalBtn = document.getElementById('close-modal-btn');

        // --- WHEEL STATE ---
        let segments = [];
        let currentRotation = 0;
        let isSpinning = false;
        const colors = ["#ef4444", "#f97316", "#eab308", "#84cc16", "#22c55e", "#14b8a6", "#06b6d4", "#3b82f6", "#8b5cf6", "#d946ef"];

        // --- FUNCTIONS ---
        
        function drawWheel() {
            const numSegments = segments.length;
            spinBtn.disabled = numSegments < 2;

            if (numSegments === 0) {
                ctx.clearRect(0, 0, canvas.width, canvas.height);
                ctx.beginPath();
                ctx.arc(canvas.width / 2, canvas.height / 2, canvas.width / 2 - 5, 0, 2 * Math.PI);
                ctx.fillStyle = '#374151'; // gray-700
                ctx.fill();
                wheelOverlay.classList.remove('hidden');
                wheelOverlay.innerHTML = `<p class="text-lg" style="font-size: 1.1rem;">右側で項目を追加してホイールを作成してください！</p>`;
                return;
            };
            
            wheelOverlay.classList.remove('hidden');
            wheelOverlay.innerHTML = `<p>クリックして回す</p>`;

            const anglePerSegment = (2 * Math.PI) / numSegments;
            const centerX = canvas.width / 2;
            const centerY = canvas.height / 2;
            const radius = canvas.width / 2 - 5; 

            ctx.clearRect(0, 0, canvas.width, canvas.height);
            
            segments.forEach((segment, i) => {
                const startAngle = i * anglePerSegment;
                const endAngle = startAngle + anglePerSegment;

                ctx.beginPath();
                ctx.moveTo(centerX, centerY);
                ctx.arc(centerX, centerY, radius, startAngle, endAngle);
                ctx.closePath();
                ctx.fillStyle = colors[i % colors.length];
                ctx.fill();

                ctx.save();
                ctx.strokeStyle = '#111827';
                ctx.lineWidth = 4;
                ctx.stroke();
                ctx.restore();

                ctx.save();
                ctx.translate(centerX, centerY);
                ctx.rotate(startAngle + anglePerSegment / 2);
                ctx.textAlign = "right";
                ctx.fillStyle = "#ffffff";
                ctx.font = getFontSize(segment, radius);

                let text = segment;
                if (text.length > 15) {
                    text = text.substring(0, 14) + '...';
                }
                ctx.fillText(text, radius - 15, 10);
                ctx.restore();
            });
        }

        function getFontSize(text, radius) {
            const baseSize = 18;
            const maxLength = 8;
            if (text.length > maxLength) {
                return `900 ${Math.max(baseSize - (text.length - maxLength), 12)}px 'Noto Sans JP'`;
            }
            return `900 ${baseSize}px 'Noto Sans JP'`;
        }
        
        function populatePresets() {
            for (const key in presets) {
                const option = document.createElement('option');
                option.value = key;
                option.textContent = key;
                presetSelect.appendChild(option);
            }
        }

        function loadPreset() {
            const selectedKey = presetSelect.value;
            const items = presets[selectedKey];
            entriesInput.value = items.join('\n');
            updateWheelFromTextarea();
        }
        
        function updateWheelFromTextarea() {
            const items = entriesInput.value.split('\n').map(item => item.trim()).filter(item => item !== '');
            
            if (items.length > 100) {
                alert("項目数は100個までにしてください。");
                items = items.slice(0, 100);
                entriesInput.value = items.join('\n');
            }

            segments = items;
            currentRotation = 0;
            canvas.style.transform = `rotate(0deg)`;
            drawWheel();
            resultDisplay.innerHTML = '';
        }

        function spin() {
            if (isSpinning || segments.length < 2) return;
            isSpinning = true;
            spinBtn.disabled = true;
            resultDisplay.innerHTML = '';
            wheelOverlay.classList.add('hidden');

            const randomSpins = Math.floor(Math.random() * 5) + 8;
            const randomAngle = Math.random() * 360;
            const totalRotation = randomSpins * 360 + randomAngle;
            const finalRotation = currentRotation + totalRotation;

            canvas.style.transform = `rotate(${finalRotation}deg)`;

            setTimeout(() => {
                isSpinning = false;
                spinBtn.disabled = false;
                currentRotation = finalRotation % 360;
                
                const numSegments = segments.length;
                const anglePerSegment = 360 / numSegments;
                
                const winningAngle = (360 - currentRotation) % 360;
                const winningIndex = Math.floor(winningAngle / anglePerSegment);
                const winner = segments[winningIndex];

                if (winner) {
                    showWinnerModal(winner);
                }
               
            }, 8000);
        }

        function showWinnerModal(winner) {
            winnerText.textContent = winner;
            winnerModal.classList.remove('hidden');
            setTimeout(() => {
                winnerModal.querySelector('div').classList.remove('scale-95', 'opacity-0');
                winnerModal.querySelector('div').classList.add('scale-100', 'opacity-100');
            }, 10);
        }

        function hideWinnerModal() {
             winnerModal.querySelector('div').classList.add('scale-95', 'opacity-0');
             winnerModal.querySelector('div').classList.remove('scale-100', 'opacity-100');
             setTimeout(() => {
                winnerModal.classList.add('hidden');
                wheelOverlay.classList.remove('hidden');
             }, 300);
        }

        function shuffleEntries() {
            let items = entriesInput.value.split('\n').map(item => item.trim()).filter(item => item !== '');
            for (let i = items.length - 1; i > 0; i--) {
                const j = Math.floor(Math.random() * (i + 1));
                [items[i], items[j]] = [items[j], items[i]];
            }
            entriesInput.value = items.join('\n');
            updateWheelFromTextarea();
        }

        function sortEntries() {
            let items = entriesInput.value.split('\n').map(item => item.trim()).filter(item => item !== '');
            items.sort((a, b) => a.localeCompare(b, 'ja'));
            entriesInput.value = items.join('\n');
            updateWheelFromTextarea();
        }

        // --- EVENT LISTENERS ---
        document.addEventListener('DOMContentLoaded', () => {
            populatePresets();
            presetSelect.value = 'ランチのアイデア'; 
            loadPreset();
        });

        spinBtn.addEventListener('click', spin);
        canvas.addEventListener('click', spin);
        updateWheelBtn.addEventListener('click', updateWheelFromTextarea);
        presetSelect.addEventListener('change', loadPreset);
        shuffleBtn.addEventListener('click', shuffleEntries);
        sortBtn.addEventListener('click', sortEntries);
        closeModalBtn.addEventListener('click', hideWinnerModal);
        winnerModal.addEventListener('click', (e) => {
            if (e.target === winnerModal) {
                hideWinnerModal();
            }
        });
        document.addEventListener('keydown', (e) => {
            if (e.key === 'Escape') {
                hideWinnerModal();
            }
        });

    </script>
</body>
</html>
]]></content:encoded>
					
					<wfw:commentRss>https://dejitarumirai.com/archives/4230/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>ゆで卵の茹で時間</title>
		<link>https://dejitarumirai.com/archives/4227</link>
					<comments>https://dejitarumirai.com/archives/4227#respond</comments>
		
		<dc:creator><![CDATA[deji]]></dc:creator>
		<pubDate>Sat, 05 Jul 2025 14:38:41 +0000</pubDate>
				<category><![CDATA[Webapps]]></category>
		<guid isPermaLink="false">https://dejitarumirai.com/?p=4227</guid>

					<description><![CDATA[ゆで卵タイマー - 完璧なゆで [&#8230;]]]></description>
										<content:encoded><![CDATA[<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>ゆで卵タイマー - 完璧なゆで卵を毎回</title>
    <meta name="description" content="カウントダウン時計付きの完璧なゆで卵タイマー。プリセット時間と簡単なガイドで、半熟、ミディアム、固ゆで卵を簡単に作りましょう。">
    <meta name="keywords" content="ゆで卵, タイマー, 茹で時間, カウントダウン, 半熟, 固ゆで">
    
    <!-- Tailwind CSS CDN -->
    <script src="https://cdn.tailwindcss.com"></script>
    <!-- Tone.js for sound -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/tone/14.7.77/Tone.js"></script>
    <!-- Google Fonts: Inter & Noto Sans JP & Roboto Mono -->
    <link rel="preconnect" href="https://fonts.googleapis.com">
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
    <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=Noto+Sans+JP:wght@400;500;700&family=Roboto+Mono:wght@400;700&display=swap" rel="stylesheet">
    <style>
        body {
            font-family: 'Noto Sans JP', 'Inter', sans-serif;
            background-color: #111827; /* gray-900 */
            color: #d1d5db; /* gray-300 */
        }
        .timer-display {
            font-family: 'Roboto Mono', monospace;
        }
        .preset-btn {
            transition: all 0.2s ease-in-out;
            border: 2px solid transparent;
        }
        .preset-btn.selected {
            border-color: #3b82f6; /* blue-500 */
            transform: scale(1.05);
            box-shadow: 0 0 15px rgba(59, 130, 246, 0.4);
            background-color: #3b82f6; /* blue-500 */
            color: white;
        }
        .progress-ring__circle {
            transition: stroke-dashoffset 1s linear;
            transform: rotate(-90deg);
            transform-origin: 50% 50%;
        }
        .prose {
            max-width: 65ch;
            margin-left: auto;
            margin-right: auto;
        }
    </style>
</head>
<body class="text-gray-300">

    <div class="container mx-auto p-4 sm:p-8 max-w-4xl">
        <!-- Header -->
        <header class="text-center mb-10">
            <h1 class="text-3xl sm:text-5xl font-extrabold text-white">完璧なゆで卵タイマー</h1>
            <p class="text-gray-400 mt-2 text-lg">お好みの固さを選んでタイマーをスタート！</p>
        </header>

        <!-- Main Timer Section -->
        <div class="bg-gray-800 p-6 sm:p-8 rounded-2xl shadow-2xl mb-12">
            <!-- Timer Display -->
            <div class="relative w-48 h-48 sm:w-64 sm:h-64 mx-auto mb-6">
                <svg class="w-full h-full" viewBox="0 0 120 120">
                    <circle class="text-gray-700" stroke-width="8" stroke="currentColor" fill="transparent" r="52" cx="60" cy="60" />
                    <circle id="progress-ring" class="progress-ring__circle text-blue-500" stroke-width="8" stroke-linecap="round" stroke="currentColor" fill="transparent" r="52" cx="60" cy="60" />
                </svg>
                <div id="timer-display" class="absolute inset-0 flex items-center justify-center timer-display text-4xl sm:text-6xl font-bold text-white">00:00</div>
            </div>

            <!-- Preset Buttons -->
            <div class="grid grid-cols-2 md:grid-cols-4 gap-4 mb-6">
                <button class="preset-btn bg-gray-700 hover:bg-gray-600 p-4 rounded-lg" data-time="360">
                    <span class="font-bold text-lg">6分</span>
                    <span class="block text-sm">超半熟</span>
                </button>
                <button class="preset-btn bg-gray-700 hover:bg-gray-600 p-4 rounded-lg" data-time="480">
                    <span class="font-bold text-lg">8分</span>
                    <span class="block text-sm">半熟</span>
                </button>
                <button class="preset-btn bg-gray-700 hover:bg-gray-600 p-4 rounded-lg" data-time="600">
                    <span class="font-bold text-lg">10分</span>
                    <span class="block text-sm">やや固ゆで</span>
                </button>
                 <button class="preset-btn bg-gray-700 hover:bg-gray-600 p-4 rounded-lg" data-time="720">
                    <span class="font-bold text-lg">12分</span>
                    <span class="block text-sm">固ゆで</span>
                </button>
            </div>

            <!-- Control Buttons -->
            <div class="grid grid-cols-2 gap-4">
                <button id="start-pause-btn" class="bg-green-600 hover:bg-green-700 text-white font-bold py-3 px-4 rounded-lg text-lg disabled:opacity-50 disabled:cursor-not-allowed transition-colors">スタート</button>
                <button id="reset-btn" class="bg-red-600 hover:bg-red-700 text-white font-bold py-3 px-4 rounded-lg text-lg transition-colors">リセット</button>
            </div>
        </div>

        <!-- SEO Content & Instructions -->
        <article class="prose prose-lg prose-invert">
            <h2 class="text-white">ゆで卵の作り方（半熟から固ゆでまで）</h2>
            <p>ゆで卵を好みの黄身の状態にするのは案外むずかしいものです。このガイドでは、冷蔵庫から出したての冷たい卵を沸騰したお湯に入れる方法で、毎回同じ仕上がりを目指します。</p>

            <h3>ゆで時間の目安</h3>
            <p>タイマーを使うか、以下のガイドを参考にしてください。時間は、沸騰したお湯に冷たい卵（Lサイズ）を入れた場合の時間です。</p>
            <ul>
                <li><strong>6分:</strong> 超半熟。黄身はとろとろで、白身も柔らかい。</li>
                <li><strong>7分:</strong> 半熟。黄身の外側が少し固まり、中心はとろとろ。</li>
                <li><strong>8分:</strong> 黄身の固まった部分と柔らかい部分が半々くらい。</li>
                <li><strong>9分:</strong> 黄身の中央まで固まっているが、まだ鮮やかな色。</li>
                <li><strong>10分:</strong> 黄身の外側がしっかり固まった、白っぽい黄色の固ゆで。</li>
                <li><strong>12分:</strong> 完全な固ゆで。黄身全体が白っぽく固まっている。</li>
            </ul>

            <h3>作り方</h3>
            <ol>
                <li>鍋にたっぷりのお湯を沸かします。</li>
                <li>沸騰したら、冷蔵庫から出した冷たい卵をお玉などでそっと入れます。（ひび割れ注意！）</li>
                <li>好みの時間でタイマーをセットし、茹で始めます。黄身を中央にしたい場合は、最初の2分間、箸で卵を軽く転がしてください。</li>
                <li>設定時間が来たら、すぐにお湯を捨て、冷水に取ります。</li>
                <li>最低5分間、冷水でしっかりと冷やします。これにより、余熱で火が通り過ぎるのを防ぎ、殻がむきやすくなります。</li>
            </ol>

            <h3>きれいに殻をむくコツ</h3>
            <ul>
                <li><strong>ひびを入れる:</strong> ゆで卵全体に優しくひびを入れると、殻がむきやすくなります。</li>
                <li><strong>水の中でむく:</strong> 流水に当てながらか、ボウルに張った水の中で殻をむくと、殻と白身の間に水が入り込み、つるんと綺麗にむけます。</li>
                <li><strong>少し古い卵を使う:</strong> 産みたてよりも、産卵後1週間ほど経った卵の方が、炭酸ガスが抜けて殻がむきやすくなります。</li>
            </ul>
        </article>
    </div>

    <script>
        // DOM Elements
        const timerDisplay = document.getElementById('timer-display');
        const progressRing = document.getElementById('progress-ring');
        const startPauseBtn = document.getElementById('start-pause-btn');
        const resetBtn = document.getElementById('reset-btn');
        const presetBtns = document.querySelectorAll('.preset-btn');
        
        // Timer state
        let timerInterval;
        let totalSeconds = 0;
        let remainingSeconds = 0;
        let isPaused = false;
        let isRunning = false;
        
        // Progress Ring setup
        const radius = progressRing.r.baseVal.value;
        const circumference = radius * 2 * Math.PI;
        progressRing.style.strokeDasharray = `${circumference} ${circumference}`;
        progressRing.style.strokeDashoffset = circumference;

        // Function to update the progress ring
        function setProgress(percent) {
            const offset = circumference - (percent / 100) * circumference;
            progressRing.style.strokeDashoffset = offset;
        }

        // Function to format time as MM:SS
        function formatTime(seconds) {
            const mins = Math.floor(seconds / 60);
            const secs = seconds % 60;
            return `${String(mins).padStart(2, '0')}:${String(secs).padStart(2, '0')}`;
        }
        
        // Function to update the display
        function updateDisplay() {
            timerDisplay.textContent = formatTime(remainingSeconds);
            if (totalSeconds > 0) {
                const percent = ((totalSeconds - remainingSeconds) / totalSeconds) * 100;
                setProgress(percent);
            } else {
                setProgress(0);
            }
        }

        // Function to start the timer
        function startTimer() {
            if (isRunning && !isPaused) return;

            isRunning = true;
            isPaused = false;
            startPauseBtn.textContent = '一時停止';
            
            const endTime = Date.now() + remainingSeconds * 1000;

            timerInterval = setInterval(() => {
                remainingSeconds = Math.round((endTime - Date.now()) / 1000);
                
                if (remainingSeconds < 0) {
                    finishTimer();
                    return;
                }
                updateDisplay();
            }, 500);
        }
        
        // Function to pause the timer
        function pauseTimer() {
            clearInterval(timerInterval);
            isPaused = true;
            startPauseBtn.textContent = '再開';
        }

        // Function to reset the timer
        function resetTimer() {
            clearInterval(timerInterval);
            isRunning = false;
            isPaused = false;
            remainingSeconds = totalSeconds;
            startPauseBtn.textContent = 'スタート';
            startPauseBtn.disabled = totalSeconds === 0;
            updateDisplay();
        }
        
        // Function to handle timer completion
        function finishTimer() {
             clearInterval(timerInterval);
             isRunning = false;
             isPaused = false;
             timerDisplay.textContent = "完成！";
             startPauseBtn.textContent = 'スタート';
             startPauseBtn.disabled = true;
             setProgress(100);
             playSound();
        }

        // Function to play a notification sound
        function playSound() {
            // Use Tone.js to play a simple melody
            const synth = new Tone.Synth().toDestination();
            const now = Tone.now();
            synth.triggerAttackRelease("C5", "8n", now);
            synth.triggerAttackRelease("G5", "8n", now + 0.2);
            synth.triggerAttackRelease("C6", "8n", now + 0.4);
        }

        // --- Event Listeners ---

        // Preset buttons
        presetBtns.forEach(btn => {
            btn.addEventListener('click', () => {
                presetBtns.forEach(b => b.classList.remove('selected'));
                btn.classList.add('selected');
                
                totalSeconds = parseInt(btn.dataset.time, 10);
                resetTimer();
                startPauseBtn.disabled = false;
            });
        });

        // Start/Pause button
        startPauseBtn.addEventListener('click', () => {
            if (totalSeconds === 0) return;
            if (isRunning && !isPaused) {
                pauseTimer();
            } else {
                startTimer();
            }
        });

        // Reset button
        resetBtn.addEventListener('click', () => {
            presetBtns.forEach(b => b.classList.remove('selected'));
            totalSeconds = 0;
            resetTimer();
            timerDisplay.textContent = '00:00';
            setProgress(0);
        });
        
        // Set initial state on page load
        setProgress(0);

    </script>

</body>
</html>
]]></content:encoded>
					
					<wfw:commentRss>https://dejitarumirai.com/archives/4227/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>カウントダウンタイマー</title>
		<link>https://dejitarumirai.com/archives/4224</link>
					<comments>https://dejitarumirai.com/archives/4224#respond</comments>
		
		<dc:creator><![CDATA[deji]]></dc:creator>
		<pubDate>Sat, 05 Jul 2025 14:25:46 +0000</pubDate>
				<category><![CDATA[Webapps]]></category>
		<guid isPermaLink="false">https://dejitarumirai.com/?p=4224</guid>

					<description><![CDATA[オンラインストップウォッチ - [&#8230;]]]></description>
										<content:encoded><![CDATA[<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>オンラインストップウォッチ - 学習時間管理ツール</title>
    <meta name="description" content="正確で使いやすいオンラインストップウォッチ。勉強、仕事、トレーニングの時間を計るのに最適なツールです。ラップ機能とデータエクスポート機能付き。">
    <meta name="keywords" content="ストップウォッチ, オンラインストップウォッチ, 時間測定, 勉強タイマー, stopwatch online">
    
    <!-- Tailwind CSS CDN -->
    <script src="https://cdn.tailwindcss.com"></script>
    <!-- Google Fonts: Inter & Roboto Mono -->
    <link rel="preconnect" href="https://fonts.googleapis.com">
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
    <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=Roboto+Mono:wght@400;700&family=Noto+Sans+JP:wght@400;700&display=swap" rel="stylesheet">
    <style>
        body {
            font-family: 'Inter', 'Noto Sans JP', sans-serif;
            background-color: #111827; /* gray-900 */
            color: #d1d5db; /* gray-300 */
        }
        .timer-display {
            font-family: 'Roboto Mono', monospace;
        }
        .btn {
            transition: all 0.2s ease-in-out;
            border-radius: 0.5rem;
            padding: 0.75rem 1rem;
            font-weight: bold;
            font-size: 1.125rem;
            color: white;
        }
        .btn:hover {
            transform: translateY(-2px);
            box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
        }
        .btn-outline {
            background-color: transparent;
            border-width: 1px;
        }
        .prose-invert ul > li::before {
            background-color: #4ade80; /* green-400 */
        }
    </style>
</head>
<body>

    <div class="container mx-auto p-4 sm:p-8 max-w-4xl">
        <!-- Header -->
        <header class="text-center mb-10">
            <h1 class="text-4xl sm:text-5xl font-extrabold text-white">学習用ストップウォッチ</h1>
            <p class="text-gray-400 mt-2">時間を管理し、生産性を向上させるための完璧なツールです。</p>
        </header>

        <!-- Stopwatch Display -->
        <div class="bg-gray-800 p-6 sm:p-8 rounded-2xl shadow-2xl mb-8">
            <div class="text-center tabular-nums">
                <!-- Main timer display -->
                <div id="main-timer" class="timer-display text-6xl sm:text-8xl md:text-9xl font-bold text-white tracking-wider">00:00:00</div>
                <!-- Lap timer display -->
                <div id="lap-timer" class="timer-display text-2xl text-green-400 mt-2 h-8"></div>
            </div>
            <!-- Action Buttons -->
            <div class="mt-8 grid grid-cols-2 sm:grid-cols-4 gap-4">
                <button id="start-btn" class="btn bg-blue-600 hover:bg-blue-500 col-span-2 sm:col-span-1">スタート</button>
                <button id="pause-btn" class="btn bg-yellow-500 hover:bg-yellow-400 hidden col-span-2 sm:col-span-1">一時停止</button>
                <button id="lap-btn" class="btn bg-gray-600 hover:bg-gray-500 hidden">ラップ</button>
                <button id="reset-btn" class="btn bg-red-600 hover:bg-red-500 hidden">リセット</button>
            </div>
        </div>

        <!-- Laps Table -->
        <div id="laps-container" class="hidden">
            <h2 class="text-2xl font-bold text-white mb-4">ラップ履歴</h2>
            <div class="bg-gray-800 rounded-lg shadow-lg max-h-80 overflow-y-auto">
                <table class="w-full text-sm text-left">
                    <thead class="text-xs text-gray-300 uppercase bg-gray-700 sticky top-0">
                        <tr>
                            <th scope="col" class="px-6 py-3">ラップ</th>
                            <th scope="col" class="px-6 py-3">ラップタイム</th>
                            <th scope="col" class="px-6 py-3">合計時間</th>
                        </tr>
                    </thead>
                    <tbody id="laps-body">
                        <!-- Lap rows will be dynamically inserted here by JavaScript -->
                    </tbody>
                </table>
            </div>
            <!-- Lap Actions -->
             <div class="mt-6 flex justify-center gap-x-4 w-full">
                <button id="export-csv-btn" class="btn btn-outline border-green-500 text-green-400 hover:bg-green-500 hover:text-white flex-grow">CSVエクスポート</button>
                <button id="clear-laps-btn" class="btn btn-outline border-red-500 text-red-400 hover:bg-red-500 hover:text-white flex-grow">履歴をクリア</button>
            </div>
        </div>
        
        <!-- SEO & Help Content -->
        <article class="prose prose-invert max-w-full mt-12 text-gray-400">
            <h2>オンラインストップウォッチとは？</h2>
            <p>オンラインストップウォッチは、経過時間を正確に測定できる使いやすいアプリケーションです。パソコンやスマートフォンのブラウザ上でデジタルストップウォッチとして機能し、勉強、仕事、スポーツトレーニングなど、正確な時間管理が必要なあらゆる活動に最適です。</p>
            
            <h2>ストップウォッチの使い方</h2>
            <p>このツールの使い方はとても簡単です：</p>
            <ul>
                <li><strong>スタート</strong>ボタンを押してタイマーを開始します。</li>
                <li>休憩が必要な場合は<strong>一時停止</strong>ボタンを押してタイマーを止めます。<strong>スタート</strong>ボタンは<strong>再開</strong>ボタンに変わります。</li>
                <li><strong>ラップ</strong>ボタンを押してタイムマーカーを記録します（例：本の1章、エクササイズの1セットの終了時）。ラップタイムと合計時間は「ラップ履歴」の表に保存されます。</li>
                <li><strong>リセット</strong>ボタンを押すと、すべての計測がクリアされ、タイマーがゼロに戻ります。</li>
            </ul>
            <p>ラップデータは、<strong>CSVエクスポート</strong>ボタンをクリックすることで、CSVファイルとしてエクスポートでき、パフォーマンスを保存・分析することができます。</p>
            
            <h2>ブラウザを閉じてもストップウォッチは動作しますか？</h2>
            <p>いいえ。ブラウザのタブやウィンドウを閉じると、ストップウォッチは停止します。ただし、別のタブに切り替えたり、コンピュータで別のアプリケーションを使用している間は、計測を続行します。</p>
        </article>
    </div>

    <script>
        // Get references to all necessary DOM elements
        const mainTimerEl = document.getElementById('main-timer');
        const lapTimerEl = document.getElementById('lap-timer');
        const startBtn = document.getElementById('start-btn');
        const pauseBtn = document.getElementById('pause-btn');
        const lapBtn = document.getElementById('lap-btn');
        const resetBtn = document.getElementById('reset-btn');
        const lapsContainer = document.getElementById('laps-container');
        const lapsBody = document.getElementById('laps-body');
        const exportCsvBtn = document.getElementById('export-csv-btn');
        const clearLapsBtn = document.getElementById('clear-laps-btn');

        // State variables to manage the stopwatch's logic
        let startTime = 0;
        let elapsedTime = 0;
        let lapStartTime = 0;
        let timerInterval; // To hold the setInterval ID
        let laps = []; // Array to store lap data
        let lapCounter = 0;

        /**
         * Formats a time in milliseconds to HH:MM:SS format.
         * @param {number} time - The time in milliseconds.
         * @returns {string} The formatted time string.
         */
        function formatTime(time) {
            const date = new Date(time);
            const hours = String(date.getUTCHours()).padStart(2, '0');
            const minutes = String(date.getUTCMinutes()).padStart(2, '0');
            const seconds = String(date.getUTCSeconds()).padStart(2, '0');
            return `${hours}:${minutes}:${seconds}`;
        }

        /**
         * Updates the main timer and the current lap timer displays.
         */
        function updateTimers() {
            const currentTime = Date.now();
            elapsedTime = currentTime - startTime;
            const lapTime = currentTime - lapStartTime;
            mainTimerEl.textContent = formatTime(elapsedTime);
            lapTimerEl.textContent = formatTime(lapTime);
        }

        /**
         * Starts or resumes the timer.
         */
        function startTimer() {
            // Adjust start time to account for paused duration
            startTime = Date.now() - elapsedTime;
            lapStartTime = Date.now() - (elapsedTime - (laps[0]?.totalTime || 0));
            timerInterval = setInterval(updateTimers, 100);
            
            // Update button visibility and text
            startBtn.classList.add('hidden');
            startBtn.textContent = '再開'; // Change text for when it's paused and resumed
            pauseBtn.classList.remove('hidden');
            lapBtn.classList.remove('hidden');
            resetBtn.classList.remove('hidden');
        }

        /**
         * Pauses the timer.
         */
        function pauseTimer() {
            clearInterval(timerInterval);
            
            // Update button visibility
            pauseBtn.classList.add('hidden');
            startBtn.classList.remove('hidden');
        }

        /**
         * Resets the entire stopwatch to its initial state.
         */
        function resetTimer() {
            clearInterval(timerInterval);
            // Reset all state variables
            startTime = 0;
            elapsedTime = 0;
            lapStartTime = 0;
            laps = [];
            lapCounter = 0;

            // Reset display elements
            mainTimerEl.textContent = '00:00:00';
            lapTimerEl.textContent = '';
            lapsBody.innerHTML = '';
            
            // Reset button states
            startBtn.textContent = 'スタート';
            startBtn.classList.remove('hidden');
            pauseBtn.classList.add('hidden');
            lapBtn.classList.add('hidden');
            resetBtn.classList.add('hidden');
            lapsContainer.classList.add('hidden');
        }

        /**
         * Records a new lap.
         */
        function recordLap() {
            lapCounter++;
            const currentTime = Date.now();
            const totalTime = currentTime - startTime;
            // The time for this specific lap is the total elapsed time minus the total time of the previous lap.
            const previousTotalTime = laps.length > 0 ? laps[0].totalTime : 0;
            const lapTime = totalTime - previousTotalTime;

            // Add new lap to the beginning of the array
            laps.unshift({
                id: lapCounter,
                lapTime: lapTime,
                totalTime: totalTime
            });

            renderLaps();
            lapsContainer.classList.remove('hidden');
            // Reset the lap timer display for the new lap
            lapStartTime = currentTime;
        }

        /**
         * Renders the list of laps in the history table.
         */
        function renderLaps() {
            lapsBody.innerHTML = ''; // Clear existing rows
            laps.forEach(lap => {
                const row = document.createElement('tr');
                row.className = 'bg-gray-800 border-b border-gray-700';
                row.innerHTML = `
                    <td class="px-6 py-4 font-medium text-white">${lap.id}</td>
                    <td class="px-6 py-4">${formatTime(lap.lapTime)}</td>
                    <td class="px-6 py-4">${formatTime(lap.totalTime)}</td>
                `;
                lapsBody.appendChild(row);
            });
        }

        /**
         * Exports the lap history to a CSV file.
         */
        function exportToCsv() {
            if (laps.length === 0) return; // Don't export if there's no data
            let csvContent = "data:text/csv;charset=utf-8,ラップ,ラップタイム,合計時間\n";
            // Reverse the laps array to export in chronological order (1, 2, 3...)
            const reversedLaps = [...laps].reverse(); 
            reversedLaps.forEach(lap => {
                const row = [lap.id, formatTime(lap.lapTime), formatTime(lap.totalTime)].join(",");
                csvContent += row + "\n";
            });

            // Create a temporary link to trigger the download
            const encodedUri = encodeURI(csvContent);
            const link = document.createElement("a");
            link.setAttribute("href", encodedUri);
            link.setAttribute("download", "stopwatch_rireki.csv");
            document.body.appendChild(link);
            link.click();
            document.body.removeChild(link);
        }
        
        /**
         * Clears the lap history without resetting the main timer.
         */
        function clearLaps() {
            laps = [];
            lapCounter = 0;
            lapsBody.innerHTML = '';
            lapsContainer.classList.add('hidden');
            // Reset the lap timer display and its start time
            lapTimerEl.textContent = '';
            lapStartTime = startTime ? Date.now() : 0;
        }

        // --- Event Listeners ---
        startBtn.addEventListener('click', startTimer);
        pauseBtn.addEventListener('click', pauseTimer);
        resetBtn.addEventListener('click', resetTimer);
        lapBtn.addEventListener('click', recordLap);
        exportCsvBtn.addEventListener('click', exportToCsv);
        clearLapsBtn.addEventListener('click', clearLaps);

    </script>

</body>
</html>
]]></content:encoded>
					
					<wfw:commentRss>https://dejitarumirai.com/archives/4224/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>タイピング速度測定</title>
		<link>https://dejitarumirai.com/archives/4221</link>
					<comments>https://dejitarumirai.com/archives/4221#respond</comments>
		
		<dc:creator><![CDATA[deji]]></dc:creator>
		<pubDate>Sat, 05 Jul 2025 14:17:12 +0000</pubDate>
				<category><![CDATA[Webapps]]></category>
		<guid isPermaLink="false">https://dejitarumirai.com/?p=4221</guid>

					<description><![CDATA[タイピング速度測定 タイピング [&#8230;]]]></description>
										<content:encoded><![CDATA[<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>タイピング速度測定</title>
    <script src="https://cdn.tailwindcss.com"></script>
    <link rel="preconnect" href="https://fonts.googleapis.com">
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
    <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;700&family=Noto+Sans+JP:wght@400;500;700&display=swap" rel="stylesheet">
    <link href="https://cdnjs.cloudflare.com/ajax/libs/heroicons/2.1.3/24/solid/styles.min.css" rel="stylesheet">
    <style>
        body {
            font-family: 'Noto Sans JP', 'Inter', sans-serif;
            background-color: #111827; /* gray-900 */
            color: #d1d5db; /* gray-300 */
        }
        /* Custom scrollbar for the text display area */
        #text-display::-webkit-scrollbar, #leaderboard-list::-webkit-scrollbar, #practice-area::-webkit-scrollbar {
            width: 8px;
        }
        #text-display::-webkit-scrollbar-track, #leaderboard-list::-webkit-scrollbar-track, #practice-area::-webkit-scrollbar-track {
            background: #1f2937; /* gray-800 */
        }
        #text-display::-webkit-scrollbar-thumb, #leaderboard-list::-webkit-scrollbar-thumb, #practice-area::-webkit-scrollbar-thumb {
            background: #4b5563; /* gray-600 */
            border-radius: 4px;
        }
        #text-display::-webkit-scrollbar-thumb:hover, #leaderboard-list::-webkit-scrollbar-thumb:hover, #practice-area::-webkit-scrollbar-thumb:hover {
            background: #6b7280; /* gray-500 */
        }
        .correct {
            color: #a3e635; /* lime-400 */
        }
        .incorrect {
            color: #ef4444; /* red-500 */
            text-decoration: underline;
            text-decoration-color: #ef4444;
        }
        .cursor {
            display: inline-block;
            width: 2px;
            height: 1.75rem; /* h-7 */
            background-color: #38bdf8; /* sky-400 */
            animation: blink 1s infinite;
            margin-left: 1px;
            border-radius: 1px;
            vertical-align: middle;
        }
        @keyframes blink {
            50% { opacity: 0; }
        }
        .test-inactive .cursor {
            display: none;
        }
        #text-input {
            position: absolute;
            top: -9999px;
            left: -9999px;
            opacity: 0;
            width: 0;
            height: 0;
        }
        /* Sidebar active link style */
        .sidebar-link.active {
            background-color: #0ea5e920; /* sky-500/20 */
            border-color: #0ea5e950; /* sky-500/30 */
            color: #ffffff;
        }
    </style>
</head>
<body class="flex items-center justify-center min-h-screen">

    <div class="w-full max-w-6xl mx-auto p-4 sm:p-6 lg:p-8">
        <div class="grid grid-cols-1 lg:grid-cols-4 gap-8">
            <!-- Sidebar -->
            <aside class="lg:col-span-1">
                <div class="bg-gray-800 p-6 rounded-2xl border border-gray-700 h-full">
                    <div class="flex items-center gap-3 mb-6">
                         <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="w-8 h-8 text-sky-400">
                            <path fill-rule="evenodd" d="M12 2.25c-5.385 0-9.75 4.365-9.75 9.75s4.365 9.75 9.75 9.75 9.75-4.365 9.75-9.75S17.385 2.25 12 2.25Zm.53 5.47a.75.75 0 0 0-1.06 0l-3 3a.75.75 0 1 0 1.06 1.06l1.72-1.72v5.69a.75.75 0 0 0 1.5 0v-5.69l1.72 1.72a.75.75 0 1 0 1.06-1.06l-3-3Z" clip-rule="evenodd" />
                        </svg>
                        <h1 class="text-xl font-bold text-white">タイピング測定</h1>
                    </div>
                    <nav class="space-y-2">
                        <a href="#" id="typing-test-link" class="sidebar-link flex items-center gap-3 px-4 py-2 text-gray-400 hover:bg-gray-700/50 rounded-lg transition-colors border border-transparent">
                            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-5 h-5"><path d="M10 8a2 2 0 1 0 0-4 2 2 0 0 0 0 4Z" /><path fill-rule="evenodd" d="M10 2a.75.75 0 0 1 .75.75v.5a.75.75 0 0 1-1.5 0v-.5A.75.75 0 0 1 10 2ZM5.28 4.22a.75.75 0 0 1 0 1.06l-.5.5a.75.75 0 1 1-1.06-1.06l.5-.5a.75.75 0 0 1 1.06 0ZM15.78 5.28a.75.75 0 0 1-1.06 0l-.5-.5a.75.75 0 0 1 1.06-1.06l.5.5a.75.75 0 0 1 0 1.06ZM17 10a.75.75 0 0 1-.75.75h-.5a.75.75 0 0 1 0-1.5h.5A.75.75 0 0 1 17 10ZM4.25 10.75a.75.75 0 0 1 0-1.5h-.5a.75.75 0 0 1 0 1.5h.5ZM14.72 14.72a.75.75 0 0 1 0-1.06l.5-.5a.75.75 0 1 1 1.06 1.06l-.5.5a.75.75 0 0 1-1.06 0ZM5.28 15.78a.75.75 0 0 1-1.06 0l-.5.5a.75.75 0 0 1-1.06-1.06l.5-.5a.75.75 0 0 1 1.06 0ZM10 17a.75.75 0 0 1 .75.75v.5a.75.75 0 0 1-1.5 0v-.5A.75.75 0 0 1 10 17ZM10 12a2 2 0 1 1 0-4 2 2 0 0 1 0 4Z" clip-rule="evenodd" /></svg>
                            <span>タイピングテスト</span>
                        </a>
                        <a href="#" id="practice-link" class="sidebar-link flex items-center gap-3 px-4 py-2 text-gray-400 hover:bg-gray-700/50 rounded-lg transition-colors border border-transparent">
                           <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-5 h-5"><path d="M11.983 1.904a.75.75 0 0 0-1.292-.782l-5.015 8.7a.75.75 0 0 0 1.292.782l5.015-8.7Z" /><path d="m13.844 12.214.339.587a.75.75 0 0 1-1.292.782l-1.37-2.374a.75.75 0 0 1 1.292-.782l1.031 1.787Z" /><path d="M12.25 10.25a2 2 0 1 0 4 0 2 2 0 0 0-4 0Z" /><path d="M15.5 10.25a3.25 3.25 0 1 0-6.5 0 3.25 3.25 0 0 0 6.5 0Z" /><path d="M8.283 6.034a.75.75 0 0 0-1.292-.782l-1.37 2.374a.75.75 0 1 0 1.292.782l1.37-2.374Z" /><path d="m.895 10.03-.98 1.697a.75.75 0 1 0 1.299.75l.98-1.697a.75.75 0 1 0-1.3-.75Z" /><path d="M6.25 15.5a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0Z" /><path d="M4.75 18.5a1.5 1.5 0 1 1 3 0 1.5 1.5 0 0 1-3 0Z" /><path d="M9 14a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0Z" /></svg>
                            <span>練習</span>
                        </a>
                        <a href="#" id="leaderboard-link" class="sidebar-link flex items-center gap-3 px-4 py-2 text-gray-400 hover:bg-gray-700/50 rounded-lg transition-colors border border-transparent">
                            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-5 h-5"><path fill-rule="evenodd" d="M6 3.75A2.75 2.75 0 0 1 8.75 1h2.5A2.75 2.75 0 0 1 14 3.75v.443c.57.166 1.111.418 1.605.748a.75.75 0 1 1-.81 1.282A11.96 11.96 0 0 0 14 6.017V11a1 1 0 0 1-1 1H7a1 1 0 0 1-1-1V6.017c-1.141.213-2.22.62-3.205 1.214a.75.75 0 0 1-.81-1.282A13.443 13.443 0 0 1 6 4.193V3.75Z" clip-rule="evenodd" /><path d="M10 14a1 1 0 0 1 1 1v1.5a1.5 1.5 0 0 1-3 0V15a1 1 0 0 1 1-1Z" /></svg>
                            <span>ランキング</span>
                        </a>
                    </nav>
                </div>
            </aside>

            <!-- Main Content -->
            <main class="lg:col-span-3">
                <div id="test-setup" class="main-view bg-gray-800 p-8 rounded-2xl border border-gray-700 flex flex-col items-center justify-center h-full">
                    <h2 class="text-3xl font-bold text-white mb-2">タイピング速度テスト</h2>
                    <p class="text-gray-400 mb-8">オプションを選択してテストを開始してください！</p>
                    <div class="flex flex-col sm:flex-row gap-4 mb-8">
                        <div class="w-full sm:w-48">
                            <label for="time-select" class="block text-sm font-medium text-gray-400 mb-2">時間</label>
                            <select id="time-select" class="w-full bg-gray-700 border border-gray-600 text-white text-sm rounded-lg focus:ring-sky-500 focus:border-sky-500 p-2.5">
                                <option value="60" selected>1分</option>
                                <option value="120">2分</option>
                                <option value="300">5分</option>
                            </select>
                        </div>
                    </div>
                    <button id="start-btn" class="bg-sky-500 hover:bg-sky-600 text-white font-bold py-3 px-10 rounded-lg transition-colors text-lg">
                        テスト開始
                    </button>
                </div>

                <div id="test-area" class="main-view hidden">
                    <div class="grid grid-cols-2 md:grid-cols-4 gap-4 mb-6">
                        <div class="bg-gray-800 p-4 rounded-xl border border-gray-700 text-center"><p class="text-sm text-gray-400">時間</p><p id="timer" class="text-2xl font-bold text-white">60</p></div>
                        <div class="bg-gray-800 p-4 rounded-xl border border-gray-700 text-center"><p class="text-sm text-gray-400">KPM</p><p id="kpm" class="text-2xl font-bold text-white">0</p></div>
                        <div class="bg-gray-800 p-4 rounded-xl border border-gray-700 text-center"><p class="text-sm text-gray-400">正確率</p><p id="accuracy" class="text-2xl font-bold text-white">100%</p></div>
                        <div class="bg-gray-800 p-4 rounded-xl border border-gray-700 text-center"><p class="text-sm text-gray-400">ミス</p><p id="errors" class="text-2xl font-bold text-red-500">0</p></div>
                    </div>
                    <div id="text-container" class="relative bg-gray-800 p-6 rounded-2xl border border-gray-700 mb-6 test-inactive" onclick="document.getElementById('text-input').focus()">
                        <div id="japanese-text-display" class="text-3xl text-white mb-4 text-center"></div>
                        <div id="text-display" class="text-2xl font-mono tracking-wide leading-relaxed h-32 overflow-y-auto"></div>
                        <input type="text" id="text-input" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false">
                    </div>
                    <div class="flex justify-center">
                        <button id="reset-btn" class="bg-gray-600 hover:bg-gray-500 text-white font-bold py-3 px-8 rounded-lg transition-colors flex items-center gap-2">
                            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-5 h-5"><path fill-rule="evenodd" d="M15.312 11.424a5.5 5.5 0 0 1-9.201-4.42 5.5 5.5 0 0 1 10.89 2.166l.255-.256a.75.75 0 0 1 1.06 1.06l-2.5 2.5a.75.75 0 0 1-1.06 0l-2.5-2.5a.75.75 0 1 1 1.06-1.06l.64.641a4 4 0 0 0-7.143-2.957 4 4 0 0 0 7.83 1.626l.216.432a.75.75 0 0 1-.941 1.055l-.216-.432a2.5 2.5 0 0 1-4.89-1.015c.34-.09.688-.14 1.038-.14a2.5 2.5 0 0 1 2.212 1.288l-.352.353a.75.75 0 0 1-1.06-1.06l.352-.353a1 1 0 0 0-.885-.514H8.25a.75.75 0 0 1 0-1.5h2.263a2.5 2.5 0 0 1 4.799 1.076Z" clip-rule="evenodd" /></svg>
                            <span>リセット</span>
                        </button>
                    </div>
                </div>
                
                <div id="results-area" class="main-view hidden bg-gray-800 p-8 rounded-2xl border border-gray-700 flex flex-col items-center justify-center h-full">
                     <h2 class="text-3xl font-bold text-white mb-2">テスト完了!</h2>
                     <p class="text-gray-400 mb-8">結果はこちらです:</p>
                     <div class="grid grid-cols-2 md:grid-cols-4 gap-4 mb-8 w-full max-w-2xl">
                        <div class="bg-gray-900/50 p-4 rounded-xl text-center"><p class="text-sm text-sky-400">KPM</p><p id="results-kpm" class="text-4xl font-bold text-white">0</p></div>
                        <div class="bg-gray-900/50 p-4 rounded-xl text-center"><p class="text-sm text-sky-400">正確率</p><p id="results-accuracy" class="text-4xl font-bold text-white">0%</p></div>
                        <div class="bg-gray-900/50 p-4 rounded-xl text-center"><p class="text-sm text-sky-400">キー入力</p><p id="results-chars" class="text-4xl font-bold text-white">0/0</p></div>
                        <div class="bg-gray-900/50 p-4 rounded-xl text-center"><p class="text-sm text-sky-400">ミス</p><p id="results-errors" class="text-4xl font-bold text-white">0</p></div>
                     </div>
                     <button id="try-again-btn" class="bg-sky-500 hover:bg-sky-600 text-white font-bold py-3 px-10 rounded-lg transition-colors text-lg">
                        もう一度
                    </button>
                </div>

                <div id="practice-area" class="main-view hidden bg-gray-800 p-8 rounded-2xl border border-gray-700 flex flex-col h-full">
                    <h2 class="text-3xl font-bold text-white mb-6 text-center">練習レッスン</h2>
                    <div id="practice-lessons-grid" class="grid grid-cols-1 md:grid-cols-2 gap-6 flex-grow overflow-y-auto pr-2">
                        <!-- Practice lessons will be generated here -->
                    </div>
                </div>

                <div id="leaderboard-area" class="main-view hidden bg-gray-800 p-8 rounded-2xl border border-gray-700 flex flex-col h-full">
                    <h2 class="text-3xl font-bold text-white mb-6 text-center">トップ10スコア</h2>
                    <div class="flex-grow overflow-y-auto">
                        <ol id="leaderboard-list" class="space-y-3 pr-2"></ol>
                    </div>
                </div>

            </main>
        </div>
    </div>

    <script>
    document.addEventListener('DOMContentLoaded', () => {
        // --- DOM Elements ---
        const mainViews = document.querySelectorAll('.main-view');
        const sidebarLinks = document.querySelectorAll('.sidebar-link');
        
        const testSetupEl = document.getElementById('test-setup');
        const testAreaEl = document.getElementById('test-area');
        const resultsAreaEl = document.getElementById('results-area');
        const practiceAreaEl = document.getElementById('practice-area');
        const leaderboardAreaEl = document.getElementById('leaderboard-area');

        const typingTestLink = document.getElementById('typing-test-link');
        const practiceLink = document.getElementById('practice-link');
        const leaderboardLink = document.getElementById('leaderboard-link');

        const startBtn = document.getElementById('start-btn');
        const timeSelect = document.getElementById('time-select');
        const timerEl = document.getElementById('timer');
        const kpmEl = document.getElementById('kpm');
        const accuracyEl = document.getElementById('accuracy');
        const errorsEl = document.getElementById('errors');
        const textContainer = document.getElementById('text-container');
        const japaneseTextDisplayEl = document.getElementById('japanese-text-display');
        const textDisplayEl = document.getElementById('text-display');
        const textInput = document.getElementById('text-input');
        const resetBtn = document.getElementById('reset-btn');
        const tryAgainBtn = document.getElementById('try-again-btn');

        const resultsKpm = document.getElementById('results-kpm');
        const resultsAccuracy = document.getElementById('results-accuracy');
        const resultsChars = document.getElementById('results-chars');
        const resultsErrors = document.getElementById('results-errors');
        
        const leaderboardList = document.getElementById('leaderboard-list');
        const practiceLessonsGrid = document.getElementById('practice-lessons-grid');

        // --- Data ---
        const textPassages = [
            { japanese: "猿も木から落ちる", romaji: "sarumokikaraochiru" },
            { japanese: "花より団子", romaji: "hanayoridango" },
            { japanese: "今日の風はとても気持ちがいいです", romaji: "kyounokazehatotemokimochigaiidesu" },
            { japanese: "明日は晴れるといいですね", romaji: "ashitaharerutoiidesune" },
            { japanese: "継続は力なり", romaji: "keizokuhachikaranari" },
            { japanese: "猫はこたつで丸くなる", romaji: "neko hakotatsudemarukunaru" },
            { japanese: "初心忘るべからず", romaji: "shoshinwasurubekarazu" }
        ];
        
        const practiceLessons = [
            { title: 'あ行', description: '「あいうえお」の練習', japanese: 'あいうえお', romaji: 'aiueo' },
            { title: 'か行', description: '「かきくけこ」の練習', japanese: 'かきくけこ', romaji: 'kakikukeko' },
            { title: 'さ行', description: '「さしすせそ」の練習', japanese: 'さしすせそ', romaji: 'sashisuseso' },
            { title: 'た行', description: '「たちつてと」の練習', japanese: 'たちつてと', romaji: 'tachitsuteto' },
            { title: 'よく使う言葉', description: '基本的な挨拶の練習', japanese: 'こんにちは おはよう ありがとう', romaji: 'konnichiha ohayou arigatou' },
            { title: '拗音（ようおん）', description: '小さい「ゃゅょ」の練習', japanese: 'きゃく しゃしん ちゅうもん', romaji: 'kyaku shashin chuumon' }
        ];

        // --- State ---
        let state = {
            targetJapaneseText: '',
            targetRomajiText: '',
            timeLimit: 60,
            timeLeft: 60,
            timerInterval: null,
            testActive: false,
            charsTyped: 0,
            correctChars: 0,
            errors: 0,
            kpm: 0,
            accuracy: 100,
            currentMode: 'test', // 'test' or 'practice'
            currentPracticeLesson: null,
        };

        // --- Functions ---
        
        function showView(viewId) {
            mainViews.forEach(view => view.classList.add('hidden'));
            document.getElementById(viewId).classList.remove('hidden');

            let activeLink;
            if (['test-setup', 'test-area', 'results-area'].includes(viewId)) {
                activeLink = typingTestLink;
            } else if (viewId === 'practice-area') {
                activeLink = practiceLink;
                renderPracticeLessons();
            } else if (viewId === 'leaderboard-area') {
                activeLink = leaderboardLink;
                renderLeaderboard();
            }

            sidebarLinks.forEach(link => link.classList.remove('active'));
            if(activeLink) activeLink.classList.add('active');
        }

        function prepareTest(japaneseText, romajiText) {
            state.targetJapaneseText = japaneseText;
            state.targetRomajiText = romajiText;
            
            japaneseTextDisplayEl.textContent = state.targetJapaneseText;
            
            textDisplayEl.innerHTML = '';
            state.targetRomajiText.split('').forEach(char => {
                const charSpan = document.createElement('span');
                charSpan.textContent = char;
                textDisplayEl.appendChild(charSpan);
            });
            const cursorSpan = document.createElement('span');
            cursorSpan.className = 'cursor';
            textDisplayEl.insertBefore(cursorSpan, textDisplayEl.firstChild);
        }

        function resetState() {
            clearInterval(state.timerInterval);
            state.timeLimit = parseInt(timeSelect.value, 10);
            state.timeLeft = state.timeLimit;
            state.testActive = false;
            state.charsTyped = 0;
            state.correctChars = 0;
            state.errors = 0;
            state.kpm = 0;
            state.accuracy = 100;
            
            timerEl.textContent = state.timeLimit;
            kpmEl.textContent = '0';
            accuracyEl.textContent = '100%';
            errorsEl.textContent = '0';
            textInput.value = '';
            textInput.disabled = false;
            textContainer.classList.add('test-inactive');
            textContainer.classList.remove('test-active');
        }

        function startTest() {
            state.currentMode = 'test';
            state.currentPracticeLesson = null;
            resetState();
            const passage = textPassages[Math.floor(Math.random() * textPassages.length)];
            prepareTest(passage.japanese, passage.romaji);
            showView('test-area');
            textInput.focus();
        }
        
        function startPractice(lesson) {
            state.currentMode = 'practice';
            state.currentPracticeLesson = lesson;
            resetState();
            prepareTest(lesson.japanese, lesson.romaji);
            showView('test-area');
            textInput.focus();
        }

        function handleInput() {
            const typedText = textInput.value;
            const typedLength = typedText.length;

            if (!state.testActive && typedLength > 0) {
                state.testActive = true;
                textContainer.classList.remove('test-inactive');
                textContainer.classList.add('test-active');
                state.timerInterval = setInterval(updateTimer, 1000);
            }
            if (!state.testActive) return;

            state.charsTyped = typedLength;
            let currentErrors = 0;
            let currentCorrectChars = 0;
            const textSpans = textDisplayEl.querySelectorAll('span:not(.cursor)');
            
            for (let i = 0; i < state.targetRomajiText.length; i++) {
                const charSpan = textSpans[i];
                if (i < typedLength) {
                    if (typedText[i] === state.targetRomajiText[i]) {
                        charSpan.className = 'correct';
                        currentCorrectChars++;
                    } else {
                        charSpan.className = 'incorrect';
                        currentErrors++;
                    }
                } else {
                    charSpan.className = '';
                }
            }
            
            const cursor = textDisplayEl.querySelector('.cursor');
            const nextCharSpan = textSpans[typedLength];
            textDisplayEl.insertBefore(cursor, nextCharSpan || null);

            state.errors = currentErrors;
            state.correctChars = currentCorrectChars;

            const timeElapsed = state.timeLimit - state.timeLeft;
            if (timeElapsed > 0) {
                // KPM is Keys Per Minute
                state.kpm = Math.round((state.charsTyped / timeElapsed) * 60);
            }
            state.accuracy = state.charsTyped > 0 ? Math.round((state.correctChars / state.charsTyped) * 100) : 100;

            kpmEl.textContent = state.kpm;
            errorsEl.textContent = state.errors;
            accuracyEl.textContent = `${state.accuracy}%`;

            if (typedLength === state.targetRomajiText.length && state.errors === 0) {
                endTest();
            }
        }

        function updateTimer() {
            if (state.timeLeft > 0) {
                state.timeLeft--;
                timerEl.textContent = state.timeLeft;
            } else {
                endTest();
            }
        }

        function endTest() {
            clearInterval(state.timerInterval);
            state.testActive = false;
            textInput.disabled = true;
            textContainer.classList.add('test-inactive');
            textContainer.classList.remove('test-active');

            const timeElapsedInSeconds = state.timeLimit - state.timeLeft;
            const finalKpm = timeElapsedInSeconds > 0 ? Math.round((state.charsTyped / timeElapsedInSeconds) * 60) : 0;
            const finalAccuracy = state.charsTyped > 0 ? Math.round((state.correctChars / state.charsTyped) * 100) : 100;
            
            if (state.currentMode === 'test') {
                saveScore({ kpm: finalKpm, accuracy: finalAccuracy, date: new Date() });
            }

            resultsKpm.textContent = finalKpm;
            resultsAccuracy.textContent = `${finalAccuracy}%`;
            resultsChars.textContent = `${state.correctChars}/${state.charsTyped}`;
            resultsErrors.textContent = state.errors;

            showView('results-area');
        }
        
        function resetToSetup() {
            state.currentMode = 'test';
            resetState();
            showView('test-setup');
        }
        
        function handleReset() {
            if (state.currentMode === 'practice') {
                startPractice(state.currentPracticeLesson);
            } else {
                startTest();
            }
        }
        
        function handleTryAgain() {
            if (state.currentMode === 'practice') {
                showView('practice-area');
            } else {
                resetToSetup();
            }
        }

        // --- Leaderboard Functions ---
        function saveScore(newScore) {
            if (newScore.kpm === 0) return;
            const scores = JSON.parse(localStorage.getItem('typingScoresJP')) || [];
            scores.push(newScore);
            scores.sort((a, b) => b.kpm - a.kpm);
            const top10 = scores.slice(0, 10);
            localStorage.setItem('typingScoresJP', JSON.stringify(top10));
        }

        function renderLeaderboard() {
            const scores = JSON.parse(localStorage.getItem('typingScoresJP')) || [];
            leaderboardList.innerHTML = '';

            if (scores.length === 0) {
                leaderboardList.innerHTML = '<li class="text-center text-gray-400">まだスコアがありません。テストを完了して、ランキングに名前を載せましょう！</li>';
                return;
            }

            scores.forEach((score, index) => {
                const li = document.createElement('li');
                li.className = 'bg-gray-700/50 p-4 rounded-lg flex items-center justify-between gap-4';
                li.innerHTML = `
                    <div class="flex items-center gap-4">
                        <span class="text-xl font-bold text-gray-400 w-8 text-center">${index + 1}</span>
                        <div>
                            <p class="text-lg font-bold text-white">${score.kpm} KPM</p>
                            <p class="text-sm text-gray-400">正確率: ${score.accuracy}%</p>
                        </div>
                    </div>
                    <p class="text-sm text-gray-500">${new Date(score.date).toLocaleDateString()}</p>
                `;
                leaderboardList.appendChild(li);
            });
        }
        
        // --- Practice Lessons ---
        function renderPracticeLessons() {
            practiceLessonsGrid.innerHTML = '';
            practiceLessons.forEach(lesson => {
                const card = document.createElement('div');
                card.className = 'bg-gray-700/50 p-6 rounded-lg flex flex-col';
                card.innerHTML = `
                    <h3 class="text-xl font-bold text-white mb-2">${lesson.title}</h3>
                    <p class="text-gray-400 mb-4 flex-grow">${lesson.description}</p>
                    <button class="start-practice-btn mt-auto bg-sky-600 hover:bg-sky-700 text-white font-semibold py-2 px-4 rounded-lg transition-colors w-full">
                        開始
                    </button>
                `;
                card.querySelector('.start-practice-btn').addEventListener('click', () => startPractice(lesson));
                practiceLessonsGrid.appendChild(card);
            });
        }

        // --- Event Listeners ---
        startBtn.addEventListener('click', startTest);
        resetBtn.addEventListener('click', handleReset);
        tryAgainBtn.addEventListener('click', handleTryAgain);
        textInput.addEventListener('input', handleInput);
        
        textContainer.addEventListener('click', () => {
            if (state.testActive || (!state.testActive && state.timeLeft === state.timeLimit)) {
                 textInput.focus();
            }
        });

        typingTestLink.addEventListener('click', (e) => { e.preventDefault(); resetToSetup(); });
        practiceLink.addEventListener('click', (e) => { e.preventDefault(); showView('practice-area'); });
        leaderboardLink.addEventListener('click', (e) => { e.preventDefault(); showView('leaderboard-area'); });
        
        // --- Initial Setup ---
        showView('test-setup');
    });
    </script>
</body>
</html>
]]></content:encoded>
					
					<wfw:commentRss>https://dejitarumirai.com/archives/4221/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>周期表</title>
		<link>https://dejitarumirai.com/archives/4218</link>
					<comments>https://dejitarumirai.com/archives/4218#respond</comments>
		
		<dc:creator><![CDATA[deji]]></dc:creator>
		<pubDate>Sat, 05 Jul 2025 13:55:07 +0000</pubDate>
				<category><![CDATA[Webapps]]></category>
		<guid isPermaLink="false">https://dejitarumirai.com/?p=4218</guid>

					<description><![CDATA[元素周期表 元素周期表 元素を [&#8230;]]]></description>
										<content:encoded><![CDATA[<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>元素周期表</title>
    <link rel="preconnect" href="https://fonts.googleapis.com">
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
    <link href="https://fonts.googleapis.com/css2?family=Noto+Sans+JP:wght@300;400;500;700&display=swap" rel="stylesheet">
    <style>
        /* --- 基本スタイルとダークテーマ --- */
        :root {
            --bg-color: #121212;
            --primary-text-color: #E0E0E0;
            --secondary-text-color: #B0B0B0;
            --border-color: #333333;
            --element-bg-color: #1E1E1E;
            --element-hover-bg-color: #2a2a2a;
            --modal-bg-color: #252525;
            
            /* --- 元素分類の色 --- */
            --diatomic-nonmetal: #3a5e8c;
            --noble-gas: #7e3a8c;
            --alkali-metal: #8c3a3a;
            --alkaline-earth-metal: #8c623a;
            --metalloid: #3a8c8c;
            --halogen: #6a3a8c;
            --post-transition-metal: #3a8c5e;
            --transition-metal: #3a6a8c;
            --lanthanide: #8c3a75;
            --actinide: #8c3a5e;
            --unknown: #444444;
        }

        * {
            box-sizing: border-box;
            margin: 0;
            padding: 0;
        }

        body {
            font-family: 'Noto Sans JP', sans-serif;
            background-color: var(--bg-color);
            color: var(--primary-text-color);
            padding: 1rem;
            line-height: 1.6;
        }

        h1 {
            text-align: center;
            font-size: 2.5rem;
            margin-bottom: 0.5rem;
            font-weight: 700;
        }
        
        .subtitle {
            text-align: center;
            font-size: 1rem;
            color: var(--secondary-text-color);
            margin-bottom: 2rem;
        }

        /* --- 周期表グリッド --- */
        #periodic-table-container {
            display: flex;
            justify-content: center;
            align-items: center;
            width: 100%;
            overflow-x: auto; /* 小さな画面で水平スクロールを許可 */
        }

        #periodic-table {
            display: grid;
            grid-template-columns: repeat(18, minmax(60px, 1fr));
            grid-template-rows: repeat(10, minmax(60px, 1fr));
            gap: 5px;
            max-width: 1200px;
            margin: 0 auto;
        }

        /* --- 元素タイルのスタイル --- */
        .element {
            background-color: var(--element-bg-color);
            border: 1px solid var(--border-color);
            border-radius: 5px;
            cursor: pointer;
            transition: all 0.2s ease-in-out;
            padding: 4px;
            display: flex;
            flex-direction: column;
            justify-content: center;
            align-items: center;
            text-align: center;
            color: var(--primary-text-color);
        }

        .element:hover {
            transform: scale(1.1);
            background-color: var(--element-hover-bg-color);
            border-color: var(--primary-text-color);
            z-index: 10;
            position: relative;
        }

        .element .atomic-number {
            font-size: 0.6rem;
            align-self: flex-start;
            padding-left: 2px;
            color: var(--secondary-text-color);
        }

        .element .symbol {
            font-size: 1.2rem;
            font-weight: 700;
        }

        .element .name {
            font-size: 0.6rem;
            word-break: break-all;
        }
        
        .element .atomic-mass {
            font-size: 0.5rem;
            color: var(--secondary-text-color);
        }

        /* --- 元素分類の色 --- */
        .diatomic.nonmetal { background-color: var(--diatomic-nonmetal); }
        .noble.gas { background-color: var(--noble-gas); }
        .alkali.metal { background-color: var(--alkali-metal); }
        .alkaline.earth.metal { background-color: var(--alkaline-earth-metal); }
        .metalloid { background-color: var(--metalloid); }
        .halogen { background-color: var(--halogen); }
        .post-transition.metal { background-color: var(--post-transition-metal); }
        .transition.metal { background-color: var(--transition-metal); }
        .lanthanide { background-color: var(--lanthanide); }
        .actinide { background-color: var(--actinide); }
        .unknown { background-color: var(--unknown); }
        
        /* --- 凡例のスタイル --- */
        #legend {
            display: flex;
            flex-wrap: wrap;
            justify-content: center;
            gap: 15px;
            margin: 2rem auto;
            max-width: 1000px;
        }
        .legend-item {
            display: flex;
            align-items: center;
            gap: 8px;
        }
        .legend-color {
            width: 20px;
            height: 20px;
            border-radius: 4px;
            border: 1px solid var(--border-color);
        }
        .legend-item span {
            font-size: 0.9rem;
        }

        /* --- モーダルのスタイル --- */
        #element-modal {
            display: none; /* デフォルトで非表示 */
            position: fixed;
            z-index: 100;
            left: 0;
            top: 0;
            width: 100%;
            height: 100%;
            overflow: auto;
            background-color: rgba(0, 0, 0, 0.7);
            align-items: center;
            justify-content: center;
        }

        .modal-content {
            background-color: var(--modal-bg-color);
            margin: auto;
            padding: 30px;
            border: 1px solid var(--border-color);
            width: 90%;
            max-width: 500px;
            border-radius: 10px;
            position: relative;
            box-shadow: 0 5px 25px rgba(0,0,0,0.5);
        }

        .modal-close {
            color: var(--secondary-text-color);
            position: absolute;
            top: 10px;
            right: 20px;
            font-size: 28px;
            font-weight: bold;
            cursor: pointer;
            transition: color 0.2s;
        }

        .modal-close:hover {
            color: var(--primary-text-color);
        }
        
        #modal-title {
            margin-top: 0;
            margin-bottom: 1rem;
            font-size: 2rem;
            font-weight: 700;
        }
        
        #modal-summary {
            margin-top: 1rem;
            font-size: 0.95rem;
            color: var(--secondary-text-color);
            min-height: 50px; /* 翻訳中のスペースを確保 */
        }
        
        .modal-details {
            list-style: none;
            padding: 0;
            margin-top: 1.5rem;
        }
        
        .modal-details li {
            padding: 5px 0;
            border-bottom: 1px solid var(--border-color);
        }
        
        .modal-details li:last-child {
            border-bottom: none;
        }
        
        .modal-details strong {
            color: var(--primary-text-color);
        }

        /* --- レスポンシブデザイン --- */
        @media (max-width: 768px) {
            h1 {
                font-size: 2rem;
            }
            #periodic-table {
                gap: 3px;
                grid-template-columns: repeat(18, 50px);
                grid-template-rows: repeat(10, 50px);
            }
            .element .symbol { font-size: 1rem; }
            .element .name, .element .atomic-mass { display: none; }
        }
        
        @media (max-width: 480px) {
            body { padding: 0.5rem; }
            h1 { font-size: 1.5rem; }
            .subtitle { font-size: 0.9rem; margin-bottom: 1rem; }
            #periodic-table {
                grid-template-columns: repeat(18, 40px);
                grid-template-rows: repeat(10, 40px);
            }
             .element .atomic-number { font-size: 0.5rem; }
             .element .symbol { font-size: 0.8rem; }
        }
    </style>
</head>
<body>

    <h1>元素周期表</h1>
    <p class="subtitle">元素をクリックすると詳細が表示されます。</p>

    <div id="periodic-table-container">
        <div id="periodic-table">
            <!-- JavaScriptによって元素が動的にここに挿入されます -->
        </div>
    </div>
    
    <div id="legend">
        <!-- JavaScriptによって凡例が動的にここに挿入されます -->
    </div>

    <!-- モーダル -->
    <div id="element-modal">
        <div class="modal-content">
            <span class="modal-close">&times;</span>
            <h2 id="modal-title">元素名</h2>
            <ul class="modal-details">
                <li id="modal-number"></li>
                <li id="modal-symbol"></li>
                <li id="modal-mass"></li>
                <li id="modal-category"></li>
                <li id="modal-discovered-by"></li>
                <li id="modal-phase"></li>
            </ul>
            <p id="modal-summary"></p>
        </div>
    </div>

    <script>
        document.addEventListener('DOMContentLoaded', function() {
            // --- データ: 元素データを取得して処理 ---
            const elementsDataUrl = 'https://raw.githubusercontent.com/Bowserinator/Periodic-Table-JSON/master/PeriodicTableJSON.json';
            
            // --- DOM要素の参照 ---
            const table = document.getElementById('periodic-table');
            const modal = document.getElementById('element-modal');
            const closeModal = document.querySelector('.modal-close');
            const legendContainer = document.getElementById('legend');

            // --- 翻訳マップ (凡例とモーダルで使用) ---
            const categoryNameMap = {
                'diatomic nonmetal': '二原子非金属',
                'noble gas': '貴ガス',
                'alkali metal': 'アルカリ金属',
                'alkaline earth metal': 'アルカリ土類金属',
                'metalloid': '半金属',
                'halogen': 'ハロゲン',
                'post-transition metal': '後遷移金属',
                'transition metal': '遷移金属',
                'lanthanide': 'ランタノイド',
                'actinide': 'アクチノイド',
                'unknown, probably transition metal': '不明',
                'unknown, probably post-transition metal': '不明',
                'unknown, probably metalloid': '不明'
            };

            const phaseNameMap = {
                'Gas': '気体',
                'Liquid': '液体',
                'Solid': '固体'
            };

            // --- データを取得して表を作成するメイン関数 ---
            async function initializePeriodicTable() {
                try {
                    const response = await fetch(elementsDataUrl);
                    if (!response.ok) {
                        throw new Error(`HTTPエラー！ステータス: ${response.status}`);
                    }
                    const data = await response.json();
                    buildTable(data.elements);
                    buildLegend(data.elements);
                } catch (error) {
                    console.error("周期表データの取得または処理ができませんでした:", error);
                    table.innerHTML = '<p style="color: red; grid-column: 1 / -1; text-align: center;">元素データの読み込みに失敗しました。後でもう一度お試しください。</p>';
                }
            }

            // --- 表のグリッドを構築する関数 ---
            function buildTable(elements) {
                elements.forEach(element => {
                    const elementTile = document.createElement('div');
                    elementTile.className = 'element';
                    
                    const categoryClass = element.category.replace(/\s+/g, '.').toLowerCase();
                    elementTile.classList.add(...categoryClass.split('.'));

                    elementTile.style.gridColumn = element.xpos;
                    elementTile.style.gridRow = element.ypos;
                    
                    elementTile.innerHTML = `
                        <div class="atomic-number">${element.number}</div>
                        <div class="symbol">${element.symbol}</div>
                        <div class="name">${element.name}</div>
                        <div class="atomic-mass">${(element.atomic_mass || 0).toFixed(3)}</div>
                    `;
                    
                    elementTile.addEventListener('click', () => showModal(element));
                    
                    table.appendChild(elementTile);
                });
            }

            // --- 色の凡例を構築する関数 ---
            function buildLegend(elements) {
                const categories = {};
                elements.forEach(el => {
                    if (!categories[el.category]) {
                        categories[el.category] = el.category.replace(/\s+/g, '.').toLowerCase();
                    }
                });

                const legendOrder = Object.keys(categoryNameMap);
                const uniqueJapaneseCategories = [...new Set(legendOrder.map(cat => categoryNameMap[cat]))];

                uniqueJapaneseCategories.forEach(jpName => {
                    const originalCategory = Object.keys(categoryNameMap).find(key => categoryNameMap[key] === jpName);
                     if (!originalCategory || !categories[originalCategory]) return;
                    
                    const categoryClass = categories[originalCategory];

                    const legendItem = document.createElement('div');
                    legendItem.className = 'legend-item';
                    
                    const colorBox = document.createElement('div');
                    colorBox.className = 'legend-color';
                    
                    const primaryClass = categoryClass.split('.')[0];
                    const secondaryClass = categoryClass.split('.')[1] || '';
                    colorBox.style.backgroundColor = `var(--${primaryClass}-${secondaryClass}`.replace(/--$/, '');
                    if (jpName === '不明') colorBox.style.backgroundColor = 'var(--unknown)';

                    const text = document.createElement('span');
                    text.textContent = jpName;
                    
                    legendItem.appendChild(colorBox);
                    legendItem.appendChild(text);
                    legendContainer.appendChild(legendItem);
                });
            }

            // --- テキスト翻訳関数 (Gemini API) ---
            async function translateText(text, targetLang = 'ja') {
                if (!text) return '';
                const prompt = `Translate the following English text to ${targetLang}: "${text}"`;
                const chatHistory = [{ role: "user", parts: [{ text: prompt }] }];
                const payload = { contents: chatHistory };
                const apiKey = ""; // APIキーは空のままにします
                const apiUrl = `https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent?key=${apiKey}`;

                try {
                    const response = await fetch(apiUrl, {
                        method: 'POST',
                        headers: { 'Content-Type': 'application/json' },
                        body: JSON.stringify(payload)
                    });
                     if (!response.ok) {
                        throw new Error(`API error: ${response.status}`);
                    }
                    const result = await response.json();
                    if (result.candidates && result.candidates[0].content.parts[0].text) {
                        return result.candidates[0].content.parts[0].text;
                    }
                } catch (error) {
                    console.error('Translation API error:', error);
                }
                return text; // 翻訳失敗時は元のテキストを返す
            }

            // --- モーダル関数 ---
            async function showModal(element) {
                // 静的コンテンツを先に設定
                const translatedCategory = categoryNameMap[element.category] || element.category;
                const translatedPhase = phaseNameMap[element.phase] || '不明';
                
                document.getElementById('modal-number').innerHTML = `<strong>原子番号:</strong> ${element.number}`;
                document.getElementById('modal-symbol').innerHTML = `<strong>記号:</strong> ${element.symbol}`;
                document.getElementById('modal-mass').innerHTML = `<strong>原子量:</strong> ${element.atomic_mass}`;
                document.getElementById('modal-category').innerHTML = `<strong>分類:</strong> ${translatedCategory}`;
                document.getElementById('modal-phase').innerHTML = `<strong>標準状態での相:</strong> ${translatedPhase}`;

                // 翻訳が必要なフィールドにプレースホルダーを設定
                const titleEl = document.getElementById('modal-title');
                const summaryEl = document.getElementById('modal-summary');
                const discoveredByEl = document.getElementById('modal-discovered-by');

                titleEl.textContent = '翻訳中...';
                summaryEl.textContent = '翻訳中...';
                discoveredByEl.innerHTML = `<strong>発見者:</strong> 翻訳中...`;

                modal.style.display = 'flex';

                // APIで翻訳を並行して実行
                try {
                    const [translatedName, translatedSummary, translatedDiscoverer] = await Promise.all([
                        translateText(element.name),
                        translateText(element.summary),
                        translateText(element.discovered_by || '不明')
                    ]);

                    // 翻訳結果でUIを更新
                    titleEl.textContent = `${translatedName} (${element.symbol})`;
                    summaryEl.textContent = translatedSummary;
                    discoveredByEl.innerHTML = `<strong>発見者:</strong> ${translatedDiscoverer}`;
                } catch (error) {
                    console.error("Failed to translate element details:", error);
                    // エラー発生時は英語のまま表示
                    titleEl.textContent = `${element.name} (${element.symbol})`;
                    summaryEl.textContent = element.summary;
                    discoveredByEl.innerHTML = `<strong>発見者:</strong> ${element.discovered_by || '不明'}`;
                }
            }

            closeModal.onclick = () => {
                modal.style.display = 'none';
            }

            window.onclick = (event) => {
                if (event.target == modal) {
                    modal.style.display = 'none';
                }
            }

            // --- 初期化 ---
            initializePeriodicTable();
        });
    </script>

</body>
</html>
]]></content:encoded>
					
					<wfw:commentRss>https://dejitarumirai.com/archives/4218/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>パーセント計算</title>
		<link>https://dejitarumirai.com/archives/4215</link>
					<comments>https://dejitarumirai.com/archives/4215#respond</comments>
		
		<dc:creator><![CDATA[deji]]></dc:creator>
		<pubDate>Sat, 05 Jul 2025 13:42:54 +0000</pubDate>
				<category><![CDATA[Webapps]]></category>
		<guid isPermaLink="false">https://dejitarumirai.com/?p=4215</guid>

					<description><![CDATA[パーセント計算機 パーセント計 [&#8230;]]]></description>
										<content:encoded><![CDATA[<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>パーセント計算機</title>
    
    <!-- Tailwind CSS for styling -->
    <script src="https://cdn.tailwindcss.com"></script>
    
    <!-- Google Fonts: Inter and Noto Sans JP for Japanese characters -->
    <link rel="preconnect" href="https://fonts.googleapis.com">
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
    <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Noto+Sans+JP:wght@400;500;700&display=swap" rel="stylesheet">

    <style>
        /* Custom styles to complement Tailwind */
        body {
            font-family: 'Inter', 'Noto Sans JP', sans-serif;
        }
        /* Style for input number spinners */
        input[type=number]::-webkit-inner-spin-button, 
        input[type=number]::-webkit-outer-spin-button { 
          -webkit-appearance: none; 
          margin: 0; 
        }
        input[type=number] {
          -moz-appearance: textfield;
        }
        .result-field {
            background-color: #1f2937; /* gray-800 */
            color: #d1d5db; /* gray-300 */
            font-weight: bold;
        }
    </style>
</head>
<body class="bg-gray-900 text-gray-200">

    <div class="container mx-auto p-4 md:p-8 max-w-2xl">

        <!-- Header -->
        <header class="text-center mb-8 md:mb-12">
            <h1 class="text-4xl md:text-5xl font-bold text-white">パーセント計算機</h1>
            <p class="text-gray-400 mt-2">生活やビジネスで役立つ様々なパーセント計算ツール</p>
        </header>

        <!-- Calculators -->
        <div class="space-y-6">

            <!-- Calculator 1: Y% of X -->
            <div class="bg-gray-800 p-6 rounded-xl shadow-lg">
                <form id="form-q1" class="flex flex-wrap items-center gap-4 text-lg">
                    <input type="number" id="q1-b" class="flex-grow p-3 rounded-md bg-gray-700 border border-gray-600 focus:ring-2 focus:ring-indigo-500 outline-none transition" placeholder="値">
                    <span class="text-gray-400 font-medium">の</span>
                    <input type="number" id="q1-a" class="w-28 p-3 rounded-md bg-gray-700 border border-gray-600 focus:ring-2 focus:ring-indigo-500 outline-none transition" placeholder="パーセント">
                    <span class="text-gray-400 font-medium">% は</span>
                    <input type="text" id="q1-c" class="flex-grow p-3 rounded-md border border-gray-600 outline-none transition result-field" placeholder="結果" readonly>
                </form>
            </div>

            <!-- Calculator 2: X is what % of Y -->
            <div class="bg-gray-800 p-6 rounded-xl shadow-lg">
                <form id="form-q2" class="flex flex-wrap items-center gap-4 text-lg">
                    <input type="number" id="q2-a" class="flex-grow p-3 rounded-md bg-gray-700 border border-gray-600 focus:ring-2 focus:ring-indigo-500 outline-none transition" placeholder="値">
                    <span class="text-gray-400 font-medium">は</span>
                    <input type="number" id="q2-b" class="flex-grow p-3 rounded-md bg-gray-700 border border-gray-600 focus:ring-2 focus:ring-indigo-500 outline-none transition" placeholder="全体の価値">
                    <span class="text-gray-400 font-medium">の</span>
                    <input type="text" id="q2-c" class="w-28 p-3 rounded-md border border-gray-600 outline-none transition result-field" placeholder="結果" readonly>
                    <span class="text-gray-400 font-medium">%？</span>
                </form>
            </div>

            <!-- Calculator 3: If X is Y%, what is the total -->
            <div class="bg-gray-800 p-6 rounded-xl shadow-lg">
                <form id="form-q3" class="flex flex-wrap items-center gap-4 text-lg">
                    <input type="number" id="q3-a" class="flex-grow p-3 rounded-md bg-gray-700 border border-gray-600 focus:ring-2 focus:ring-indigo-500 outline-none transition" placeholder="値">
                    <span class="text-gray-400 font-medium">が</span>
                    <input type="number" id="q3-b" class="w-28 p-3 rounded-md bg-gray-700 border border-gray-600 focus:ring-2 focus:ring-indigo-500 outline-none transition" placeholder="パーセント">
                    <span class="text-gray-400 font-medium">% の場合、全体は？</span>
                    <input type="text" id="q3-c" class="flex-grow p-3 rounded-md border border-gray-600 outline-none transition result-field" placeholder="結果" readonly>
                </form>
            </div>
            
            <!-- Calculator 4: Percentage change from X to Y -->
            <div class="bg-gray-800 p-6 rounded-xl shadow-lg">
                 <form id="form-q6" class="flex flex-wrap items-center gap-4 text-lg">
                    <input type="number" id="q6-a" class="flex-grow p-3 rounded-md bg-gray-700 border border-gray-600 focus:ring-2 focus:ring-indigo-500 outline-none transition" placeholder="元の値">
                    <span class="text-gray-400 font-medium">から</span>
                    <input type="number" id="q6-b" class="flex-grow p-3 rounded-md bg-gray-700 border border-gray-600 focus:ring-2 focus:ring-indigo-500 outline-none transition" placeholder="新しい値">
                    <span class="text-gray-400 font-medium">への増減率は？</span>
                    <input type="text" id="q6-c" class="w-32 p-3 rounded-md border border-gray-600 outline-none transition result-field" placeholder="結果 (%)" readonly>
                </form>
            </div>

            <!-- Calculator 5: Increase X by Y% -->
            <div class="bg-gray-800 p-6 rounded-xl shadow-lg">
                <form id="form-q7" class="flex flex-wrap items-center gap-4 text-lg">
                    <input type="number" id="q7-a" class="flex-grow p-3 rounded-md bg-gray-700 border border-gray-600 focus:ring-2 focus:ring-indigo-500 outline-none transition" placeholder="値">
                    <span class="text-gray-400 font-medium">の</span>
                    <input type="number" id="q7-b" class="w-28 p-3 rounded-md bg-gray-700 border border-gray-600 focus:ring-2 focus:ring-indigo-500 outline-none transition" placeholder="パーセント">
                    <span class="text-gray-400 font-medium">% 増しは？</span>
                    <input type="text" id="q7-c" class="flex-grow p-3 rounded-md border border-gray-600 outline-none transition result-field" placeholder="結果" readonly>
                </form>
            </div>

            <!-- Calculator 6: Decrease X by Y% -->
            <div class="bg-gray-800 p-6 rounded-xl shadow-lg">
                <form id="form-q8" class="flex flex-wrap items-center gap-4 text-lg">
                    <input type="number" id="q8-a" class="flex-grow p-3 rounded-md bg-gray-700 border border-gray-600 focus:ring-2 focus:ring-indigo-500 outline-none transition" placeholder="値">
                    <span class="text-gray-400 font-medium">の</span>
                    <input type="number" id="q8-b" class="w-28 p-3 rounded-md bg-gray-700 border border-gray-600 focus:ring-2 focus:ring-indigo-500 outline-none transition" placeholder="パーセント">
                    <span class="text-gray-400 font-medium">% 引きは？</span>
                    <input type="text" id="q8-c" class="flex-grow p-3 rounded-md border border-gray-600 outline-none transition result-field" placeholder="結果" readonly>
                </form>
            </div>
            
            <!-- Clear Button -->
            <div class="text-center pt-4">
                <button type="button" id="clear-all" class="bg-gray-600 hover:bg-gray-700 text-white font-bold py-2 px-8 rounded-lg transition">すべてのデータを消去</button>
            </div>

        </div>

        <!-- Explanations Section -->
        <div class="mt-12 md:mt-16 bg-gray-800 p-6 md:p-8 rounded-xl shadow-lg">
            <h2 class="text-3xl font-bold text-white mb-6">パーセントについて</h2>
            <div class="prose prose-invert max-w-none text-gray-300 space-y-4">
                <p>私たちは、人生、ビジネス、投資でパーセンテージ計算をよく使用します。このページでは、比率計算、割引計算、株価変動率、税込み計算、税抜き計算などに使用できるパーセンテージ計算機をいくつか示します。これらの数学の問題は説明的な質問になり、フィールドに数字を入力するだけですぐに答えられます。</p>
            </div>
        </div>

    </div>

    <script>
        document.addEventListener('DOMContentLoaded', () => {

            // Helper function to parse float from input value
            const getFloat = (id) => {
                const value = document.getElementById(id).value;
                return value ? parseFloat(value) : null;
            };

            // Helper function to format the output
            const formatOutput = (value) => {
                if (!isFinite(value)) return '';
                if (Math.abs(value) < 1e-9 && value !== 0) return value.toExponential(4);
                return parseFloat(value.toFixed(10));
            };

            // Function to set up a calculator
            const setupCalculator = (formId, inputIds, calculation) => {
                const form = document.getElementById(formId);
                const inputs = inputIds.map(id => document.getElementById(id));
                
                const calculate = () => {
                    const values = inputIds.map(id => getFloat(id));
                    calculation(values);
                };

                inputs.forEach(input => {
                    input.addEventListener('input', calculate);
                });
                
                form.addEventListener('submit', (e) => e.preventDefault());
            };

            // --- Calculator Setups ---

            // Q1: Y% of X
            setupCalculator('form-q1', ['q1-a', 'q1-b'], (values) => {
                const [a, b] = values;
                const resultField = document.getElementById('q1-c');
                if (a !== null && b !== null) {
                    resultField.value = formatOutput((b * a) / 100);
                } else {
                    resultField.value = '';
                }
            });

            // Q2: X is what % of Y
            setupCalculator('form-q2', ['q2-a', 'q2-b'], (values) => {
                const [a, b] = values;
                const resultField = document.getElementById('q2-c');
                if (a !== null && b !== null && b !== 0) {
                    resultField.value = formatOutput((a / b) * 100);
                } else {
                    resultField.value = '';
                }
            });

            // Q3: If X is Y%, what is the total
            setupCalculator('form-q3', ['q3-a', 'q3-b'], (values) => {
                const [a, b] = values;
                const resultField = document.getElementById('q3-c');
                if (a !== null && b !== null && b !== 0) {
                    resultField.value = formatOutput(a / (b / 100));
                } else {
                    resultField.value = '';
                }
            });
            
            // Q6: Percentage change from X to Y
            setupCalculator('form-q6', ['q6-a', 'q6-b'], (values) => {
                const [a, b] = values;
                const resultField = document.getElementById('q6-c');
                if (a !== null && b !== null && a !== 0) {
                    const result = ((b / a) - 1) * 100;
                    resultField.value = formatOutput(result) + '%';
                } else {
                    resultField.value = '';
                }
            });

            // Q7: Increase X by Y%
            setupCalculator('form-q7', ['q7-a', 'q7-b'], (values) => {
                const [a, b] = values;
                const resultField = document.getElementById('q7-c');
                if (a !== null && b !== null) {
                    resultField.value = formatOutput(a * (1 + b / 100));
                } else {
                    resultField.value = '';
                }
            });

            // Q8: Decrease X by Y%
            setupCalculator('form-q8', ['q8-a', 'q8-b'], (values) => {
                const [a, b] = values;
                const resultField = document.getElementById('q8-c');
                if (a !== null && b !== null) {
                    resultField.value = formatOutput(a * (1 - b / 100));
                } else {
                    resultField.value = '';
                }
            });

            // --- Clear All Button ---
            document.getElementById('clear-all').addEventListener('click', () => {
                const forms = document.querySelectorAll('form');
                forms.forEach(form => form.reset());
                const resultFields = document.querySelectorAll('.result-field');
                resultFields.forEach(field => field.value = '');
            });
        });
    </script>
</body>
</html>
]]></content:encoded>
					
					<wfw:commentRss>https://dejitarumirai.com/archives/4215/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>日割り計算くん</title>
		<link>https://dejitarumirai.com/archives/4212</link>
					<comments>https://dejitarumirai.com/archives/4212#respond</comments>
		
		<dc:creator><![CDATA[deji]]></dc:creator>
		<pubDate>Sat, 05 Jul 2025 13:32:48 +0000</pubDate>
				<category><![CDATA[Webapps]]></category>
		<guid isPermaLink="false">https://dejitarumirai.com/?p=4212</guid>

					<description><![CDATA[日割り計算ツール 日割計算くん [&#8230;]]]></description>
										<content:encoded><![CDATA[<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>日割り計算ツール</title>
    <!-- Tailwind CSS for styling -->
    <script src="https://cdn.tailwindcss.com"></script>
    <!-- Google Fonts: Noto Sans JP -->
    <link rel="preconnect" href="https://fonts.googleapis.com">
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
    <link href="https://fonts.googleapis.com/css2?family=Noto+Sans+JP:wght@400;500;700&display=swap" rel="stylesheet">
    <style>
        /* Custom styles for a better dark mode experience */
        body {
            font-family: 'Noto Sans JP', sans-serif;
            background-color: #111827; /* bg-gray-900 */
            color: #d1d5db; /* text-gray-300 */
        }
        /* Custom focus ring color */
        .form-input:focus, .form-select:focus {
            outline: none;
            border-color: #3b82f6; /* border-blue-500 */
            box-shadow: 0 0 0 2px #1e40af; /* ring-blue-500/50 */
        }
        .form-input, .form-select {
            background-color: #1f2937; /* bg-gray-800 */
            border-color: #4b5563; /* border-gray-600 */
            -webkit-appearance: none;
            -moz-appearance: none;
            appearance: none;
        }
        .form-select {
             background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%239ca3af' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e");
            background-position: right 0.5rem center;
            background-repeat: no-repeat;
            background-size: 1.5em 1.5em;
            padding-right: 2.5rem;
        }
        .result-card {
            background: linear-gradient(145deg, #1f2937, #2c3a4f);
            border: 1px solid #4b5563; /* border-gray-600 */
        }
    </style>
</head>
<body class="antialiased">

    <div class="max-w-4xl mx-auto p-4 sm:p-6 lg:p-8">
        
        <!-- Calculator Section -->
        <div class="bg-gray-800 rounded-2xl shadow-2xl p-6 md:p-8">
            <div class="text-center mb-8">
                <h1 class="text-3xl md:text-4xl font-bold text-white">日割計算くん</h1>
                <p class="text-gray-400 mt-2">給与や家賃など、面倒な日割り計算を簡単に行います。</p>
            </div>

            <div class="grid grid-cols-1 md:grid-cols-2 gap-8">
                
                <!-- Input Form -->
                <div class="space-y-6">
                    <div>
                        <label for="total-amount" class="block text-sm font-medium text-gray-300 mb-1">合計金額（月額など）</label>
                        <div class="relative">
                            <input type="number" id="total-amount" class="form-input w-full rounded-md shadow-sm text-white pl-4 pr-8" placeholder="例: 150000">
                             <span class="absolute inset-y-0 right-0 pr-3 flex items-center text-gray-400">円</span>
                        </div>
                    </div>

                    <!-- Start Date -->
                    <fieldset class="border border-gray-600 rounded-lg p-4">
                        <legend class="text-base font-medium text-blue-300 px-2">開始日</legend>
                        <div class="grid grid-cols-3 gap-3 mt-2">
                            <div>
                                <label for="start-year" class="sr-only">年</label>
                                <select id="start-year" class="form-select w-full rounded-md shadow-sm text-white"></select>
                            </div>
                            <div>
                                <label for="start-month" class="sr-only">月</label>
                                <select id="start-month" class="form-select w-full rounded-md shadow-sm text-white"></select>
                            </div>
                            <div>
                                <label for="start-day" class="sr-only">日</label>
                                <select id="start-day" class="form-select w-full rounded-md shadow-sm text-white"></select>
                            </div>
                        </div>
                    </fieldset>

                    <!-- End Date -->
                     <fieldset class="border border-gray-600 rounded-lg p-4">
                        <legend class="text-base font-medium text-blue-300 px-2">終了日</legend>
                        <div class="grid grid-cols-3 gap-3 mt-2">
                             <div>
                                <label for="end-year" class="sr-only">年</label>
                                <select id="end-year" class="form-select w-full rounded-md shadow-sm text-white"></select>
                            </div>
                            <div>
                                <label for="end-month" class="sr-only">月</label>
                                <select id="end-month" class="form-select w-full rounded-md shadow-sm text-white"></select>
                            </div>
                            <div>
                                <label for="end-day" class="sr-only">日</label>
                                <select id="end-day" class="form-select w-full rounded-md shadow-sm text-white"></select>
                            </div>
                        </div>
                    </fieldset>
                    
                    <button id="calculate-btn" class="w-full bg-blue-600 hover:bg-blue-700 text-white font-bold py-3 px-4 rounded-lg shadow-lg transition duration-300 ease-in-out transform hover:scale-105">
                        計算する
                    </button>
                </div>

                <!-- Results Display -->
                <div class="result-card rounded-2xl p-6 flex flex-col justify-center">
                    <h2 class="text-xl font-bold text-white mb-6 text-center">計算結果</h2>
                    <div id="results-summary" class="space-y-4 text-base">
                        <div class="flex justify-between items-center">
                            <span class="text-gray-400">計算の基となる月の日数</span>
                            <span id="days-in-month-result" class="font-semibold text-white">- 日</span>
                        </div>
                        <div class="flex justify-between items-center">
                            <span class="text-gray-400">対象となる日数</span>
                            <span id="prorated-days-result" class="font-semibold text-white">- 日</span>
                        </div>
                        <div class="flex justify-between items-center">
                            <span class="text-gray-400">1日あたりの金額</span>
                            <span id="daily-rate-result" class="font-semibold text-white">¥0</span>
                        </div>
                    </div>
                    <div class="mt-8 pt-6 border-t-2 border-blue-500 text-center">
                        <p class="text-gray-300 text-lg">日割り計算後の金額</p>
                        <p id="net-amount-result" class="text-4xl font-extrabold text-green-400 mt-2">¥0</p>
                    </div>
                </div>
            </div>
            
            <!-- Instructions -->
            <div class="mt-8 text-sm text-gray-400 bg-gray-900/50 p-4 rounded-lg">
                <h3 class="font-bold text-white mb-2">使い方</h3>
                <ul class="list-disc list-inside space-y-1">
                    <li>合計金額と期間（開始日・終了日）を入力して「計算する」ボタンを押してください。</li>
                    <li>金額は半角数字で入力してください。</li>
                    <li>日割り額は、入力された「開始日」が含まれる月の日数を基準に計算されます。</li>
                    <li>計算結果の小数点以下は四捨五入されます。</li>
                </ul>
            </div>
        </div>
    </div>

    <script>
        // --- DOM Elements ---
        const totalAmountInput = document.getElementById('total-amount');
        const startYearSelect = document.getElementById('start-year');
        const startMonthSelect = document.getElementById('start-month');
        const startDaySelect = document.getElementById('start-day');
        const endYearSelect = document.getElementById('end-year');
        const endMonthSelect = document.getElementById('end-month');
        const endDaySelect = document.getElementById('end-day');
        const calculateBtn = document.getElementById('calculate-btn');

        // --- Result Elements ---
        const daysInMonthResult = document.getElementById('days-in-month-result');
        const proratedDaysResult = document.getElementById('prorated-days-result');
        const dailyRateResult = document.getElementById('daily-rate-result');
        const netAmountResult = document.getElementById('net-amount-result');

        // --- Initial Setup ---
        window.onload = () => {
            const today = new Date();
            const currentYear = today.getFullYear();
            const currentMonth = today.getMonth() + 1;
            const currentDay = today.getDate();

            // Populate Year and Month selects
            populateYears(startYearSelect, currentYear);
            populateYears(endYearSelect, currentYear);
            populateMonths(startMonthSelect, currentMonth);
            populateMonths(endMonthSelect, currentMonth);

            // Set default values
            startYearSelect.value = currentYear;
            startMonthSelect.value = currentMonth;
            endYearSelect.value = currentYear;
            endMonthSelect.value = currentMonth;

            // Populate Day selects based on default month/year
            populateDays(startDaySelect, currentYear, currentMonth);
            populateDays(endDaySelect, currentYear, currentMonth);
            
            // Set default day
            startDaySelect.value = currentDay > startDaySelect.length ? 1 : currentDay;
            endDaySelect.value = currentDay > endDaySelect.length ? endDaySelect.length : currentDay;
            
            // Add event listeners
            calculateBtn.addEventListener('click', performCalculation);
            startYearSelect.addEventListener('change', () => populateDays(startDaySelect, startYearSelect.value, startMonthSelect.value));
            startMonthSelect.addEventListener('change', () => populateDays(startDaySelect, startYearSelect.value, startMonthSelect.value));
            endYearSelect.addEventListener('change', () => populateDays(endDaySelect, endYearSelect.value, endMonthSelect.value));
            endMonthSelect.addEventListener('change', () => populateDays(endDaySelect, endYearSelect.value, endMonthSelect.value));
        };

        // --- Population Functions ---
        function populateYears(selectElement, currentYear) {
            for (let i = currentYear - 5; i <= currentYear + 5; i++) {
                const option = document.createElement('option');
                option.value = i;
                option.textContent = `${i}年`;
                selectElement.appendChild(option);
            }
        }

        function populateMonths(selectElement, currentMonth) {
            for (let i = 1; i <= 12; i++) {
                const option = document.createElement('option');
                option.value = i;
                option.textContent = `${i}月`;
                selectElement.appendChild(option);
            }
        }

        function populateDays(selectElement, year, month) {
            selectElement.innerHTML = ''; // Clear existing options
            const daysInMonth = new Date(year, month, 0).getDate();
            for (let i = 1; i <= daysInMonth; i++) {
                const option = document.createElement('option');
                option.value = i;
                option.textContent = `${i}日`;
                selectElement.appendChild(option);
            }
        }

        // --- Calculation Logic ---
        function performCalculation() {
            // 1. Get and parse inputs
            const totalAmount = parseFloat(totalAmountInput.value) || 0;
            if (totalAmount <= 0) {
                alert("合計金額を正しく入力してください。");
                return;
            }

            const startDate = new Date(startYearSelect.value, startMonthSelect.value - 1, startDaySelect.value);
            const endDate = new Date(endYearSelect.value, endMonthSelect.value - 1, endDaySelect.value);

            if (endDate < startDate) {
                alert("終了日は開始日より後の日付にしてください。");
                return;
            }

            // 2. Calculate based on the proration rule
            const baseYear = parseInt(startYearSelect.value);
            const baseMonth = parseInt(startMonthSelect.value);
            
            // The number of days in the month of the start date
            const daysInBaseMonth = new Date(baseYear, baseMonth, 0).getDate();
            
            // Daily rate
            const dailyRate = totalAmount / daysInBaseMonth;

            // Number of prorated days (inclusive)
            const timeDiff = endDate.getTime() - startDate.getTime();
            const dayDiff = Math.ceil(timeDiff / (1000 * 3600 * 24)) + 1;

            // Final prorated amount
            const proratedAmount = Math.round(dailyRate * dayDiff);

            // 3. Display results
            updateResultsUI({
                daysInBaseMonth,
                dayDiff,
                dailyRate,
                proratedAmount
            });
        }

        // --- UI Update Function ---
        function formatCurrency(amount) {
            return new Intl.NumberFormat('ja-JP', {
                style: 'currency',
                currency: 'JPY',
            }).format(amount);
        }

        function updateResultsUI(data) {
            daysInMonthResult.textContent = `${data.daysInBaseMonth}日`;
            proratedDaysResult.textContent = `${data.dayDiff}日`;
            dailyRateResult.textContent = formatCurrency(data.dailyRate);
            netAmountResult.textContent = formatCurrency(data.proratedAmount);
        }

    </script>
</body>
</html>
]]></content:encoded>
					
					<wfw:commentRss>https://dejitarumirai.com/archives/4212/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>複利計算（元利合計）</title>
		<link>https://dejitarumirai.com/archives/4209</link>
					<comments>https://dejitarumirai.com/archives/4209#respond</comments>
		
		<dc:creator><![CDATA[deji]]></dc:creator>
		<pubDate>Sat, 05 Jul 2025 11:36:45 +0000</pubDate>
				<category><![CDATA[Webapps]]></category>
		<guid isPermaLink="false">https://dejitarumirai.com/?p=4209</guid>

					<description><![CDATA[複利計算ツール 複利計算ツール [&#8230;]]]></description>
										<content:encoded><![CDATA[<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>複利計算ツール</title>
    <!-- Tailwind CSS for styling -->
    <script src="https://cdn.tailwindcss.com"></script>
    <!-- Chart.js for data visualization -->
    <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
    <!-- Google Fonts: Inter -->
    <link rel="preconnect" href="https://fonts.googleapis.com">
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
    <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;700;900&display=swap" rel="stylesheet">
    <style>
        /* Custom styles to apply the font and theme */
        body {
            font-family: 'Inter', sans-serif;
            background-color: #111827; /* Tailwind's bg-gray-900 */
            color: #d1d5db; /* Tailwind's text-gray-300 */
        }
        /* Custom styles for the informational prose section */
        .prose-custom h3 {
            color: #818cf8; /* Tailwind's text-indigo-400 */
        }
        .prose-custom ul > li::before {
            background-color: #a78bfa; /* Tailwind's bg-purple-400 */
        }
    </style>
</head>
<body>

    <div class="container mx-auto p-4 md:p-8 max-w-5xl">
        <!-- Header Section -->
        <header class="text-center mb-8">
            <h1 class="text-4xl md:text-5xl font-black text-transparent bg-clip-text bg-gradient-to-r from-indigo-400 to-purple-500">複利計算ツール</h1>
            <p class="text-gray-400 mt-2 text-lg">あなたの金融の未来を今日計画しましょう。</p>
        </header>

        <main class="space-y-12">
            <!-- Calculator Tool Section -->
            <section class="bg-gray-800 p-6 rounded-2xl shadow-lg border border-gray-700">
                <div class="grid grid-cols-1 lg:grid-cols-3 gap-8">
                    <!-- Left Column: Input Fields -->
                    <div class="lg:col-span-1 space-y-4">
                        <div>
                            <label for="initial-principal" class="block mb-2 text-sm font-medium text-gray-300">元金</label>
                            <input type="text" id="initial-principal" value="1,000,000円" class="currency-input bg-gray-700 border border-gray-600 text-white text-sm rounded-lg focus:ring-indigo-500 focus:border-indigo-500 block w-full p-2.5">
                        </div>
                        <div>
                            <label for="monthly-contribution" class="block mb-2 text-sm font-medium text-gray-300">毎月の積立額</label>
                            <input type="text" id="monthly-contribution" value="50,000円" class="currency-input bg-gray-700 border border-gray-600 text-white text-sm rounded-lg focus:ring-indigo-500 focus:border-indigo-500 block w-full p-2.5">
                        </div>
                        <div>
                            <label for="interest-rate" class="block mb-2 text-sm font-medium text-gray-300">年利率 (%)</label>
                            <input type="number" id="interest-rate" value="5" class="bg-gray-700 border border-gray-600 text-white text-sm rounded-lg focus:ring-indigo-500 focus:border-indigo-500 block w-full p-2.5">
                        </div>
                        <div>
                            <label for="years" class="block mb-2 text-sm font-medium text-gray-300">投資期間 (年)</label>
                            <input type="number" id="years" value="10" class="bg-gray-700 border border-gray-600 text-white text-sm rounded-lg focus:ring-indigo-500 focus:border-indigo-500 block w-full p-2.5">
                        </div>
                        <div>
                            <label for="compound-frequency" class="block mb-2 text-sm font-medium text-gray-300">複利頻度</label>
                            <select id="compound-frequency" class="bg-gray-700 border border-gray-600 text-white text-sm rounded-lg focus:ring-indigo-500 focus:border-indigo-500 block w-full p-2.5">
                                <option value="365">毎日</option>
                                <option value="12">毎月</option>
                                <option value="4">四半期ごと</option>
                                <option value="2">半年ごと</option>
                                <option value="1" selected>毎年</option>
                            </select>
                        </div>
                    </div>

                    <!-- Right Column: Results & Chart -->
                    <div class="lg:col-span-2 space-y-6">
                        <div class="grid grid-cols-1 sm:grid-cols-3 gap-4 text-center">
                            <div class="bg-gray-900/50 p-4 rounded-lg">
                                <p class="text-sm text-gray-400">拠出金合計</p>
                                <p id="total-principal" class="text-2xl font-bold text-white">0円</p>
                            </div>
                            <div class="bg-gray-900/50 p-4 rounded-lg">
                                <p class="text-sm text-gray-400">獲得利息合計</p>
                                <p id="total-interest" class="text-2xl font-bold text-green-400">0円</p>
                            </div>
                            <div class="bg-gray-900/50 p-4 rounded-lg">
                                <p class="text-sm text-gray-400">将来価値</p>
                                <p id="future-value" class="text-2xl font-bold text-purple-400">0円</p>
                            </div>
                        </div>
                        <div class="relative h-64 md:h-80">
                            <canvas id="compound-chart"></canvas>
                        </div>
                    </div>
                </div>
                <!-- Results Table -->
                <div class="mt-8 overflow-x-auto">
                    <table class="w-full text-sm text-left text-gray-400">
                        <thead class="text-xs text-gray-300 uppercase bg-gray-700/50">
                            <tr>
                                <th scope="col" class="px-6 py-3 rounded-l-lg">年</th>
                                <th scope="col" class="px-6 py-3">拠出金合計</th>
                                <th scope="col" class="px-6 py-3">年間獲得利息</th>
                                <th scope="col" class="px-6 py-3 rounded-r-lg">年末価値</th>
                            </tr>
                        </thead>
                        <tbody id="results-table-body">
                           <!-- Rows will be generated by JavaScript -->
                        </tbody>
                    </table>
                </div>
            </section>

            <!-- Informational Section -->
            <section class="bg-gray-800 p-8 rounded-2xl shadow-lg border border-gray-700">
                <div class="prose prose-invert max-w-none text-gray-300 prose-custom">
                    <h2 class="text-2xl font-bold text-center mb-6 text-indigo-400">複利：世界の8番目の不思議</h2>
                    
                    <h3>複利とは？</h3>
                    <p>複利とは、投資で得た利息を元本に加え、その新しい、より大きな元本がさらに利息を生むプロセスのことです。この「利息が利息を生む」効果により、あなたの資産は加速的に、指数関数的に増加し、特に長期間にわたってその力は絶大です。</p>
                    
                    <h3>複利計算式</h3>
                    <p>この計算機は、定期的な積立金を含む将来価値の計算式を使用しています：</p>
                    <p class="text-center font-mono bg-gray-900/50 p-3 rounded-md text-sm">A = P(1 + r/n)<sup>nt</sup> + PMT × [ ((1 + r/n)<sup>nt</sup> - 1) / (r/n) ]</p>
                    <ul class="text-sm">
                        <li><strong>A</strong>: 利息を含む投資の将来価値</li>
                        <li><strong>P</strong>: 元本（初期投資額）</li>
                        <li><strong>PMT</strong>: 毎月の積立額</li>
                        <li><strong>r</strong>: 年利率（小数形式）</li>
                        <li><strong>n</strong>: 年間の複利計算回数</li>
                        <li><strong>t</strong>: 投資年数</li>
                    </ul>

                    <h3>複利成長を最大化するためのヒント</h3>
                    <p>複利の恩恵を最大限に受けるためには、3つの黄金律を覚えておきましょう：</p>
                    <ul>
                        <li><strong>できるだけ早く始める：</strong>時間はあなたの最大の資産です。少額からでも早く始めることが、長期的には大きな違いを生みます。</li>
                        <li><strong>定期的に規律を持って投資する：</strong>一貫して積立を行うことで、あなたの投資の成長は著しく加速します。</li>
                        <li><strong>利率を最適化する：</strong>競争力のある利率を提供する安全な投資先を探しましょう。わずかな利率の上昇でも、長年にわたって莫大な利益を生み出すことがあります。</li>
                    </ul>
                </div>
            </section>
        </main>
    </div>

    <script>
        document.addEventListener('DOMContentLoaded', () => {
            // --- DOM ELEMENT REFERENCES ---
            const initialPrincipalInput = document.getElementById('initial-principal');
            const monthlyContributionInput = document.getElementById('monthly-contribution');
            const interestRateInput = document.getElementById('interest-rate');
            const yearsInput = document.getElementById('years');
            const compoundFrequencySelect = document.getElementById('compound-frequency');
            
            const futureValueDisplay = document.getElementById('future-value');
            const totalPrincipalDisplay = document.getElementById('total-principal');
            const totalInterestDisplay = document.getElementById('total-interest');
            const resultsTableBody = document.getElementById('results-table-body');
            
            const chartCanvas = document.getElementById('compound-chart');
            let compoundChart; // Variable to hold the chart instance

            // --- HELPER FUNCTIONS ---
            // Formats a number into JPY currency string
            const formatCurrency = (value) => {
                if (isNaN(value) || value === null) return '0円';
                return new Intl.NumberFormat('ja-JP', { style: 'currency', currency: 'JPY' }).format(Math.round(value));
            };

            // Parses a currency string into a number
            const parseCurrency = (value) => {
                return Number(String(value).replace(/[^0-9.-]+/g, ''));
            };

            // --- MAIN CALCULATION LOGIC ---
            function calculateAndDisplay() {
                // Parse input values
                const P = parseCurrency(initialPrincipalInput.value);
                const PMT_monthly = parseCurrency(monthlyContributionInput.value);
                const annualRate = parseFloat(interestRateInput.value) / 100;
                const t = parseInt(yearsInput.value);
                const n = parseInt(compoundFrequencySelect.value);

                // Basic validation
                if (isNaN(P) || isNaN(PMT_monthly) || isNaN(annualRate) || isNaN(t) || isNaN(n) || t <= 0) {
                    resultsTableBody.innerHTML = ''; // Clear table if input is invalid
                    return; 
                }

                // Update chart and table
                updateChartAndTable(P, PMT_monthly, annualRate, t, n);
            }

            // --- CHART AND TABLE UPDATE LOGIC ---
            function updateChartAndTable(P, PMT_monthly, r, t, n) {
                const labels = Array.from({ length: t + 1 }, (_, i) => `${i}年`);
                const contributionData = [];
                const futureValueData = [];
                const yearlyInterestData = [];
                let totalInterestSoFar = 0;

                // Generate data points for each year
                for (let i = 0; i <= t; i++) {
                    const totalContribution = P + (PMT_monthly * 12 * i);
                    contributionData.push(totalContribution);

                    const nt = n * i;
                    const r_over_n = r / n;
                    const PMT = PMT_monthly * (12 / n);

                    const fv_p = P * Math.pow(1 + r_over_n, nt);
                    let fv_c = 0;
                    if (r > 0) {
                        fv_c = PMT * ((Math.pow(1 + r_over_n, nt) - 1) / r_over_n);
                    } else {
                        fv_c = PMT * nt;
                    }
                    const futureValue = fv_p + fv_c;
                    futureValueData.push(futureValue);
                    
                    const totalInterestForYear = futureValue - totalContribution;
                    const interestThisYear = totalInterestForYear - totalInterestSoFar;
                    yearlyInterestData.push(interestThisYear);
                    totalInterestSoFar = totalInterestForYear;
                }
                
                // Update summary displays
                const finalFutureValue = futureValueData[t];
                const finalTotalContribution = contributionData[t];
                const finalTotalInterest = finalFutureValue - finalTotalContribution;

                futureValueDisplay.textContent = formatCurrency(finalFutureValue);
                totalPrincipalDisplay.textContent = formatCurrency(finalTotalContribution);
                totalInterestDisplay.textContent = formatCurrency(finalTotalInterest);

                // Update results table
                resultsTableBody.innerHTML = ''; // Clear previous results
                for (let i = 1; i <= t; i++) {
                    const row = `
                        <tr class="bg-gray-800 border-b border-gray-700">
                            <td class="px-6 py-4">${i}</td>
                            <td class="px-6 py-4">${formatCurrency(contributionData[i])}</td>
                            <td class="px-6 py-4">${formatCurrency(yearlyInterestData[i])}</td>
                            <td class="px-6 py-4 font-medium text-purple-400">${formatCurrency(futureValueData[i])}</td>
                        </tr>
                    `;
                    resultsTableBody.innerHTML += row;
                }

                // If a chart instance already exists, destroy it
                if (compoundChart) {
                    compoundChart.destroy();
                }

                // Create a new Chart.js instance
                compoundChart = new Chart(chartCanvas, {
                    type: 'line',
                    data: {
                        labels: labels,
                        datasets: [
                            {
                                label: '拠出金合計',
                                data: contributionData,
                                borderColor: '#9ca3af', // gray-400
                                backgroundColor: '#9ca3af',
                                fill: false,
                                tension: 0.1
                            },
                            {
                                label: '将来価値',
                                data: futureValueData,
                                borderColor: '#a78bfa', // purple-400
                                backgroundColor: 'rgba(167, 139, 250, 0.2)',
                                fill: true,
                                tension: 0.1
                            }
                        ]
                    },
                    options: {
                        responsive: true,
                        maintainAspectRatio: false,
                        plugins: {
                            legend: { position: 'top', labels: { color: '#d1d5db' } },
                            tooltip: {
                                mode: 'index',
                                intersect: false,
                                callbacks: {
                                    label: (context) => `${context.dataset.label}: ${formatCurrency(context.parsed.y)}`
                                }
                            }
                        },
                        scales: {
                            x: { ticks: { color: '#9ca3af' }, grid: { color: '#374151' } },
                            y: {
                                ticks: { 
                                    color: '#9ca3af',
                                    callback: (value) => formatCurrency(value)
                                },
                                grid: { color: '#374151' }
                            }
                        }
                    }
                });
            }

            // --- EVENT LISTENERS ---
            const inputs = [initialPrincipalInput, monthlyContributionInput, interestRateInput, yearsInput, compoundFrequencySelect];
            inputs.forEach(input => input.addEventListener('input', calculateAndDisplay));

            document.querySelectorAll('.currency-input').forEach(input => {
                input.addEventListener('blur', (e) => {
                    const value = parseCurrency(e.target.value);
                    e.target.value = formatCurrency(value);
                });
                 input.addEventListener('focus', (e) => {
                    e.target.value = parseCurrency(e.target.value) || '';
                });
            });

            // --- INITIALIZATION ---
            calculateAndDisplay();
        });
    </script>
</body>
</html>
]]></content:encoded>
					
					<wfw:commentRss>https://dejitarumirai.com/archives/4209/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>72の法則とは｜知るぽると</title>
		<link>https://dejitarumirai.com/archives/4205</link>
					<comments>https://dejitarumirai.com/archives/4205#respond</comments>
		
		<dc:creator><![CDATA[deji]]></dc:creator>
		<pubDate>Sat, 05 Jul 2025 11:11:34 +0000</pubDate>
				<category><![CDATA[Webapps]]></category>
		<guid isPermaLink="false">https://dejitarumirai.com/?p=4205</guid>

					<description><![CDATA[72の法則 計算ツール 72の [&#8230;]]]></description>
										<content:encoded><![CDATA[<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>72の法則 計算ツール</title>
    <script src="https://cdn.tailwindcss.com"></script>
    <link rel="preconnect" href="https://fonts.googleapis.com">
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
    <link href="https://fonts.googleapis.com/css2?family=Noto+Sans+JP:wght@400;500;700;900&display=swap" rel="stylesheet">
    <style>
        body {
            font-family: 'Noto Sans JP', sans-serif;
            background-color: #111827; /* bg-gray-900 */
            color: #d1d5db; /* text-gray-300 */
        }
        .prose-custom h3 {
            color: #2dd4bf; /* text-teal-400 */
            border-bottom: 1px solid #374151; /* border-gray-700 */
            padding-bottom: 0.5rem;
            margin-top: 2em;
        }
        .prose-custom p {
            line-height: 1.8;
        }
    </style>
</head>
<body>

    <div class="container mx-auto p-4 md:p-8 max-w-3xl">
        <!-- Header -->
        <header class="text-center mb-8">
            <h1 class="text-4xl md:text-5xl font-black text-transparent bg-clip-text bg-gradient-to-r from-teal-400 to-cyan-500">72の法則 計算ツール</h1>
            <p class="text-gray-400 mt-2 text-lg">資産が2倍になるまでの年数を簡単に計算します。</p>
        </header>

        <main class="space-y-12">
            <!-- Calculator Tool -->
            <section class="bg-gray-800 p-8 rounded-2xl shadow-lg border border-gray-700">
                <div class="grid grid-cols-1 md:grid-cols-2 gap-8 items-center">
                    <!-- Left Column: Input -->
                    <div class="space-y-4">
                        <div>
                            <label for="interest-rate" class="block mb-2 text-lg font-medium text-gray-300">年利率（％）を入力:</label>
                            <input type="number" id="interest-rate" placeholder="例: 3" class="bg-gray-700 border border-gray-600 text-white text-2xl rounded-lg focus:ring-cyan-500 focus:border-cyan-500 block w-full p-4">
                        </div>
                        <div class="text-center text-gray-400">
                            <p class="font-mono text-lg">年数 ≒ 72 ÷ 金利</p>
                            <p class="text-sm">お金が2倍になる期間の目安</p>
                        </div>
                    </div>

                    <!-- Right Column: Result -->
                    <div class="text-center">
                        <p class="text-lg text-gray-400 mb-2">お金が2倍になる年数:</p>
                        <p id="result-display" class="text-6xl font-bold text-teal-400">0</p>
                    </div>
                </div>
            </section>

            <!-- Information Section -->
            <section class="bg-gray-800 p-8 rounded-2xl shadow-lg border border-gray-700">
                <div class="prose prose-invert max-w-none text-gray-300 prose-custom">
                    <h2 class="text-2xl font-bold text-center mb-6 text-teal-400">便利な算式「72の法則」について</h2>
                    
                    <p>「72の法則」とは、お金が2倍になる期間が簡単にわかる便利な算式です。<br><strong>「72 ÷ 金利 ≒ お金が2倍になる期間」</strong>で求めることができます。</p>

                    <h3>お金を借りる場合</h3>
                    <p>たとえば、金利18％でお金を借りた場合、「72 ÷ 18 ＝ 4」となるので、約4年で借りたお金が2倍になることがわかります。金利12％でお金を借りた場合には、「72 ÷ 12 ＝ 6」となるので、約6年で借りたお金が2倍になります。この法則を知れば、たとえば消費者金融でお金を借りる場合（10万円以上100万円未満を借りる場合、法律上の上限金利である18％が通常適用される）や、クレジットカードの分割払いやリボルビング払いを利用する場合（通常12～18％が多い）に、どの程度の速さで借金が2倍になるかがわかるため、利用に慎重になることが期待されます。</p>

                    <h3>お金を運用する場合</h3>
                    <p>お金を借りる場合だけでなく、お金を運用する場合も、この算式を利用できます。たとえば、「金利3％でお金を運用した場合、何年で2倍になるか」を知りたいときには、「72 ÷ 3 ＝ 24」となるので、約24年で2倍になることがわかります。</p>
                    <p>この算式は<strong>「72 ÷ お金が2倍になる期間 ≒ 金利」</strong>と変形できるので、こちらを使えば、「お金を2倍にするためには、何％で運用する必要があるか」がわかります。たとえば、10年でお金を2倍にしようと思った場合、「72 ÷ 10 ＝ 7.2」となるので、金利7.2％で運用する必要があることがわかります。</p>
                    
                    <h3>注意点</h3>
                    <p>この算式で使用する金利は複利（利子にも利子がつくこと）が前提です。また、算出される結果（期間や金利）は、大まかな数字であって、正確な数字ではありません。</p>
                    <p class="text-sm text-gray-500 text-center mt-6">この情報は、2015年（平成27年）10月時点の情報です。</p>
                </div>
            </section>
        </main>
    </div>

    <script>
        // JavaScriptのロジックは言語に依存しないため、変更は不要です。
        // It calculates based on the input value and updates the result display.
        document.addEventListener('DOMContentLoaded', () => {
            const interestRateInput = document.getElementById('interest-rate');
            const resultDisplay = document.getElementById('result-display');

            function calculateRuleOf72() {
                const rate = parseFloat(interestRateInput.value);

                if (isNaN(rate) || rate <= 0) {
                    resultDisplay.textContent = '0';
                    return;
                }

                const years = 72 / rate;
                // 小数点以下2桁までにフォーマットします。
                resultDisplay.textContent = Number(years.toFixed(2));
            }

            interestRateInput.addEventListener('input', calculateRuleOf72);
        });
    </script>
</body>
</html>
]]></content:encoded>
					
					<wfw:commentRss>https://dejitarumirai.com/archives/4205/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
