Gemini Chat
Hi! I'm Gemini. How can I help you today?
我想要寫一個顯示照片的app,只需要在自己電腦上的瀏覽器能執行即可,不需要靠遠端的伺服器來執行,請幫幫我。 需求如下,(另外以JavaScript語法在最後附上我的相簿位置,請注意,這僅供參考,我以後會自行增減。) (1) 生成一個單獨而完整的html。 (2) 提供一個下拉選單,可讓我選擇不同的相簿。 (3) 相簿的照片先以縮圖方式呈現,等我進一步點選後才呈現完整的照片。 (4) 除了能讓我能單獨選擇照片外,另外提供一個"播放/暫停"按鍵,可依序自動播放所選相簿內的所有照片,同一按鍵也可以暫停相片的播放。 (5) "播放/暫停"按鍵的功能可用鍵盤的空白鍵來取代。鍵盤的左和右方向鍵,可以控制播放前一張和下一張照片。 (6) 自動播放時,照片顯示的時間可讓我指定,單位以秒計算。 (7) 注意!當處在暫停狀態時,照片會永遠顯示,也就是說照片顯示時間這個設定會跟著失效。一旦恢復播放模式,照片顯示時間才有作用。 (8) 下拉選單、播放按鍵和控制照片顯示時間的輸入這三項請固定在畫面最上方。縮圖以一條水平列呈現,固定在畫面最下方。中間大部分空白則顯示完整的圖片。注意照片下方的描述文字別被遮掉。 (9) 不要一次顯示所有的縮圖,因為萬一相簿內的照片太多的話,會延遲程式後面的操作。僅在可視畫面內顯示可選的縮圖,更多的縮圖要隨著照片的播放而動態改變。 const sources = { "my_hiking_1": { path: "../photo_galleries/my_album_1/", data: [ "001.jpg", "002.jpg", "003.jpg", ], description: [ "001_comment", "002_comment", "003_comment", ] }, "my_hiking_2": { path: "../photo_galleries/my_album_2/", data: [ "001.jpg", "002.jpg", ], description: [ "001_comment", "002_comment", ] }, };

好的,這是一個符合您需求的完整 HTML 文件,其中包含了 JavaScript 程式碼,可以直接在瀏覽器中開啟使用:

<!DOCTYPE html>
<html lang="zh-Hant">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>我的相簿</title>
    <style>
        body {
            font-family: sans-serif;
            margin: 0;
            padding: 0;
            background-color: #f0f0f0;
            display: flex;
            flex-direction: column;
            height: 100vh; /* 使 body 佔滿整個視窗高度 */
        }

        #controls {
            position: sticky;
            top: 0;
            background-color: #fff;
            padding: 10px;
            z-index: 10; /* 確保控制項在最上層 */
            display: flex;
            align-items: center;
            gap: 10px;
        }

        #mainImageContainer {
            flex-grow: 1; /* 讓圖片容器佔滿剩餘空間 */
            display: flex;
            justify-content: center;
            align-items: center;
            overflow: hidden; /* 隱藏超出範圍的部分 */
        }

        #mainImage {
            max-width: 90%;  /* 控制圖片最大寬度 */
            max-height: 90%; /* 控制圖片最大高度 */
            display: block; /* 移除圖片下方的空隙 */
        }
        #mainImageCaption{
            text-align: center;        
        }

        #thumbnailContainer {
            position: sticky;
            bottom: 0;
            background-color: #ddd;
            padding: 10px;
            overflow-x: auto; /* 允許水平滾動 */
            white-space: nowrap; /* 防止縮圖換行 */
            z-index: 10;
        }


        .thumbnail {
            width: 100px;
            height: 100px;
            margin: 5px;
            cursor: pointer;
            border: 2px solid transparent;
        }

        .thumbnail.active {
            border-color: blue;
        }
    </style>
