Greasy Fork is available in English.
单击切换进度条显示/隐藏(显示时3秒后自动隐藏),双击仅播放/暂停,保留长按倍速、左右滑动进度、左右半屏上下滑亮度/音量
当前为
// ==UserScript==
// @name Bilibili Surface
// @namespace http://tampermonkey.net/
// @version 1.0.7
// @description 单击切换进度条显示/隐藏(显示时3秒后自动隐藏),双击仅播放/暂停,保留长按倍速、左右滑动进度、左右半屏上下滑亮度/音量
// @author You
// @match *://*.bilibili.com/*
// @icon https://www.bilibili.com/favicon.ico
// @run-at document-end
// @grant none
// @license MIT
// ==/UserScript==
(function() {
'use strict';
// --- 参数配置 ---
const PRESS_DELAY = 250;
const TARGET_SPEED = 3.0;
const DOUBLE_TAP_DELAY = 250;
const SEEK_SENSITIVITY = 0.2;
const CTRL_SHOW_DURATION = 5000;
// ---------------
let pressTimer = null;
let clickTimer = null;
let clickCount = 0;
let ctrlTimer = null;
let originalSpeed = 1.0;
let gestureType = '';
let isInteracting = false;
let wasPlaying = false;
let startX = 0, startY = 0;
let startVal = 0;
// --- 1. 注入核心 CSS ---
const css = `
/* 强制显示底部控制栏容器 */
.gemini-seeking .bpx-player-control-wrap,
.gemini-seeking .bilibili-player-control-wrap {
opacity: 1 !important;
visibility: visible !important;
transform: none !important;
bottom: 0 !important;
}
/* 强制显示底部阴影遮罩 */
.gemini-seeking .bpx-player-control-mask,
.gemini-seeking .bilibili-player-control-mask {
opacity: 1 !important;
visibility: visible !important;
}
/* 强制显示顶部遮罩 */
.gemini-seeking .bpx-player-top-mask {
opacity: 1 !important;
visibility: visible !important;
}
`;
const style = document.createElement('style');
style.textContent = css;
(document.head || document.documentElement).appendChild(style);
// --- 提示框相关 ---
function formatTime(seconds) {
const m = Math.floor(seconds / 60);
const s = Math.floor(seconds % 60);
return `${m.toString().padStart(2, '0')}:${s.toString().padStart(2, '0')}`;
}
function getToast(playerArea) {
let div = playerArea.querySelector('#gemini-clean-toast');
if (!div) {
div = document.createElement('div');
div.id = 'gemini-clean-toast';
div.style.cssText = `
position: absolute;
top: 20%; left: 50%; transform: translateX(-50%);
padding: 10px 22px;
background: rgba(0,0,0,0.75);
color: #fff;
border-radius: 8px;
font-size: 18px;
font-family: "Segoe UI", sans-serif; font-weight: 500;
z-index: 100001;
pointer-events: none;
display: none;
backdrop-filter: blur(4px);
text-align: center;
white-space: nowrap;
box-shadow: 0 4px 10px rgba(0,0,0,0.3);
`;
playerArea.appendChild(div);
}
return div;
}
function showToast(playerArea, text) {
const div = getToast(playerArea);
div.innerText = text;
div.style.display = 'block';
}
function hideToast(playerArea) {
const div = playerArea.querySelector('#gemini-clean-toast');
if (div) div.style.display = 'none';
}
// --- 核心逻辑 ---
function getPlayerContainer(playerArea) {
return playerArea.closest('.bpx-player-container') ||
playerArea.closest('#bilibili-player') ||
playerArea;
}
function toggleSeekingMode(playerArea, isSeeking) {
const container = getPlayerContainer(playerArea);
if (isSeeking) {
container.classList.add('gemini-seeking');
} else {
container.classList.remove('gemini-seeking');
}
}
function showCtrl(playerArea) {
const container = getPlayerContainer(playerArea);
const progressEntity = container.querySelector('.bpx-player-control-entity');
container.classList.remove('bpx-state-no-cursor');
container.setAttribute('data-ctrl-hidden', 'false');
if (progressEntity) {
progressEntity.setAttribute('data-shadow-show', 'false');
}
// 配合强制显示类,确保视觉层面也显示
toggleSeekingMode(playerArea, true);
}
function hideCtrl(playerArea) {
const container = getPlayerContainer(playerArea);
const progressEntity = container.querySelector('.bpx-player-control-entity');
container.classList.add('bpx-state-no-cursor');
container.setAttribute('data-ctrl-hidden', 'true');
if (progressEntity) {
progressEntity.setAttribute('data-shadow-show', 'true');
}
toggleSeekingMode(playerArea, false);
}
function hideCtrlMenus(playerArea) {
const container = getPlayerContainer(playerArea);
const rightMenus = container.querySelector('.bpx-player-control-bottom-right');
if (!rightMenus) return;
rightMenus.childNodes.forEach(menu => {
if (menu.classList) {
menu.classList.remove('bpx-state-show');
}
});
}
function clearCtrlTimer() {
if (ctrlTimer) {
clearTimeout(ctrlTimer);
ctrlTimer = null;
}
}
function showCtrlForAWhile(playerArea) {
clearCtrlTimer();
showCtrl(playerArea);
ctrlTimer = setTimeout(() => {
hideCtrl(playerArea);
ctrlTimer = null;
}, CTRL_SHOW_DURATION);
}
function hideCtrlNow(playerArea) {
clearCtrlTimer();
hideCtrlMenus(playerArea);
hideCtrl(playerArea);
}
function toggleCtrlWithAutoHide(playerArea) {
const container = getPlayerContainer(playerArea);
const isCtrlHidden = container.getAttribute('data-ctrl-hidden');
// 按代码2的逻辑:
// 当前显示,或者已有自动隐藏计时器 → 直接隐藏
if (isCtrlHidden === 'false' || ctrlTimer) {
hideCtrlNow(playerArea);
} else {
showCtrlForAWhile(playerArea);
}
}
function getCurrentBrightness(video) {
const filter = video.style.filter;
if (!filter || !filter.includes('brightness')) return 100;
const match = filter.match(/brightness\((\d+)%\)/);
return match ? parseInt(match[1], 10) : 100;
}
function createSafeShield(playerArea) {
if (playerArea.querySelector('#gemini-mobile-shield')) return;
console.log('Gemini: 单击切换显示/隐藏进度条 / 双击播放暂停 版已部署');
const shield = document.createElement('div');
shield.id = 'gemini-mobile-shield';
shield.style.cssText = `
position: absolute; top: 0; left: 0; width: 100%; height: 85%;
z-index: 20; background: transparent;
touch-action: none !important; user-select: none;
`;
playerArea.appendChild(shield);
shield.addEventListener('pointerdown', (e) => handleDown(e, playerArea));
shield.addEventListener('pointermove', (e) => handleMove(e, playerArea));
shield.addEventListener('pointerup', (e) => handleUp(e, playerArea));
shield.addEventListener('pointercancel', (e) => handleUp(e, playerArea));
shield.addEventListener('contextmenu', (e) => { e.preventDefault(); e.stopPropagation(); });
}
function handleDown(e, playerArea) {
if (!e.isPrimary || e.button === 2) return;
const video = playerArea.querySelector('video');
if (!video) return;
startX = e.clientX;
startY = e.clientY;
gestureType = '';
isInteracting = false;
originalSpeed = video.playbackRate;
pressTimer = setTimeout(() => {
if (!isInteracting) {
gestureType = 'speed';
isInteracting = true;
video.playbackRate = TARGET_SPEED;
showToast(playerArea, `倍速 ${TARGET_SPEED}x`);
}
}, PRESS_DELAY);
}
function handleMove(e, playerArea) {
if (!startX) return;
const video = playerArea.querySelector('video');
if (!video) return;
const deltaX = e.clientX - startX;
const deltaY = startY - e.clientY;
const absX = Math.abs(deltaX);
const absY = Math.abs(deltaY);
if (!isInteracting && (absX > 15 || absY > 15)) {
if (pressTimer) {
clearTimeout(pressTimer);
pressTimer = null;
}
isInteracting = true;
if (absX > absY) {
gestureType = 'seek';
startVal = video.currentTime;
wasPlaying = !video.paused;
video.pause();
clearCtrlTimer();
showCtrl(playerArea);
} else {
const screenW = window.innerWidth;
if (startX < screenW / 2) {
gestureType = 'brightness';
startVal = getCurrentBrightness(video);
} else {
gestureType = 'volume';
startVal = video.volume;
}
}
}
if (isInteracting) {
if (gestureType === 'seek') {
const seekDelta = deltaX * SEEK_SENSITIVITY;
let targetTime = startVal + seekDelta;
if (targetTime < 0) targetTime = 0;
if (targetTime > video.duration) targetTime = video.duration;
video.currentTime = targetTime;
showToast(playerArea, `${formatTime(targetTime)} / ${formatTime(video.duration)}`);
}
else if (gestureType === 'volume') {
const sensitivity = window.innerHeight * 0.8;
let newVol = startVal + (deltaY / sensitivity);
if (newVol > 1) newVol = 1;
else if (newVol < 0) newVol = 0;
video.volume = newVol;
showToast(playerArea, `音量 ${Math.round(newVol * 100)}%`);
}
else if (gestureType === 'brightness') {
const sensitivity = window.innerHeight * 0.8;
let newBrit = startVal + (deltaY / sensitivity * 100);
if (newBrit > 200) newBrit = 200;
else if (newBrit < 0) newBrit = 0;
video.style.filter = `brightness(${newBrit}%)`;
showToast(playerArea, `亮度 ${Math.round(newBrit)}%`);
}
}
}
function handleUp(e, playerArea) {
if (pressTimer) {
clearTimeout(pressTimer);
pressTimer = null;
}
const video = playerArea.querySelector('video');
if (!video) {
startX = 0;
startY = 0;
return;
}
if (isInteracting) {
if (gestureType === 'speed') {
video.playbackRate = originalSpeed;
}
else if (gestureType === 'seek') {
if (wasPlaying) video.play();
hideCtrl(playerArea);
}
isInteracting = false;
gestureType = '';
setTimeout(() => hideToast(playerArea), 500);
} else {
if (Math.abs(e.clientX - startX) < 10 && Math.abs(e.clientY - startY) < 10) {
clickCount++;
if (clickCount === 1) {
clickTimer = setTimeout(() => {
toggleCtrlWithAutoHide(playerArea);
clickCount = 0;
clickTimer = null;
}, DOUBLE_TAP_DELAY);
} else if (clickCount === 2) {
if (clickTimer) {
clearTimeout(clickTimer);
clickTimer = null;
}
togglePlayOnly(video, playerArea);
clickCount = 0;
}
}
}
startX = 0;
startY = 0;
}
function togglePlayOnly(video, playerArea) {
if (video.paused) {
video.play();
showToast(playerArea, '播放');
} else {
video.pause();
showToast(playerArea, '暂停');
}
setTimeout(() => hideToast(playerArea), 500);
}
function init() {
const targetArea = document.querySelector('.bpx-player-video-area') ||
document.querySelector('.bilibili-player-video-wrap');
if (targetArea) {
createSafeShield(targetArea);
}
}
const observer = new MutationObserver(() => init());
observer.observe(document.body, { childList: true, subtree: true });
window.addEventListener('load', init);
})();