Amazon Page Smoother

Performance optimization for Amazon: background throttling, lazy loading, and layout stabilization.

当前为 2025-11-25 提交的版本,查看 最新版本

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         Amazon Page Smoother
// @namespace    http://tampermonkey.net/
// @version      0.0.9
// @description  Performance optimization for Amazon: background throttling, lazy loading, and layout stabilization.
// @license      Unlicense
// @author       VeleSila
// @match        https://www.amazon.com/*
// @match        https://www.amazon.co.jp/*
// @match        https://www.amazon.co.uk/*
// @match        https://www.amazon.es/*
// @match        https://www.amazon.fr/*
// @match        https://www.amazon.de/*
// @match        https://www.amazon.it/*
// @match        https://www.amazon.ca/*
// @match        https://www.amazon.com.au/*
// @exclude      */cart/*
// @exclude      */buy/*
// @exclude      */checkout/*
// @exclude      */gp/buy/*
// @grant        GM_addStyle
// @run-at       document-start
// ==/UserScript==

(function() {
    'use strict';

    // --- Configuration ---
    const CONFIG = {
        // Minimum interval delay when tab is hidden (ms)
        BACKGROUND_TICK_RATE_MS: 2000,
        // Pause animations when tab is hidden
        DISABLE_ANIMATION_IN_BACKGROUND: true,
        // Use CSS containment for rendering optimization
        ENABLE_CSS_CONTAINMENT: true,
        // Delay for debouncing mutation observer (ms)
        DEBOUNCE_DELAY_MS: 300,
        // Pixel height to consider "above the fold" for priority loading
        CRITICAL_VIEWPORT_HEIGHT: 1200
    };

    const SUPPORTS = {
        fetchPriority: 'fetchPriority' in HTMLImageElement.prototype,
        requestIdleCallback: typeof window.requestIdleCallback === 'function',
        closest: typeof Element.prototype.closest === 'function'
    };

    /**
     * 1. Critical Safety Check
     * Exit immediately on sensitive pages (checkout, payments, etc).
     */
    const sensitivePaths = /(checkout|signin|payment|addressselect|huc)/i;
    if (sensitivePaths.test(window.location.pathname)) {
        console.log('[Amazon Smoother] Sensitive page detected. Script disabled.');
        return;
    }

    /**
     * 2. CSS Layout Optimizer
     * Uses CSS Containment to prevent rendering off-screen content.
     */
    function injectOptimizedStyles() {
        if (!CONFIG.ENABLE_CSS_CONTAINMENT) return;

        const css = `
            /* Optimize Search Results */
            .s-main-slot .s-result-item {
                content-visibility: auto;
                contain-intrinsic-size: 1px 300px;
            }
            /* Optimize Carousels (viewport only) */
            .a-carousel-viewport {
                content-visibility: auto;
                contain-intrinsic-size: 1px 250px;
            }
            /* Defer Footer */
            #navFooter, .nav-footer-line {
                content-visibility: auto;
                contain-intrinsic-size: 1px 500px;
            }
            /* Reduce Spinner CPU Usage */
            .a-spinner-wrapper, .loading-spinner {
                will-change: transform, opacity;
            }
            /* GPU Acceleration for Search Images */
            img.s-image {
                transform: translateZ(0);
            }
        `;

        if (typeof GM_addStyle === 'function') {
            GM_addStyle(css);
        } else {
            const style = document.createElement('style');
            style.textContent = css;
            document.head.appendChild(style);
        }
    }

    /**
     * 3. Background Throttler
     * Wraps timing functions to throttle them dynamically based on visibility state.
     * FIXED: Removed the logic that deleted the wrappers on visibility change.
     */
    function initializeThrottler() {
        if (window.self !== window.top) return; // Skip iframes

        const originalSetInterval = window.setInterval;
        const originalRequestAnimationFrame = window.requestAnimationFrame;

        // Throttle Intervals
        window.setInterval = function(callback, delay, ...args) {
            // If hidden and delay is aggressive, force it to be slower
            if (document.hidden && delay < CONFIG.BACKGROUND_TICK_RATE_MS) {
                return originalSetInterval(callback, CONFIG.BACKGROUND_TICK_RATE_MS, ...args);
            }
            return originalSetInterval(callback, delay, ...args);
        };

        // Throttle Animations (RAF)
        window.requestAnimationFrame = function(callback) {
            if (document.hidden && CONFIG.DISABLE_ANIMATION_IN_BACKGROUND) {
                // Delay execution to 1 second (1 FPS)
                return setTimeout(() => {
                    originalRequestAnimationFrame(callback);
                }, 1000);
            }
            return originalRequestAnimationFrame(callback);
        };
    }

    /**
     * 4. Resource Optimizer
     * Manages lazy loading and fetch priority without expensive DOM queries.
     */
    function optimizeResources(rootNode = document) {
        // Select only unoptimized images
        const images = rootNode.querySelectorAll('img:not([data-optimized])');
        if (images.length === 0) return;

        // Performance: Avoid querying the DOM inside the loop.
        // We use a heuristic: If the image is physically near the top of the page, it's critical.
        
        for (let i = 0, len = images.length; i < len; i++) {
            const img = images[i];
            
            // Mark processed immediately
            img.dataset.optimized = 'true';

            // A. Lazy Loading & Async Decoding
            if (!img.hasAttribute('loading')) img.loading = 'lazy';
            if (!img.hasAttribute('decoding')) img.decoding = 'async';

            // B. Priority Management
            if (SUPPORTS.fetchPriority) {
                // Lower priority for footer
                if (img.closest('#navFooter')) {
                    img.fetchPriority = 'low';
                    continue;
                }

                // High priority for "above the fold" product images
                // We use a loose coordinate check rather than calculating DOM index (much faster)
                if (img.classList.contains('s-image')) {
                     // Check if image is roughly in the top viewport
                    const rect = img.getBoundingClientRect();
                    // If top of image is within defined threshold (e.g., 1200px)
                    const isAboveFold = rect.top < CONFIG.CRITICAL_VIEWPORT_HEIGHT;
                    
                    img.fetchPriority = isAboveFold ? 'high' : 'auto';
                }
            }
        }

        // Optimize Iframes (Ads/Tracking)
        const iframes = rootNode.querySelectorAll('iframe:not([loading])');
        for (let i = 0; i < iframes.length; i++) {
            iframes[i].loading = 'lazy';
        }
    }

    /**
     * 5. Initialization & Observer
     */
    function main() {
        console.log('[Amazon Smoother] Initializing optimized v0.0.9...');
        try {
            injectOptimizedStyles();
            initializeThrottler();
            
            // Initial pass
            if (SUPPORTS.requestIdleCallback) {
                requestIdleCallback(() => optimizeResources(document), { timeout: 2000 });
            } else {
                optimizeResources(document);
            }

            // Debounced Observer
            let debounceTimer;
            const observer = new MutationObserver((mutations) => {
                // Quick check if relevant nodes were added
                let hasNewNodes = false;
                for (const m of mutations) {
                    if (m.addedNodes.length > 0) {
                        hasNewNodes = true;
                        break;
                    }
                }
                if (!hasNewNodes) return;

                clearTimeout(debounceTimer);
                debounceTimer = setTimeout(() => {
                     if (SUPPORTS.requestIdleCallback) {
                        requestIdleCallback(() => optimizeResources(document.body));
                    } else {
                        optimizeResources(document.body);
                    }
                }, CONFIG.DEBOUNCE_DELAY_MS);
            });

            // Observe the main slot if available (better performance), else body
            const targetNode = document.querySelector('.s-main-slot') || document.body;
            
            observer.observe(targetNode, {
                childList: true,
                subtree: true
            });

        } catch (error) {
            console.error('[Amazon Smoother] Initialization failed:', error);
        }
    }

    // Boot
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', main, { once: true });
    } else {
        main();
    }

})();