スクロールするHTML

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Interactive World Map</title>
    <style>
        body {
            margin: 0;
            padding: 0;
            overflow: hidden;
        }

        .map-container {
            position: absolute;
            width: 100%;
            height: 100%;
            overflow: hidden;
            background-color: #a4d1e9;
        }

        .map-wrapper {
            position: absolute;
            will-change: transform;
            touch-action: none;
        }

        .map {
            width: 2000px;
            height: 1000px;
            border: 1px solid #333;
            overflow: visible;
        }

        .instructions {
            position: absolute;
            bottom: 20px;
            left: 20px;
            background-color: rgba(255, 255, 255, 0.7);
            padding: 10px;
            border-radius: 5px;
            font-family: Arial, sans-serif;
            z-index: 100;
        }
    </style>
</head>
<body>
    <div class="map-container">
        <div class="map-wrapper">
            <div class="map" id="map">
                <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 2000 1000">
                    <!-- 海の背景 -->
                    <rect width="2000" height="1000" fill="#a4d1e9"/>
                    
                    <!-- 主要な大陸 -->
                    <!-- 北米 -->
                    <path d="M400,150 Q450,120 500,150 Q520,140 550,150 Q600,120 650,150 Q680,200 670,250 Q640,280 650,330 Q630,380 580,410 Q560,450 520,460 Q500,500 460,520 Q420,510 400,540 Q350,530 320,490 Q330,450 300,420 Q270,400 280,350 Q300,320 280,290 Q290,250 330,230 Q370,240 380,200 Q390,170 400,150 Z" fill="#c2b280"/>
                    
                    <!-- 南米 -->
                    <path d="M520,460 Q540,500 560,550 Q580,600 560,650 Q540,700 560,750 Q540,800 500,830 Q460,820 430,780 Q410,740 440,700 Q420,650 440,600 Q420,550 450,520 Q480,510 520,460 Z" fill="#c2b280"/>
                    
                    <!-- ユーラシア -->
                    <path d="M800,150 Q850,130 900,150 Q950,120 1000,150 Q1050,130 1100,150 Q1150,140 1200,170 Q1250,140 1300,170 Q1350,150 1400,180 Q1450,220 1480,180 Q1530,200 1550,250 Q1580,300 1550,350 Q1500,330 1450,350 Q1400,380 1350,350 Q1300,380 1250,350 Q1200,380 1150,400 Q1100,380 1050,410 Q1000,380 950,350 Q900,380 850,350 Q820,320 780,350 Q750,320 720,280 Q680,250 670,200 Q700,180 750,190 Q780,160 800,150 Z" fill="#c2b280"/>
                    
                    <!-- アフリカ -->
                    <path d="M900,380 Q930,420 970,450 Q1000,500 980,550 Q950,600 980,650 Q950,700 900,720 Q850,700 820,650 Q800,600 750,580 Q720,530 750,480 Q780,450 750,410 Q780,380 820,400 Q850,370 900,380 Z" fill="#c2b280"/>
                    
                    <!-- オーストラリア -->
                    <path d="M1500,500 Q1550,480 1600,500 Q1650,480 1700,520 Q1730,570 1700,620 Q1650,650 1600,620 Q1550,650 1500,620 Q1470,570 1500,500 Z" fill="#c2b280"/>
                    
                    <!-- 南極大陸 -->
                    <path d="M800,900 Q900,870 1000,880 Q1100,860 1200,880 Q1300,860 1400,880 Q1500,850 1600,890 Q1550,920 1450,930 Q1350,950 1250,930 Q1150,950 1050,930 Q950,950 850,930 Q800,920 800,900 Z" fill="#e0e0e0"/>
                    
                    <!-- 主要な島々 -->
                    <ellipse cx="1450" cy="280" rx="40" ry="25" fill="#c2b280"/>  <!-- 日本 -->
                    <ellipse cx="1650" cy="350" rx="35" ry="20" fill="#c2b280"/>  <!-- フィリピン -->
                    <ellipse cx="850" cy="200" rx="30" ry="20" fill="#c2b280"/>   <!-- イギリス -->
                    
                    <!-- グリッド線 (オプション) -->
                    <line x1="0" y1="500" x2="2000" y2="500" stroke="#88b3ce" stroke-width="1" stroke-dasharray="5,5"/>
                    <line x1="1000" y1="0" x2="1000" y2="1000" stroke="#88b3ce" stroke-width="1" stroke-dasharray="5,5"/>
                </svg>
            </div>
        </div>
    </div>
    <div class="instructions">
        ドラッグ: マップを動かす<br>
        マウスホイール: ズーム
    </div>

    <script>
        document.addEventListener('DOMContentLoaded', () => {
            const mapContainer = document.querySelector('.map-container');
            const mapWrapper = document.querySelector('.map-wrapper');
            const map = document.getElementById('map');

            // Initial position
            let posX = 0;
            let posY = 0;
            let scale = 1;
            let isDragging = false;
            let startPosX = 0;
            let startPosY = 0;
            let lastMouseX = 0;
            let lastMouseY = 0;

            // Map dimensions
            const mapWidth = 2000;
            const mapHeight = 1000;

            // マップを敷き詰めるための関数
            function createMapGrid() {
                mapWrapper.innerHTML = ''; // 既存のマップをクリア
                mapWrapper.appendChild(map); // 元のマップを再追加
                
                // 3x3のグリッドでマップを複製して敷き詰める
                for (let y = -1; y <= 1; y++) {
                    for (let x = -1; x <= 1; x++) {
                        // 中央の元マップはスキップ
                        if (x === 0 && y === 0) continue;
                        
                        const clone = map.cloneNode(true);
                        clone.style.position = 'absolute';
                        clone.style.left = `${x * mapWidth}px`;
                        clone.style.top = `${y * mapHeight}px`;
                        mapWrapper.appendChild(clone);
                    }
                }
            }
            
            // マップを敷き詰める
            createMapGrid();
            
            // 背景の海の色を変更
            mapContainer.style.backgroundColor = 'transparent';

            // Initialize position
            updatePosition();

            // Touch event handlers
            mapContainer.addEventListener('touchstart', handleTouchStart);
            mapContainer.addEventListener('touchmove', handleTouchMove);
            mapContainer.addEventListener('touchend', handleTouchEnd);

            // Mouse event handlers
            mapContainer.addEventListener('mousedown', handleMouseDown);
            window.addEventListener('mousemove', handleMouseMove);
            window.addEventListener('mouseup', handleMouseUp);
            mapContainer.addEventListener('wheel', handleWheel, { passive: false });

            // Touch events
            function handleTouchStart(e) {
                if (e.touches.length === 1) {
                    isDragging = true;
                    startPosX = posX;
                    startPosY = posY;
                    lastMouseX = e.touches[0].clientX;
                    lastMouseY = e.touches[0].clientY;
                    e.preventDefault();
                }
            }

            function handleTouchMove(e) {
                if (isDragging && e.touches.length === 1) {
                    const dx = e.touches[0].clientX - lastMouseX;
                    const dy = e.touches[0].clientY - lastMouseY;
                    
                    posX = startPosX + dx;
                    posY = startPosY + dy;
                    
                    lastMouseX = e.touches[0].clientX;
                    lastMouseY = e.touches[0].clientY;
                    
                    updatePosition();
                    e.preventDefault();
                }
            }

            function handleTouchEnd(e) {
                isDragging = false;
                e.preventDefault();
            }

            // Mouse events
            function handleMouseDown(e) {
                isDragging = true;
                startPosX = posX;
                startPosY = posY;
                lastMouseX = e.clientX;
                lastMouseY = e.clientY;
                e.preventDefault();
            }

            function handleMouseMove(e) {
                if (isDragging) {
                    const dx = e.clientX - lastMouseX;
                    const dy = e.clientY - lastMouseY;
                    
                    posX += dx;
                    posY += dy;
                    
                    lastMouseX = e.clientX;
                    lastMouseY = e.clientY;
                    
                    updatePosition();
                }
            }

            function handleMouseUp() {
                isDragging = false;
            }

            // Wheel for zooming
            function handleWheel(e) {
                e.preventDefault();
                const delta = -Math.sign(e.deltaY) * 0.1;
                const newScale = Math.min(Math.max(scale + delta, 0.5), 3);
                
                // Calculate mouse position relative to map
                const rect = mapContainer.getBoundingClientRect();
                const mouseX = e.clientX - rect.left;
                const mouseY = e.clientY - rect.top;
                
                // Adjust position to zoom toward mouse position
                const scaleChange = newScale / scale;
                posX = mouseX - (mouseX - posX) * scaleChange;
                posY = mouseY - (mouseY - posY) * scaleChange;
                
                scale = newScale;
                updatePosition();
            }

            // Update map position with wrapping
            function updatePosition() {
                // Handle vertical wrapping
                while (posY > 0) posY -= mapHeight * scale;
                while (posY < -mapHeight * scale) posY += mapHeight * scale;
                
                // Handle horizontal wrapping
                while (posX > 0) posX -= mapWidth * scale;
                while (posX < -mapWidth * scale) posX += mapWidth * scale;
                
                mapWrapper.style.transform = `translate(${posX}px, ${posY}px) scale(${scale})`;
                mapWrapper.style.transformOrigin = '0 0';
                
                // 大きくズームアウトした時やスクロールで新しいエリアが見えるようになった時に
                // 必要に応じてマップグリッドを再構築
                const visibleWidth = window.innerWidth / scale;
                const visibleHeight = window.innerHeight / scale;
                
                if (visibleWidth > mapWidth * 3 || visibleHeight > mapHeight * 3) {
                    const newScale = Math.min(window.innerWidth / (mapWidth * 2), window.innerHeight / (mapHeight * 2));
                    scale = Math.max(newScale, 0.5);
                    updatePosition();
                }
            }
        });
    </script>
</body>
</html>Code language: HTML, XML (xml)