您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
提供九宫格预设速度按钮和拖拽滑块。
// ==UserScript== // @name 视频加速 // @version 7.0.0 // @description 提供九宫格预设速度按钮和拖拽滑块。 // @author kuaisudaohang // @match *://*.bilibili.com/* // @match *://*.youtube.com/* // @match *://v.youku.com/v_show/* // @match *://*.iqiyi.com/* // @match *://v.qq.com/x/cover/* // @match *://*.mgtv.com/* // @match *://tv.sohu.com/* // @match *://*.acfun.cn/v/* // @match *://*.nicovideo.jp/watch/* // @match *://*.dailymotion.com/video/* // @match *://vimeo.com/* // @match *://ani.gamer.com.tw/* // @grant none // @license MIT // @namespace https://greasyfork.org/users/1513773 // ==/UserScript== (function() { 'use strict'; const style = document.createElement("style"); document.head.appendChild(style); const sheet = style.sheet; sheet.insertRule(`.vsc-container { position: fixed; left: 15px; top: 50%; transform: translateY(-50%); z-index: 2147483647; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; }`, 0); sheet.insertRule(`.vsc-icon { width: 42px; height: 42px; background: rgba(28, 28, 30, 0.8); border-radius: 50%; cursor: pointer; display: flex; justify-content: center; align-items: center; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2); transition: all 0.3s ease; backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px); }`, 1); // --- 修改:调整面板宽度以适应九宫格 --- sheet.insertRule(`.vsc-panel { position: absolute; left: 21px; top: 50%; width: 240px; padding: 15px 20px; background: rgba(28, 28, 30, 0.8); backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px); border-radius: 12px; box-shadow: 0 8px 30px rgba(0, 0, 0, 0.3); visibility: hidden; opacity: 0; transform: translateY(-50%) translateX(-20px) scale(0.95); pointer-events: none; transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); color: #f5f5f7; }`, 2); sheet.insertRule(`.vsc-container:hover .vsc-panel { visibility: visible; opacity: 1; transform: translateY(-50%) translateX(0) scale(1); pointer-events: auto; }`, 3); sheet.insertRule(`.vsc-container:hover .vsc-icon { transform: scale(1.1) rotate(90deg); }`, 4); // --- 修改:将预设按钮容器改为九宫格布局 --- sheet.insertRule(`.vsc-options { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; margin-bottom: 20px; }`, 5); sheet.insertRule(`.vsc-btn { background: #3A3A3C; border: none; color: #f5f5f7; padding: 8px 0; border-radius: 8px; cursor: pointer; font-size: 14px; transition: background-color 0.2s; }`, 6); sheet.insertRule(`.vsc-btn:hover { background-color: #555; }`, 7); sheet.insertRule(`.vsc-btn.active { background-color: #007AFF; font-weight: 600; }`, 8); sheet.insertRule(`.vsc-slider-track { position: relative; flex: 1; height: 8px; background-color: rgba(255,255,255,0.2); border-radius: 4px; cursor: pointer; }`, 9); sheet.insertRule(`.vsc-slider-thumb { position: absolute; top: 50%; transform: translateY(-50%); width: 60px; height: 30px; background-color: #007AFF; border-radius: 8px; color: white; font-weight: 600; font-size: 14px; display: flex; justify-content: center; align-items: center; cursor: grab; box-shadow: 0 2px 10px rgba(0,0,0,0.2); user-select: none; -webkit-user-select: none; }`, 10); sheet.insertRule(`.vsc-slider-thumb:active { cursor: grabbing; transform: translateY(-50%) scale(1.1); }`, 11); sheet.insertRule(`.vsc-slider-controls { display: flex; align-items: center; gap: 8px; }`, 12); sheet.insertRule(`.vsc-step-btn { width: 30px; height: 30px; background: #3A3A3C; border: none; color: #f5f5f7; border-radius: 50%; font-size: 22px; line-height: 30px; cursor: pointer; transition: background-color 0.2s; }`, 13); sheet.insertRule(`.vsc-step-btn:hover { background-color: #555; }`, 14); const SPEED_MIN = 0.1; const SPEED_MAX = 16.0; // --- 修改:更新预设速度为9个,以适应九宫格 --- const config = [0.5, 0.75, 1.0, 1.25, 1.5, 2.0, 4.0, 8.0, 16.0]; let mainVideo = null; let curSpeed = localStorage.getItem("videoSpeed") || 1; const iconSVG = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24" fill="white" width="24px" height="24px"><path d="M4 18l8.5-6L4 6v12zm9-12v12l8.5-6L13 6z"/></svg>`; function createSpeedController() { const container = document.createElement('div'); container.className = 'vsc-container'; const icon = document.createElement('div'); icon.className = 'vsc-icon'; icon.innerHTML = iconSVG; const panel = document.createElement('div'); panel.className = 'vsc-panel'; const optionsContainer = document.createElement('div'); optionsContainer.className = 'vsc-options'; config.forEach(speed => { const btn = document.createElement('button'); btn.className = 'vsc-btn'; btn.textContent = `${speed}x`; btn.dataset.speed = speed; optionsContainer.appendChild(btn); }); const sliderControls = document.createElement('div'); sliderControls.className = 'vsc-slider-controls'; const sliderTrack = document.createElement('div'); sliderTrack.className = 'vsc-slider-track'; const sliderThumb = document.createElement('div'); sliderThumb.className = 'vsc-slider-thumb'; sliderTrack.appendChild(sliderThumb); const btnDecrease = document.createElement('button'); btnDecrease.className = 'vsc-step-btn'; btnDecrease.textContent = '-'; const btnIncrease = document.createElement('button'); btnIncrease.className = 'vsc-step-btn'; btnIncrease.textContent = '+'; sliderControls.append(sliderTrack, btnDecrease, btnIncrease); panel.append(optionsContainer, sliderControls); container.append(icon, panel); document.body.appendChild(container); optionsContainer.addEventListener('click', (e) => { if (e.target.matches('.vsc-btn')) { curSpeed = parseFloat(e.target.dataset.speed); setVideoSpeed(curSpeed); } }); sliderThumb.addEventListener('mousedown', onDragStart); sliderTrack.addEventListener('click', onTrackClick); btnDecrease.addEventListener('click', () => handleStepChange(-0.25)); btnIncrease.addEventListener('click', () => handleStepChange(0.25)); updateUI(curSpeed); } function handleStepChange(amount) { let newSpeed = parseFloat(curSpeed) + amount; newSpeed = Math.max(SPEED_MIN, Math.min(newSpeed, SPEED_MAX)); curSpeed = newSpeed; setVideoSpeed(curSpeed); } let isDragging = false; function onDragStart(e) { e.preventDefault(); isDragging = true; document.addEventListener('mousemove', onDragMove); document.addEventListener('mouseup', onDragEnd); } function onDragEnd() { isDragging = false; document.removeEventListener('mousemove', onDragMove); document.removeEventListener('mouseup', onDragEnd); } function onDragMove(e) { if (!isDragging) return; const track = document.querySelector('.vsc-slider-track'); if (!track) return; const rect = track.getBoundingClientRect(); const thumbWidth = document.querySelector('.vsc-slider-thumb').offsetWidth; let positionX = e.clientX - rect.left; positionX = Math.max(thumbWidth / 2, Math.min(positionX, rect.width - thumbWidth / 2)); const percentage = (positionX - thumbWidth / 2) / (rect.width - thumbWidth); curSpeed = SPEED_MIN + percentage * (SPEED_MAX - SPEED_MIN); setVideoSpeed(curSpeed); } function onTrackClick(e) { if (e.target.classList.contains('vsc-slider-thumb')) return; onDragMove(e); } function updateUI(speed) { const speedFloat = parseFloat(speed); const container = document.querySelector('.vsc-container'); if (!container) return; const buttons = container.querySelectorAll('.vsc-btn'); buttons.forEach(btn => { btn.classList.toggle('active', parseFloat(btn.dataset.speed) === speedFloat); }); const track = container.querySelector('.vsc-slider-track'); const thumb = container.querySelector('.vsc-slider-thumb'); if (!track || !thumb) return; thumb.textContent = `${speedFloat.toFixed(2)}x`; requestAnimationFrame(() => { const trackWidth = track.offsetWidth; const thumbWidth = thumb.offsetWidth; if (trackWidth <= 0 || thumbWidth <= 0) return; const percentage = (speedFloat - SPEED_MIN) / (SPEED_MAX - SPEED_MIN); const leftPosition = Math.max(0, Math.min(percentage * (trackWidth - thumbWidth), trackWidth - thumbWidth)); thumb.style.left = `${leftPosition}px`; }); } function setVideoSpeed(speed) { mainVideo = document.querySelector('video'); if (mainVideo) { mainVideo.playbackRate = speed; localStorage.setItem("videoSpeed", speed); updateUI(speed); } } function initSpeedController() { const videoCheckInterval = setInterval(() => { mainVideo = document.querySelector('video'); if (mainVideo) { if (!document.querySelector('.vsc-container')) { createSpeedController(); } setVideoSpeed(curSpeed); clearInterval(videoCheckInterval); } }, 500); } initSpeedController(); })();