Amazon Page Smoother

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

目前為 2025-11-25 提交的版本,檢視 最新版本

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

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

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 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();
    }

})();