您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
非全螢幕時自動展開聊天室
// ==UserScript== // @name Twitch聊天室自動展開v2 // @version 2.2 // @description 非全螢幕時自動展開聊天室 // @author BaconEgg // @match https://www.twitch.tv/* // @grant none // @namespace https://greasyfork.org/users/735944 // ==/UserScript== (() => { 'use strict'; // 獲取聊天室狀態 按鈕標籤 const getChatState = () => { const label = state.button?.getAttribute('aria-label'); if (!label) return 'unknown'; if (label.indexOf('展開') >= 0) return 'collapsed'; if (label.indexOf('摺疊') >= 0) return 'expanded'; return 'unknown'; }; // 常數定義 const BUTTON_SELECTOR = '[data-a-target="right-column__toggle-collapse-btn"]'; const CHAT_CONTAINER_SELECTOR = '[data-a-target="right-column"]'; const THEATER_MODE_SELECTOR = '.channel-page__video-player--theatre-mode'; const THROTTLE_DELAY = 500; // 增加到 500ms 進一步降低 CPU 使用率 // 狀態管理 const state = { button: null, chatContainer: null, lastCombo: '', observer: null, isTheaterMode: false, // 快取劇院模式狀態 pendingRAF: null // 追蹤 requestAnimationFrame }; // 節流工具:確保函數不會高頻呼叫 const throttle = (fn, delay) => { let lastCall = 0; let scheduled = null; return (...args) => { const now = Date.now(); if (now - lastCall >= delay) { lastCall = now; fn(...args); } else if (!scheduled) { scheduled = setTimeout(() => { lastCall = Date.now(); scheduled = null; fn(...args); }, delay - (now - lastCall)); } }; }; // 快取按鈕元素 const getButton = () => { if (state.button?.isConnected) return state.button; const chat = getChatContainer(); if (chat) { state.button = chat.querySelector(BUTTON_SELECTOR); if (state.button?.isConnected) return state.button; } state.button = document.querySelector(BUTTON_SELECTOR); return state.button; }; // 快取聊天室區塊 const getChatContainer = () => { if (state.chatContainer?.isConnected) return state.chatContainer; state.chatContainer = document.querySelector(CHAT_CONTAINER_SELECTOR); return state.chatContainer; }; // 檢查是否為劇院模式,並快取結果 const checkTheaterMode = () => { state.isTheaterMode = !!document.querySelector(THEATER_MODE_SELECTOR); return state.isTheaterMode; }; // 主要功能:只在狀態改變時才操作 DOM const updateChatVisibility = (trigger = '') => { try { const btn = getButton(); if (!btn) return; const chatState = getChatState(); const isFS = document.fullscreenElement !== null; const theater = state.isTheaterMode; // 使用快取的劇院模式狀態 const combo = `${isFS}-${theater}-${chatState}`; if (combo === state.lastCombo) return; state.lastCombo = combo; // 取消任何已排程但尚未執行的 RAF if (state.pendingRAF) { cancelAnimationFrame(state.pendingRAF); state.pendingRAF = null; } if ((!isFS && chatState === 'collapsed') || (isFS && chatState === 'expanded')) { state.pendingRAF = requestAnimationFrame(() => { btn.click(); state.pendingRAF = null; }); } } catch (err) { console.error('[Twitch] Error updating chat visibility:', err); } }; // 節流版本 const throttledUpdateChatVisibility = throttle(updateChatVisibility, THROTTLE_DELAY); // 觀察器:只在必要時才更新,並用節流避免高頻率 const startObserver = () => { if (state.observer) return; // 先檢查劇院模式狀態 checkTheaterMode(); const chat = getChatContainer(); const target = chat || document.body; state.observer = new MutationObserver((mutations) => { if (!state.button?.isConnected) state.button = null; let needsUpdate = false; let theaterModeChanged = false; for (const mutation of mutations) { // 檢查按鈕狀態變化 if (mutation.type === 'attributes' && mutation.attributeName === 'aria-label' && mutation.target.matches && mutation.target.matches(BUTTON_SELECTOR)) { needsUpdate = true; break; } // 檢查劇院模式變化 if (mutation.type === 'attributes' && mutation.attributeName === 'class' && mutation.target.classList && mutation.target.classList.contains('channel-page__video-player')) { theaterModeChanged = true; needsUpdate = true; break; } // 檢查按鈕是否新增 if (mutation.type === 'childList' && !state.button) { const addedNodes = Array.from(mutation.addedNodes); if (addedNodes.some(node => node.nodeType === 1 && (node.matches?.(BUTTON_SELECTOR) || node.querySelector?.(BUTTON_SELECTOR)))) { needsUpdate = true; break; } } } // 只在需要時更新劇院模式狀態 if (theaterModeChanged) { checkTheaterMode(); } if (needsUpdate) { throttledUpdateChatVisibility('Mutation'); } }); // 優化 MutationObserver 配置 state.observer.observe(target, { childList: true, subtree: true, attributes: true, attributeFilter: ['aria-label', 'class'], attributeOldValue: false }); }; // 初始化優化:只在按鈕存在時才啟動 observer const init = () => { // 延遲初始化,確保頁面完全載入 setTimeout(() => { const btn = getButton(); if (btn) { checkTheaterMode(); // 初始化時檢查劇院模式 throttledUpdateChatVisibility('Init'); startObserver(); } else { // 如果按鈕不存在,設置一個一次性 MutationObserver 來等待按鈕出現 const tempObserver = new MutationObserver((mutations, observer) => { if (getButton()) { observer.disconnect(); init(); // 按鈕出現後再次嘗試初始化 } }); tempObserver.observe(document.body, { childList: true, subtree: true, attributes: false }); } }, 500); // 延遲 500ms 確保頁面元素已載入 }; // 事件監聽 window.addEventListener('load', () => { init(); // 全螢幕變化事件 document.addEventListener('fullscreenchange', () => { throttledUpdateChatVisibility('Fullscreen'); }, { passive: true }); // 頁面可見性變化事件 document.addEventListener('visibilitychange', () => { if (!document.hidden) { // 頁面變為可見時,重新檢查劇院模式 checkTheaterMode(); throttledUpdateChatVisibility('Visibility'); } }, { passive: true }); }, { passive: true }); })();