Disable animations

Speed up browsing by reducing animation and transition delays

Tendrás que instalar una extensión para tu navegador como Tampermonkey, Greasemonkey o Violentmonkey si quieres utilizar este script.

You will need to install an extension such as Tampermonkey to install this script.

Tendrás que instalar una extensión como Tampermonkey o Violentmonkey para instalar este script.

Necesitarás instalar una extensión como Tampermonkey o Userscripts para instalar este script.

Tendrás que instalar una extensión como Tampermonkey antes de poder instalar este script.

Necesitarás instalar una extensión para administrar scripts de usuario si quieres instalar este script.

(Ya tengo un administrador de scripts de usuario, déjame instalarlo)

Tendrás que instalar una extensión como Stylus antes de poder instalar este script.

Tendrás que instalar una extensión como Stylus antes de poder instalar este script.

Tendrás que instalar una extensión como Stylus antes de poder instalar este script.

Para poder instalar esto tendrás que instalar primero una extensión de estilos de usuario.

Para poder instalar esto tendrás que instalar primero una extensión de estilos de usuario.

Para poder instalar esto tendrás que instalar primero una extensión de estilos de usuario.

(Ya tengo un administrador de estilos de usuario, déjame instalarlo)

// ==UserScript==
// @name        Disable animations
// @description Speed up browsing by reducing animation and transition delays
// @version     2026.04.12
// @author      Claude Opus 4.6
// @grant       none
// @inject-into auto
// @run-at      document-start
// @match       *://*/*
// @namespace https://greasyfork.org/users/1519047
// ==/UserScript==

