PTT Web Image Preview (term.ptt.cc) Centered & Large

在 term.ptt.cc 預覽圖片 (置中放大顯示、防切邊、防卡圖)

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         PTT Web Image Preview (term.ptt.cc) Centered & Large
// @namespace    http://tampermonkey.net/
// @version      1.4
// @description  在 term.ptt.cc 預覽圖片 (置中放大顯示、防切邊、防卡圖)
// @author       Gemini
// @match        https://term.ptt.cc/*
// @grant        none
// @license MIT
// ==/UserScript==

(function() {
    'use strict';

    const PREVIEW_ID = 'ptt-center-preview-img';
    const IMG_EXTENSIONS = /\.(jpg|jpeg|png|gif|webp|bmp|svg)(\?.*)?$/i;

    let activeLinkElement = null;
    let watchDogTimer = null;

    // 建立或取得預覽圖片元素
    function getPreviewElement() {
        let img = document.getElementById(PREVIEW_ID);
        if (!img) {
            img = document.createElement('img');
            img.id = PREVIEW_ID;

            // === CSS 樣式設定:置中與最大化 ===
            img.style.position = 'fixed';
            img.style.top = '50%';
            img.style.left = '50%';
            img.style.transform = 'translate(-50%, -50%)'; // 讓圖片真正的中心點對準畫面中心

            img.style.zIndex = '2147483647'; // 最上層
            img.style.maxWidth = '95vw';     // 最大寬度:視窗寬度的 95%
            img.style.maxHeight = '95vh';    // 最大高度:視窗高度的 95%
            img.style.objectFit = 'contain'; // 保持比例縮放,確保圖片完整顯示不裁切

            img.style.border = '2px solid rgba(255, 255, 255, 0.5)';
            img.style.borderRadius = '8px';
            img.style.backgroundColor = '#000'; // 背景黑,避免透明圖看不清
            img.style.boxShadow = '0 0 30px rgba(0,0,0,0.9)'; // 強烈的陰影增加對比
            img.style.pointerEvents = 'none'; // 讓滑鼠穿透,避免游標誤觸圖片
            img.style.display = 'none';

            document.body.appendChild(img);
        }
        return img;
    }

    // 關閉預覽
    function removePreviewImage() {
        const img = document.getElementById(PREVIEW_ID);
        if (img) {
            img.style.display = 'none';
            img.src = '';
        }
        activeLinkElement = null;
        if (watchDogTimer) {
            clearInterval(watchDogTimer);
            watchDogTimer = null;
        }
    }

    // 滑鼠進入連結
    document.addEventListener('mouseover', function(e) {
        const target = e.target.closest('a');

        if (target && target.href && IMG_EXTENSIONS.test(target.href)) {
            // 防止重複觸發
            if (activeLinkElement === target) return;

            activeLinkElement = target;

            const img = getPreviewElement();
            img.src = target.href;
            img.style.display = 'block'; // 顯示圖片 (位置由 CSS 自動置中)

            // 啟動 WatchDog:防止換頁後圖片卡住
            if (watchDogTimer) clearInterval(watchDogTimer);
            watchDogTimer = setInterval(() => {
                // 檢查元素是否還存在於頁面上
                if (activeLinkElement && !activeLinkElement.isConnected) {
                    removePreviewImage();
                }
            }, 200);
        }
    }, true);

    // 滑鼠離開連結
    document.addEventListener('mouseout', function(e) {
        const target = e.target.closest('a');
        if (target && target === activeLinkElement) {
            removePreviewImage();
        }
    }, true);

    // === 安全機制:按鍵或滾動時強制關閉 ===
    window.addEventListener('keydown', function() {
        if (activeLinkElement) removePreviewImage();
    }, true);

    window.addEventListener('wheel', function() {
        if (activeLinkElement) removePreviewImage();
    }, true);

})();