</head>
<body>
    <div id="controls">
        <label for="albumSelect">選擇相簿:</label>
        <select id="albumSelect"></select>

        <button id="playPauseBtn">播放</button>

        <label for="intervalInput">間隔 (秒):</label>
        <input type="number" id="intervalInput" value="3" min="1">
    </div>

    <div id="mainImageContainer">
        <img id="mainImage" src="" alt="">
        <div id="mainImageCaption"></div>  
    </div>

    <div id="thumbnailContainer">
    </div>

    <script>
        const sources = {
            "my_hiking_1": {
                path: "../photo_galleries/my_album_1/",
                data: [
                    "001.jpg",
                    "002.jpg",
                    "003.jpg",
                ],
                description: [
                    "001_comment",
                    "002_comment",
                    "003_comment",
                ]
            },
            "my_hiking_2": {
                path: "../photo_galleries/my_album_2/",
                data: [
                    "001.jpg",
                    "002.jpg",
                ],
                description: [
                    "001_comment",
                    "002_comment",
                ]
            },
        };

        const albumSelect = document.getElementById('albumSelect');
        const playPauseBtn = document.getElementById('playPauseBtn');
        const intervalInput = document.getElementById('intervalInput');
        const mainImage = document.getElementById('mainImage');
        const mainImageCaption = document.getElementById('mainImageCaption');
        const thumbnailContainer = document.getElementById('thumbnailContainer');
        const mainImageContainer = document.getElementById('mainImageContainer');

        let currentAlbum = null;
        let currentIndex = 0;
        let timer = null;
        let isPlaying = false;
        let interval = 3000; // 預設間隔 3 秒

        // 初始化相簿選項
        function initAlbums() {
            for (const albumName in sources) {
                const option = document.createElement('option');
                option.value = albumName;
                option.textContent = albumName;
                albumSelect.appendChild(option);
            }
            albumSelect.addEventListener('change', loadAlbum);
        }
        // 載入相簿
        function loadAlbum() {
            currentAlbum = albumSelect.value;
            currentIndex = 0;
            stopPlayback();
            createThumbnails();
            showImage(0); // 顯示第一張圖片

             // 顯示可視範圍內的縮圖
            showVisibleThumbnails();
        }
        
        // 建立縮圖
        function createThumbnails() {
            thumbnailContainer.innerHTML = ''; // 清空現有縮圖
            if (!sources[currentAlbum]) return;

            sources[currentAlbum].data.forEach((filename, index) => {
                const thumbnail = document.createElement('img');
                thumbnail.src = sources[currentAlbum].path + filename;
                thumbnail.classList.add('thumbnail');
                thumbnail.addEventListener('click', () => {
                    stopPlayback();  //點擊縮圖時停止播放
                    showImage(index);
                });
                thumbnailContainer.appendChild(thumbnail);
            });
        }
        
        // 顯示指定索引的圖片
        function showImage(index) {
           if (!sources[currentAlbum] || index < 0 || index >= sources[currentAlbum].data.length) {
                return; // 索引超出範圍
            }

            currentIndex = index;
            mainImage.src = sources[currentAlbum].path + sources[currentAlbum].data[index];
            mainImageCaption.textContent = sources[currentAlbum].description[index] || '';
            
            // 更新縮圖的 active 狀態
            document.querySelectorAll('.thumbnail').forEach((thumb, i) => {
                thumb.classList.toggle('active', i === index);
            });

            // 捲動到目前圖片的縮圖
            scrollToThumbnail(index);            
        }

        //捲動到指定縮圖的可視範圍內
        function scrollToThumbnail(index) {
            const thumbnail = document.querySelectorAll('.thumbnail')[index];
            if(thumbnail){
                thumbnail.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'center' });
            }
        }


        // 開始/繼續 播放
        function startPlayback() {
            if (!sources[currentAlbum]) return;
            if (isPlaying) return; // 已經在播放
                isPlaying = true;
                playPauseBtn.textContent = '暫停';
            if (currentIndex >= sources[currentAlbum].data.length)
            {    currentIndex = 0; //如果已到最後一張, 從頭播放
            }    
            showImage(currentIndex); // 顯示目前圖片
            timer = setInterval(() => {
                currentIndex = (currentIndex + 1) % sources[currentAlbum].data.length; // 循環播放
                showImage(currentIndex);
            }, interval);
           
        }

        // 暫停播放
        function pausePlayback() {
            isPlaying = false;
            playPauseBtn.textContent = '播放';
            clearInterval(timer);
        }

        function stopPlayback() {
            pausePlayback(); // 停止計時器
        }

        // 控制播放/暫停
        function togglePlayback() {
            if (isPlaying) {
                pausePlayback();
            } else {
                startPlayback();
            }
        }

        // 鍵盤事件處理
        function handleKeyPress(event) {
            switch (event.key) {
                case ' ': // 空白鍵
                    togglePlayback();
                    event.preventDefault();
                    break;
                case 'ArrowLeft': // 左方向鍵
                    stopPlayback(); //先停止播放
                    showImage(currentIndex - 1);
                    break;
                case 'ArrowRight': // 右方向鍵
                    stopPlayback(); //先停止播放                
                    showImage(currentIndex + 1);
                    break;
            }
        }

        // 更新間隔時間
        function updateInterval() {
            const newInterval = parseInt(intervalInput.value, 10) * 1000;
            if (!isNaN(newInterval) && newInterval >= 1000) {
                interval = newInterval;
                // 如果正在播放,重新啟動計時器以應用新的間隔
                if (isPlaying) {
                    pausePlayback();
                    startPlayback();
                }
            }
        }
        
        // 監聽縮圖容器的滾動事件
        thumbnailContainer.addEventListener('scroll', () => {
            clearTimeout(scrollTimeout);
            scrollTimeout = setTimeout(showVisibleThumbnails, 100); // 設定延遲
        });
        
        // 顯示可視範圍內的縮圖
        function showVisibleThumbnails() {
            const thumbnails = document.querySelectorAll('.thumbnail');
            const containerRect = thumbnailContainer.getBoundingClientRect();

            thumbnails.forEach((thumbnail) => {
                const thumbnailRect = thumbnail.getBoundingClientRect();
                const isVisible = (
                    thumbnailRect.left >= containerRect.left &&
                    thumbnailRect.right <= containerRect.right
                );

                // 根據可視性來延遲載入或隱藏縮圖
                if (isVisible) {
                    thumbnail.style.visibility = 'visible'; // 顯示
                } else {
                    thumbnail.style.visibility = 'hidden'; // 隱藏, 但仍占空間
                }
            });
        }
        
        // 初始化
        initAlbums();
        playPauseBtn.addEventListener('click', togglePlayback);
        intervalInput.addEventListener('change', updateInterval);
        document.addEventListener('keydown', handleKeyPress);
        
        // 初始載入第一個相簿 (如果存在)
        if (albumSelect.options.length > 0) {
            albumSelect.value = albumSelect.options[0].value;
            loadAlbum();
        }

    </script>
