您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Universal smooth scrolling for mouse wheel only. Touchpad uses native scrolling.
当前为
- // ==UserScript==
- // @name Smooth Scroll
- // @description Universal smooth scrolling for mouse wheel only. Touchpad uses native scrolling.
- // @author DXRK1E
- // @icon https://i.imgur.com/IAwk6NN.png
- // @include *
- // @exclude https://www.youtube.com/*
- // @exclude https://mail.google.com/*
- // @version 2.3
- // @namespace sttb-dxrk1e
- // @license MIT
- // ==/UserScript==
- (function() {
- 'use strict';
- class SmoothScroll {
- constructor() {
- this.config = {
- smoothness: 0.8, // Increased for more stability
- acceleration: 0.25, // Reduced to prevent jumps
- minDelta: 0.5, // Increased minimum threshold
- maxRefreshRate: 144, // Reduced max refresh rate
- minRefreshRate: 30,
- defaultRefreshRate: 60,
- debug: false
- };
- this.state = {
- isLoaded: false,
- lastFrameTime: 0,
- lastWheelTime: 0,
- lastDelta: 0,
- scrollHistory: [],
- activeScrollElements: new WeakMap()
- };
- this.handleWheel = this.handleWheel.bind(this);
- this.handleClick = this.handleClick.bind(this);
- this.animateScroll = this.animateScroll.bind(this);
- this.detectScrollDevice = this.detectScrollDevice.bind(this);
- }
- init() {
- if (window.top !== window.self || this.state.isLoaded) {
- return;
- }
- if (!window.requestAnimationFrame) {
- window.requestAnimationFrame =
- window.mozRequestAnimationFrame ||
- window.webkitRequestAnimationFrame ||
- window.msRequestAnimationFrame ||
- ((cb) => setTimeout(cb, 1000 / 60));
- }
- document.addEventListener('wheel', this.handleWheel, {
- passive: false,
- capture: true
- });
- document.addEventListener('mousedown', this.handleClick, true);
- document.addEventListener('touchstart', this.handleClick, true);
- document.addEventListener('visibilitychange', () => {
- if (document.hidden) {
- this.clearAllScrolls();
- }
- });
- this.state.isLoaded = true;
- this.log('Smooth Scroll Activated (Mouse Only)');
- }
- detectScrollDevice(event) {
- const now = performance.now();
- const timeDelta = now - this.state.lastWheelTime;
- // Update scroll history for better detection
- this.state.scrollHistory.push({
- delta: event.deltaY,
- time: now,
- mode: event.deltaMode
- });
- // Keep only last 5 events
- if (this.state.scrollHistory.length > 5) {
- this.state.scrollHistory.shift();
- }
- // Analyze scroll pattern
- const isConsistent = this.analyzeScrollPattern();
- // More accurate touchpad detection
- const isTouchpad = (
- (Math.abs(event.deltaY) < 5 && event.deltaMode === 0) || // Very small precise deltas
- (timeDelta < 32 && this.state.scrollHistory.length > 2) || // Rapid small movements
- !isConsistent || // Inconsistent scroll pattern
- (event.deltaMode === 0 && !Number.isInteger(event.deltaY)) // Fractional pixels
- );
- this.state.lastWheelTime = now;
- this.state.lastDelta = event.deltaY;
- return isTouchpad;
- }
- analyzeScrollPattern() {
- if (this.state.scrollHistory.length < 3) return true;
- const deltas = this.state.scrollHistory.map(entry => entry.delta);
- const avgDelta = deltas.reduce((a, b) => a + Math.abs(b), 0) / deltas.length;
- // Check if deltas are relatively consistent (characteristic of mouse wheels)
- return deltas.every(delta =>
- Math.abs(Math.abs(delta) - avgDelta) < avgDelta * 0.5
- );
- }
- log(...args) {
- if (this.config.debug) {
- console.log('[Smooth Scroll]', ...args);
- }
- }
- getCurrentRefreshRate(timestamp) {
- const frameTime = timestamp - (this.state.lastFrameTime || timestamp);
- this.state.lastFrameTime = timestamp;
- const fps = 1000 / Math.max(frameTime, 1);
- return Math.min(
- Math.max(fps, this.config.minRefreshRate),
- this.config.maxRefreshRate
- );
- }
- getScrollableParents(element, direction) {
- const scrollables = [];
- while (element && element !== document.body) {
- if (this.isScrollable(element, direction)) {
- scrollables.push(element);
- }
- element = element.parentElement;
- }
- if (this.isScrollable(document.body, direction)) {
- scrollables.push(document.body);
- }
- return scrollables;
- }
- isScrollable(element, direction) {
- if (!element || element === window || element === document) {
- return false;
- }
- const style = window.getComputedStyle(element);
- const overflowY = style['overflow-y'];
- if (overflowY === 'hidden' || overflowY === 'visible') {
- return false;
- }
- const scrollTop = element.scrollTop;
- const scrollHeight = element.scrollHeight;
- const clientHeight = element.clientHeight;
- return direction < 0 ?
- scrollTop > 0 :
- Math.ceil(scrollTop + clientHeight) < scrollHeight;
- }
- handleWheel(event) {
- if (event.defaultPrevented || window.getSelection().toString()) {
- return;
- }
- // If using touchpad, let native scrolling handle it
- if (this.detectScrollDevice(event)) {
- return;
- }
- const scrollables = this.getScrollableParents(event.target, Math.sign(event.deltaY));
- if (!scrollables.length) {
- return;
- }
- const target = scrollables[0];
- let delta = event.deltaY;
- // Normalize delta based on mode
- if (event.deltaMode === 1) { // LINE mode
- const lineHeight = parseInt(getComputedStyle(target).lineHeight) || 20;
- delta *= lineHeight;
- } else if (event.deltaMode === 2) { // PAGE mode
- delta *= target.clientHeight;
- }
- // Apply a more consistent delta transformation
- delta = Math.sign(delta) * Math.sqrt(Math.abs(delta)) * 10;
- this.scroll(target, delta);
- event.preventDefault();
- }
- handleClick(event) {
- const elements = this.getScrollableParents(event.target, 0);
- elements.forEach(element => this.stopScroll(element));
- }
- scroll(element, delta) {
- if (!this.state.activeScrollElements.has(element)) {
- this.state.activeScrollElements.set(element, {
- pixels: 0,
- subpixels: 0,
- direction: Math.sign(delta)
- });
- }
- const scrollData = this.state.activeScrollElements.get(element);
- // Only accumulate scroll if in same direction or very small remaining scroll
- if (Math.sign(delta) === scrollData.direction || Math.abs(scrollData.pixels) < 1) {
- const acceleration = Math.min(
- 1 + (Math.abs(scrollData.pixels) * this.config.acceleration),
- 2
- );
- scrollData.pixels += delta * acceleration;
- scrollData.direction = Math.sign(delta);
- } else {
- // If direction changed, reset acceleration
- scrollData.pixels = delta;
- scrollData.direction = Math.sign(delta);
- }
- if (!scrollData.animating) {
- scrollData.animating = true;
- this.animateScroll(element);
- }
- }
- stopScroll(element) {
- if (this.state.activeScrollElements.has(element)) {
- const scrollData = this.state.activeScrollElements.get(element);
- scrollData.pixels = 0;
- scrollData.subpixels = 0;
- scrollData.animating = false;
- }
- }
- clearAllScrolls() {
- this.state.activeScrollElements = new WeakMap();
- }
- animateScroll(element) {
- if (!this.state.activeScrollElements.has(element)) {
- return;
- }
- const scrollData = this.state.activeScrollElements.get(element);
- if (Math.abs(scrollData.pixels) < this.config.minDelta) {
- scrollData.animating = false;
- return;
- }
- requestAnimationFrame((timestamp) => {
- const refreshRate = this.getCurrentRefreshRate(timestamp);
- const smoothnessFactor = Math.pow(refreshRate, -1 / (refreshRate * this.config.smoothness));
- // More stable scroll amount calculation
- const scrollAmount = scrollData.pixels * (1 - smoothnessFactor);
- const integerPart = Math.trunc(scrollAmount);
- // Accumulate subpixels more accurately
- scrollData.subpixels += (scrollAmount - integerPart);
- let additionalPixels = Math.trunc(scrollData.subpixels);
- scrollData.subpixels -= additionalPixels;
- const totalScroll = integerPart + additionalPixels;
- // Only update if we have a meaningful scroll amount
- if (Math.abs(totalScroll) >= 1) {
- scrollData.pixels -= totalScroll;
- try {
- element.scrollTop += totalScroll;
- } catch (error) {
- this.log('Scroll error:', error);
- this.stopScroll(element);
- return;
- }
- }
- if (scrollData.animating) {
- this.animateScroll(element);
- }
- });
- }
- }
- // Initialize
- const smoothScroll = new SmoothScroll();
- smoothScroll.init();
- })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址