您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Greasy Fork is available in English.
哔哩哔哩宽屏体验
当前为
// ==UserScript== // @name 哔哩哔哩宽屏 // @name:en Wider Bilibili // @namespace http://greasyfork.icu/users/1125570 // @description 哔哩哔哩宽屏体验 // @description:en BiliBili, but wider // @version 0.3.0.1 // @author posthumz // @license MIT // @match http*://*.bilibili.com/* // @icon https://www.bilibili.com/favicon.ico // @run-at document-end // @grant GM_addStyle // @grant GM_getValue // @grant GM_setValue // @grant GM_registerMenuCommand // @noframes // ==/UserScript== (async () => { 'use strict' const styles = { home: `.feed-card, .floor-single-card, .bili-video-card { margin-top: 0px !important; }`, t: `.bili-dyn-home--member { margin: 0 var(--layout-padding) !important; main { flex: 1 } } `, read: `.article-detail { width: 90%; .article-up-info { width: initial; margin: 0 80px 20px; } .right-side-bar { right: 0; } }`, video: `/* 播放器 */ #bilibili-player { position: relative; z-index: 1; width: 100%; height: 100%; } #playerWrap, #bilibili-player-wrap { position: relative; height: 100vh; min-height: 20vh; padding: 0; } /* 小窗 */ .bpx-player-container[data-screen="mini"] { height: auto !important; /* 以视频长宽比为准,且不显示黑边 */ transform: translateY(24px) !important; } .bpx-player-container:not([data-screen="mini"]) { width: 100% !important; } /* 视频标题换行显示 */ #viewbox_report { height: auto; } .video-title { white-space: normal !important; } /* 视频页, 番剧页, 收藏(包括稍后再看)页 下方容器 */ .video-container-v1, .main-container, .playlist-container { z-index: 0; padding: 0 var(--layout-padding); } .left-container, .plp-l, .playlist-container--left { flex: 1; } .plp-r { padding-top: 0 !important; } /* 番剧/影视页下方容器 */ .main-container { width: 100%; margin: 0; padding: 15px 50px 15px 25px !important; box-sizing: border-box; display: flex; } .player-left-components { padding-right: 30px !important; } .toolbar { padding-top: 0; } /* 播放器控件 */ .bpx-player-top-left-title, .bpx-player-top-left-music { display: block !important; } .bpx-player-control-bottom { padding: 0 24px; } .bpx-player-control-bottom-left, .bpx-player-control-bottom-right, .bpx-player-sending-bar, .be-video-control-bar-extend { gap: 12px; >:not(.bpx-player-ctrl-viewpoint, .bpx-player-ctrl-time) { width: auto !important; } } .bpx-player-ctrl-viewpoint { margin: 0; } .bpx-player-control-bottom-center { padding: 0 !important; display: flex; justify-content: center; } .bpx-player-sending-bar { margin: 0 !important; } .bpx-player-control-bottom-right { min-width: initial !important; } .bpx-player-ctrl-time-label { text-align: center !important; text-indent: 0 !important; } .bpx-player-ctrl-time, .bpx-player-ctrl-quality { margin-right: 0 !important; } .bpx-player-video-info { margin-right: 0 !important; display: flex !important; } /* 右下方浮动按钮 */ div[class^=navTools_floatNav] { z-index: 1 !important; } /* 笔记位移 (不然笔记会超出页面初始范围) */ .note-pc { transform: translate(-12px, 64px); } /* 导航栏 (兼容Bilibili Evolved自定义导航栏) */ #biliMainHeader, .custom-navbar { position: sticky !important; top: 0; z-index: 3 !important; } #biliMainHeader > .bili-header { min-height: 0 !important; } .bili-header__bar { position: relative !important; } /* Bilibili Evolved 夜间模式修正 */ .bpx-player-container .bpx-player-sending-bar { background-color: transparent !important; } .bpx-player-container .bpx-player-video-info { color: hsla(0,0%,100%,.9) !important; } .bpx-player-container .bpx-player-sending-bar .bpx-player-video-btn-dm, .bpx-player-container .bpx-player-sending-bar .bpx-player-dm-setting, .bpx-player-container .bpx-player-sending-bar .bpx-player-dm-switch { fill: hsla(0,0%,100%,.9) !important; } /* Bilibili Evolved侧栏 */ .be-settings { z-index: 3; position: fixed; }`, mini: `.bpx-player-container { min-width: 180px; } .bpx-player-mini-resizer { position: absolute; left: 0; width: 10px; height: 100%; cursor: ew-resize; }`, general: `:root { --layout-padding: ${GM_getValue('左右边距', 30)}px; } /* 搜索栏 */ .center-search-container { min-width: 0; } .nav-search-input { padding-right: 0; } .nav-search-clean { display: none; } /* 脚本设置样式 */ #WBOptions { position: fixed; left: 50%; top: 50%; transform: translate(-50%, -50%); z-index: 114514; border-radius: 15px; padding: 20px; display: none; grid-template-columns: repeat(2, 1fr); gap: 20px 30px; background-color: var(--bg1); color: var(--text1); box-shadow: 0 0 4px #00a0d8; font-size: 18px; } #WBOptionsClose { position: absolute; border: none; right: 0; font-size: 30px; line-height: 30px; width: 30px; border-top-right-radius: 15px; border-bottom-left-radius: 5px; transition: .1s; background-color: transparent; color: var(--text1); } #WBOptionsClose:hover { background-color: #e81123; } #WBOptionsClose:active { opacity: 0.5; } #WBOptions>header { grid-column: 1/-1; } #WBOptions>label { align-items: center; display: flex; gap: 10px; } #WBOptions input { height: 20px; margin: 0; padding: 4px !important; box-sizing: content-box !important; } .slider::before { content: ""; display: block; position: relative; height: 100%; aspect-ratio: 1/1; border-radius: 50%; background-color: #fff; transition: .4s; } .slider { appearance: none; width: 40px; border-radius: 20px; box-sizing: content-box; cursor: pointer; background-color: #ccc; transition: .4s; } .slider:checked::before { transform: translateX(20px); } .slider:checked { background-color: #00a0d8; } .slider:hover { box-shadow: 0 0 4px #00a0d8; } .slider:active { opacity: 0.5; } #WBOptions input[type=number] { width: 60px; border: none; border-radius: 5px; background: transparent; box-shadow: 0 0 0 2px #00a0d8; color: var(--text1) !important; }`} GM_addStyle(styles.general) /** @type Map<string, Function> */ const callbacks = new Map() callbacks.set('左右边距', (padding=30) => { document.documentElement.style.setProperty('--layout-padding', `${padding}px`) GM_setValue('左右边距', padding) }) // 设置选项功能 const options = document.body.appendChild(document.createElement('div')) options.id = 'WBOptions' { const commentPadding = GM_getValue('左右边距') options.innerHTML =`<button id="WBOptionsClose">×</button> <header>⚙️宽屏选项</header> <label><input type="checkbox" class="slider"${GM_getValue('导航栏下置', true) ? ' checked': ''}>导航栏下置</label> <label><input type="number" placeholder="px"${commentPadding ? ` value=${commentPadding}` : ''}>左右边距</label>` } GM_registerMenuCommand('选项', () => { options.style.display = 'grid' }) options.getElementsByTagName('button')[0]?.addEventListener('click', () => { options.style.display = 'none' }) for (const Element of options.children) { if (Element instanceof HTMLLabelElement) { const input = Element.getElementsByTagName('input')[0] const key = Element.textContent ?? '' switch (input?.type) { case null: default: console.error('啊?') break case 'checkbox': input.onchange = () => { const callback = callbacks.get(key) callback && callback(input.checked) GM_setValue(key, input.checked) } break case 'number': input.oninput = () => { const val = Number(input.value) if (Number.isInteger(val)) { const callback = callbacks.get(key) callback && callback(val) GM_setValue(key, val) } } break } } } // 每一定时间检测某个条件是否满足,超时则reject const waitFor = (/** @type {() => boolean}*/ loaded, retry = 100, interval = 100) => new Promise((resolve, reject) => { const intervalID = setInterval(() => { if (--retry == 0) { console.error('页面加载超时') clearInterval(intervalID) return reject() } if (loaded()) { clearInterval(intervalID) return resolve(null) } if (retry % 10 == 0) { console.debug('等待页面加载') } }, interval) }) const url = new URL(window.location.href) switch (url.host) { case 't.bilibili.com': GM_addStyle(styles.t) console.info('使用动态样式') break case 'www.bilibili.com': { // #region 首页 if (document.getElementById('i_cecream')) { GM_addStyle(styles.home) console.info('使用首页宽屏样式') break } // #endregion // #region 阅读页 if (document.getElementsByClassName('article-detail')[0]) { GM_addStyle(styles.read) console.info('使用阅读页宽屏样式') break } // #endregion // #region 播放页 // 播放器不存在时不执行 const player = document.getElementById('bilibili-player') if (!player) { return console.info('未找到播放器,不启用宽屏模式') } // 主容器,视频播放页为#app,番剧/影视播放页为.home-container const home = document.getElementById('app') ?? document.getElementsByClassName('home-container')[0] // 播放器外容器,视频播放页为#playerWrap,番剧/影视播放页为#bilibili-player-wrap const wrap = document.getElementById('playerWrap') ?? document.getElementById('bilibili-player-wrap') // 在新版本页面,播放器存在时都应该存在 if (!wrap || !home) { return console.error( `页面加载错误:${[ wrap ? '' : '播放器外容器', home ? '' : '主容器', ].filter(Boolean).join(', ')},请检查是否为新版页面` ) } // 等待人数加载 const b = player.getElementsByTagName('b') await waitFor(() => b[0]?.textContent != null) await waitFor(() => b[0]?.textContent != '-') // 导航栏 (兼容Bilibili Evolved自定义顶栏,有可能延后加载) const navigation = await (async () => { const header = document.getElementById('biliMainHeader') if (header) { header.style.setProperty('height', 'initial', 'important') // 将导航栏移至主容器最前 home.insertAdjacentElement('afterbegin', header) // bili-header__bar不可见时使用自定义顶栏 const headerBar = header.getElementsByClassName('bili-header__bar')[0] if (headerBar && window.getComputedStyle(headerBar).display == 'none') { const navbar = document.getElementsByClassName('custom-navbar') await waitFor(() => Boolean(navbar[0])) return home.insertAdjacentElement('afterbegin', navbar[0]) } } return header })() // 播放器内容器 const container = player.getElementsByClassName('bpx-player-container')[0] // 播放器底中部框 (用于放置弹幕框内容) const bottomCenter = (() => { const center = player.getElementsByClassName('bpx-player-control-bottom-center')[0] // 番剧版使用squirtle-controller-wrap-center,但也存在bpx-player-control-bottom-center // 所以通过检测前一个元素(bpx-player-control-bottom-left)是否有子元素来判断使用哪个 return center?.previousElementSibling?.hasChildNodes() ? center : player.getElementsByClassName('squirtle-controller-wrap-center')[0] })() // 弹幕框 const danmaku = player.getElementsByClassName('bpx-player-sending-bar')[0] // 正常情况应该都存在 if (!navigation || !(container instanceof HTMLDivElement) || !bottomCenter || !danmaku) { return console.error( `页面加载错误:${[ navigation ? '' : '导航栏', container ? '' : '播放器内容器', bottomCenter ? '' : '播放器底中部框', danmaku ? '' : '弹幕框', ].filter(Boolean).join(', ')}` ) } // 改变导航栏位置,true为视频下方,false为视频上方,默认为下方 const lowerNavigation = (value = true) => { if (value) { wrap.style.removeProperty('height') navigation.insertAdjacentElement('beforebegin', wrap) } else { wrap.style.height = `calc(100vh - ${navigation.clientHeight}px)` navigation.insertAdjacentElement('afterend', wrap) } return value } lowerNavigation(GM_getValue('导航栏下置', true)) callbacks.set('导航栏下置', lowerNavigation) // 使用宽屏样式 (除非当前是小窗模式) if (container.getAttribute('data-screen') != 'mini') { container.setAttribute('data-screen', 'web') } // 重载container的setAttribute:data-screen被设置为mini(小窗)以外的值时将其设置为web(宽屏) const setAttributeContainer = container.setAttribute.bind(container) container.setAttribute = (name, value) => setAttributeContainer(name, name == 'data-screen' && value != 'mini' ? 'web' : value) // 番剧页面需要初始与退出全屏时移除#bilibili-player-wrap的class if (wrap.id == 'bilibili-player-wrap') { wrap.className = '' document.addEventListener('fullscreenchange', () => { document.fullscreenElement ?? (wrap.className = '') } ) } // 退出全屏时弹幕框移至播放器下方 document.addEventListener('fullscreenchange', () => { document.fullscreenElement ?? bottomCenter.replaceChildren(danmaku) } ) // 移除原 宽屏/网页全屏 按钮,因为没有用了 for (const className of [ 'bpx-player-ctrl-wide', 'bpx-player-ctrl-web', 'squirtle-widescreen-wrap', 'squirtle-pagefullscreen-wrap', ]) { player.getElementsByClassName(className)[0]?.remove() } // 添加视频样式 GM_addStyle(styles.video) // 将弹幕框移至播放器下方一次 bottomCenter.replaceChildren(danmaku) // 将笔记移至主容器,不然会被视频和导航栏遮挡 const note = document.getElementsByClassName('note-pc')[0] if (note) { navigation.insertAdjacentElement('afterend', note) } console.info('宽屏模式成功启用') // #region 小窗 GM_addStyle(styles.mini) const miniResizer = document.createElement('div') miniResizer.className = 'bpx-player-mini-resizer' miniResizer.onmousedown = (ev) => { ev.stopImmediatePropagation() ev.preventDefault() const resize = (/** @type MouseEvent */ ev) => { container.style.width = `${container.offsetWidth + container.offsetLeft - ev.x + 1}px` } document.addEventListener('mousemove', resize) document.addEventListener('mouseup', () => document.removeEventListener('mousemove', resize), {once: true}) } container.getElementsByClassName('bpx-player-mini-warp')[0]?.appendChild(miniResizer) ?? ( container.getElementsByClassName('bpx-player-video-area')[0] && new MutationObserver((mutations, observer) => { mutations.filter(mutation => mutation.type == 'childList').forEach(mutation => { mutation.addedNodes.forEach(node => { if (node instanceof Element && node.classList.contains('bpx-player-mini-warp')) { node.appendChild(miniResizer) observer.disconnect() } }) }) } ).observe(container.getElementsByClassName('bpx-player-video-area')[0], {childList: true}) ) // #endregion break // #endregion } default: console.info('未知页面') break } })()