</body>
</html>

程式碼說明與重要功能解釋:

  1. HTML 結構:

    • controls: 包含下拉選單、播放/暫停按鈕、間隔時間輸入框。
    • mainImageContainer: 用於顯示完整大小的圖片。
    • mainImage: img 標籤,顯示實際的圖片。
    • mainImageCaption: 顯示圖片描述文字。
    • thumbnailContainer: 包含所有縮圖的容器,設定為水平滾動。
    • .thumbnail: 每個縮圖的樣式。
    • .thumbnail.active: 當前選中圖片的縮圖樣式。
  2. CSS 樣式:

    • 使用 position: sticky#controls#thumbnailContainer 固定在頂部和底部。
    • #mainImageContainer 使用 flex-grow: 1 佔滿剩餘空間,並置中顯示圖片。
    • #mainImage 設置最大寬度和高度以適應容器。
    • .thumbnail 設定基本樣式,cursor: pointer 表示可點擊。
    • .thumbnail.active 為選中的縮圖加上邊框。
  3. JavaScript 功能:

    • sources: 定義相簿資料,包含路徑、圖片檔名、描述。

    • 變數宣告: 取得 HTML 元素,並初始化變數。

    • initAlbums(): 動態產生下拉選單的選項。

    • loadAlbum():

      • 設定 currentAlbum
      • 重置 currentIndex 為 0。
      • 停止播放 (如果有)。
      • 呼叫 createThumbnails() 建立縮圖。
      • 呼叫 showImage(0) 顯示第一張圖片。
      • 呼叫showVisibleThumbnails()顯示可視範圍內的縮圖。
    • createThumbnails():

      • 清空 #thumbnailContainer
      • 根據 sources 資料動態產生縮圖 img 元素。
      • 為每個縮圖加上點擊事件,點擊時呼叫 showImage()
    • showImage(index):

      • 更新 mainImagesrc 屬性為指定圖片。
      • 更新圖片下方的描述文字mainImageCaption.
      • 更新縮圖的 .active 類別。
      • 呼叫 scrollToThumbnail(index) 將對應的縮圖捲動到可視範圍。
    • scrollToThumbnail(index): 將對應的縮圖捲動到可視範圍。

    • startPlayback():

      • 檢查是否已在播放。
      • 設定 isPlayingtrue
      • 更新按鈕文字為 "暫停"。
      • 使用 setInterval() 定時切換圖片,並呼叫 showImage()
    • pausePlayback():

      • 設定 isPlayingfalse
      • 更新按鈕文字為 "播放"。
      • 使用 clearInterval() 清除計時器。
    • stopPlayback(): 完全停止播放 (清除計時器)。

    • togglePlayback(): 切換播放/暫停狀態。

    • handleKeyPress(event): 處理鍵盤事件 (空白鍵、左右方向鍵)。

    • updateInterval(): 更新間隔時間,並在播放時重新啟動計時器。

    • showVisibleThumbnails():

      • 獲取所有縮圖和容器的可視範圍。
      • 根據每個縮圖是否在可視範圍內,改變其visibility屬性。
      • 延遲處理,以提高效能。
    • 初始化:

      • 呼叫 initAlbums()
      • 綁定事件監聽器 (按鈕點擊、輸入框改變、鍵盤按下)。
      • 載入第一個相簿 (如果存在)。