(function() {
    'use strict';

    var TRANSITION_CAP_S = 0.01;
    var ANIMATION_CAP_S = 0.001;

    // --- Patch Web Animations API (Element.animate) ---
    var origAnimate = Element.prototype.animate;
    if (origAnimate) {
        Element.prototype.animate = function(keyframes, options) {
            if (typeof options === 'number') {
                options = 1;
            } else if (options && typeof options === 'object') {
                if (options.duration > 1) options.duration = 1;
                if (options.delay) options.delay = 0;
                if (options.iterations === Infinity) options.iterations = 1;
            }
            return origAnimate.call(this, keyframes, options);
        };
    }

    // --- Duration clamping utility ---
    var clampDurations = function(raw, cap) {
        var parts = raw.split(',');
        var changed = false;
        var clamped = parts.map(function(part) {
            var val = parseFloat(part);
            if (val <= 0 || isNaN(val)) return part.trim();
            changed = true;
            return Math.min(val, cap) + 's';
        });
        return changed ? clamped.join(', ') : null;
    };

    // --- Check if any value in a comma-separated list is "infinite" or a large number ---
    // Threshold of 2 catches "infinite" and high iteration counts while allowing
    // deliberate 1- or 2-iteration animations (e.g. attention pulses) to complete normally.
    var hasInfiniteIteration = function(raw) {
        var parts = raw.split(',');
        for (var i = 0; i < parts.length; i++) {
            var val = parts[i].trim();
            if (val === 'infinite') return true;
            var num = parseFloat(val);
            if (!isNaN(num) && num > 2) return true;
        }
        return false;
    };

    // --- RAF batching for processElement to avoid layout thrashing ---
    var pendingSet = new Set();
    var pendingList = [];
    var rafScheduled = false;
    var processing = false;

    var scheduleProcess = function(el) {
        if (pendingSet.has(el)) return;
        pendingSet.add(el);
        pendingList.push(el);
        if (!rafScheduled) {
            rafScheduled = true;
            requestAnimationFrame(function() {
                processing = true;
                for (var i = 0; i < pendingList.length; i++) {
                    processElement(pendingList[i]);
                }
                processing = false;
                pendingSet.clear();
                pendingList.length = 0;
                rafScheduled = false;
            });
        }
    };

    var scheduleTree = function(root) {
        if (root.nodeType !== 1) return;
        scheduleProcess(root);
        if (root.querySelectorAll) {
            var nodes = root.querySelectorAll('*');
            for (var i = 0; i < nodes.length; i++) {
                scheduleProcess(nodes[i]);
            }
        }
    };

    // --- Process a single element ---
    var processElement = function(el) {
        if (el.nodeType !== 1) return;

        var computed = getComputedStyle(el);

        // Clamp transition durations
        var tDur = computed.transitionDuration;
        if (tDur && tDur !== '0s') {
            var tClamped = clampDurations(tDur, TRANSITION_CAP_S);
            if (tClamped) {
                el.style.setProperty('transition-duration', tClamped, 'important');
                el.style.setProperty('transition-delay', '0s', 'important');
            }
        }

        // Clamp animation durations and stop infinite loops
        var aDur = computed.animationDuration;
        if (aDur && aDur !== '0s') {
            var aClamped = clampDurations(aDur, ANIMATION_CAP_S);
            if (aClamped) {
                el.style.setProperty('animation-duration', aClamped, 'important');
                el.style.setProperty('animation-delay', '0s', 'important');

                // Only force iteration-count and fill-mode on infinite/looping animations
                var iterCount = computed.animationIterationCount;
                if (iterCount && hasInfiniteIteration(iterCount)) {
                    el.style.setProperty('animation-iteration-count', '1', 'important');
                    el.style.setProperty('animation-fill-mode', 'both', 'important');
                }
            }
        }
    };

    // --- Initial synchronous pass + observer ---
    var processTree = function(root) {
        processElement(root);
        if (root.querySelectorAll) {
            var nodes = root.querySelectorAll('*');
            for (var i = 0; i < nodes.length; i++) {
                processElement(nodes[i]);
            }
        }
    };

    var startObserver = function() {
        // Synchronous first pass so existing elements are clamped immediately
        processTree(document.documentElement);

        new MutationObserver(function(mutations) {
            for (var i = 0; i < mutations.length; i++) {
                var mutation = mutations[i];
                if (mutation.type === 'childList') {
                    var added = mutation.addedNodes;
                    for (var j = 0; j < added.length; j++) {
                        if (added[j].nodeType === 1) scheduleTree(added[j]);
                    }
                } else if (mutation.type === 'attributes' && !processing) {
                    // Re-process elements whose class or style changed,
                    // since new animations/transitions may have been applied.
                    // Guard with `processing` to avoid infinite loops from
                    // our own style.setProperty calls triggering style mutations.
                    scheduleProcess(mutation.target);
                }
            }
        }).observe(document.documentElement, {
            childList: true,
            subtree: true,
            attributes: true,
            attributeFilter: ['class', 'style']
        });
    };

    // --- jQuery patching ---
    var patchjQuery = function(jq) {
        if (!jq || !jq.fx || jq.fx._animDisablerPatched) return;
        jq.fx._animDisablerPatched = true;

        jq.fx.speeds = { slow: 1, fast: 1, _default: 1 };

        if (jq.fn && jq.fn.animate && !jq.fn.animate._patched) {
            var origAnim = jq.fn.animate;
            jq.fn.animate = function(prop, speed) {
                var args = Array.prototype.slice.call(arguments);
                if (typeof speed === 'object' && speed !== null) {
                    args[1].duration = 1;
                } else {
                    args[1] = 1;
                }
                return origAnim.apply(this, args);
            };
            jq.fn.animate._patched = true;
        }

        ['fadeTo', 'fadeIn', 'fadeOut', 'fadeToggle',
         'slideUp', 'slideDown', 'slideToggle'].forEach(function(method) {
            if (jq.fn[method] && !jq.fn[method]._patched) {
                var orig = jq.fn[method];
                jq.fn[method] = function() {
                    var args = Array.prototype.slice.call(arguments);
                    args[0] = 1;
                    return orig.apply(this, args);
                };
                jq.fn[method]._patched = true;
            }
        });
    };

    // --- Patch GSAP if present ---
    var patchGSAP = function() {
        try {
            if (window.gsap) {
                window.gsap.globalTimeline.timeScale(1000);
            } else if (window.TweenMax) {
                window.TweenMax.globalTimeScale(1000);
            }
            if (window.wrappedJSObject) {
                if (window.wrappedJSObject.gsap) {
                    window.wrappedJSObject.gsap.globalTimeline.timeScale(1000);
                } else if (window.wrappedJSObject.TweenMax) {
                    window.wrappedJSObject.TweenMax.globalTimeScale(1000);
                }
            }
        } catch (e) {
            console.debug('Animation disabler (GSAP):', e);
        }
    };

    var disableJSAnimations = function() {
        try {
            [window.jQuery, window.$,
             window.wrappedJSObject?.jQuery,
             window.wrappedJSObject?.$].forEach(patchjQuery);

            if (window.Velocity) window.Velocity.mock = true;
            if (window.wrappedJSObject?.Velocity) window.wrappedJSObject.Velocity.mock = true;

            patchGSAP();

            if (!window.jQuery && !window.$) {
                var check = setInterval(function() {
                    if (window.jQuery || window.$) {
                        patchjQuery(window.jQuery);
                        patchjQuery(window.$);
                        clearInterval(check);
                    }
                }, 100);
                setTimeout(function() { clearInterval(check); }, 10000);
            }
        } catch (e) {
            console.debug('Animation disabler:', e);
        }
    };

    // --- Initialization ---
    var init = function() {
        startObserver();
        disableJSAnimations();
    };

    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', init);
    } else {
        init();
    }

    // Re-check JS animation libraries after full page load,
    // as some are loaded asynchronously via script tags.
    window.addEventListener('load', disableJSAnimations);
})();