如何使用:

  1. 將程式碼複製到一個文字編輯器 (例如 Notepad++, VS Code)。
  2. 將檔案儲存為 .html 檔案 (例如 my_album.html)。
  3. 將您的照片放入相對應的資料夾中。
    • 建立一個名為 photo_galleries 的資料夾。
    • photo_galleries 資料夾中,建立 my_album_1my_album_2 資料夾,並放入對應的圖片。
  4. 用瀏覽器開啟 .html 檔案。

重要注意事項:

  • 路徑: 確保 sources 中的 path 正確指向您的圖片資料夾。 此範例使用相對路徑 (例如 ../photo_galleries/my_album_1/)。 如果您的 HTML 檔案和 photo_galleries 資料夾位於同一層級,則可以使用 ./photo_galleries/my_album_1/。 如果它們在不同的位置,您需要調整路徑。
  • 圖片檔名和描述: 確保 sources 中的 data (圖片檔名) 和 description (描述文字) 與您的實際檔案和內容相符。
  • 跨域問題: 如果您將 HTML 檔案和圖片資料夾放在不同的網域 (例如,HTML 在 file:///,圖片在 http:// 開頭的網址),可能會遇到跨域問題。 最簡單的解決方法是將它們放在同一個資料夾中,並使用相對路徑。
  • 效能: 此範例已使用showVisibleThumbnails()來優化縮圖的載入,但如果您要處理非常大量的圖片 (數千張),可能需要進一步的優化,例如使用 Web Workers 或更進階的圖片載入技術。
  • 相容性: 此程式碼使用了一些較新的 JavaScript 語法 (例如 const, let, 箭頭函式),在較舊的瀏覽器中可能無法正常運作。 如果您需要支援較舊的瀏覽器,您可能需要使用 Babel 或類似的工具將程式碼轉換為更相容的版本。
  • 延展性: 這個程式碼提供了一個基本的框架。您可以根據需要新增更多功能,例如:
    • 圖片預載入 (Preloading)。
    • 全螢幕模式。
    • 圖片編輯功能 (裁剪、旋轉等)。
    • 更精細的控制 (例如,調整圖片的顯示速度、過場效果等)。
    • 支援影片播放。

希望這個程式碼對您有幫助!

Tokens used: 4582