设备模拟器

模拟各种设备的 UA 和屏幕参数

// ==UserScript==
// @name         设备模拟器
// @namespace    http://tampermonkey.net/
// @version      1.4
// @description  模拟各种设备的 UA 和屏幕参数
// @author       Your name
// @license      MIT
// @match        *://*/*
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM.setValue
// @grant        GM.getValue
// @run-at       document-start
// ==/UserScript==

(function() {
    'use strict';
    
    // 如果存在保存的设置,立即应用
    const savedSettings = GM_getValue('deviceSettings', null);
    if (savedSettings) {
        try {
            const settings = JSON.parse(savedSettings);
            applySettings(settings);
        } catch (e) {
            console.error('加载初始设置失败:', e);
        }
    }

    // 预设设备列表
    const presetDevices = {
        'iPhone 13': {
            userAgent: 'Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.0 Mobile/15E148 Safari/604.1',
            width: 390,
            height: 844,
            deviceScaleFactor: 3
        },
        'Pixel 5': {
            userAgent: 'Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.91 Mobile Safari/537.36',
            width: 393,
            height: 851,
            deviceScaleFactor: 2.75
        },
        'iPad Pro': {
            userAgent: 'Mozilla/5.0 (iPad; CPU OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.0 Mobile/15E148 Safari/604.1',
            width: 1024,
            height: 1366,
            deviceScaleFactor: 2
        }
    };

    // 创建悬浮窗
    function createFloatingWindow() {
        const container = document.createElement('div');
        container.innerHTML = `
            <div id="device-simulator" style="position: fixed; top: 20px; right: 20px; background: white; border: 1px solid #ccc; padding: 10px; z-index: 9999; box-shadow: 0 0 10px rgba(0,0,0,0.1); min-width: 200px;">
                <div id="simulator-header" style="cursor: move; padding: 5px; background: #f0f0f0; margin-bottom: 10px;">
                    <span>设备模拟器</span>
                    <button id="toggle-simulator" style="float: right;">收起</button>
                </div>
                <div id="simulator-content">
                    <select id="device-select" style="width: 100%; margin-bottom: 10px;">
                        <option value="">选择设备</option>
                        ${Object.keys(presetDevices).map(device => `<option value="${device}">${device}</option>`).join('')}
                    </select>
                    <div style="margin-bottom: 10px;">
                        <label>User Agent:</label>
                        <textarea id="ua-input" style="width: 100%; height: 60px;"></textarea>
                    </div>
                    <div style="margin-bottom: 10px;">
                        <label>屏幕宽度:</label>
                        <input type="number" id="width-input" style="width: 100%;">
                    </div>
                    <div style="margin-bottom: 10px;">
                        <label>屏幕高度:</label>
                        <input type="number" id="height-input" style="width: 100%;">
                    </div>
                    <div style="margin-bottom: 10px;">
                        <label>设备像素比:</label>
                        <input type="number" id="scale-input" style="width: 100%;" step="0.01">
                    </div>
                    <button id="apply-settings" style="width: 100%;">应用设置</button>
                </div>
            </div>
        `;

        document.body.appendChild(container);
        return container;
    }

    // 初始化拖拽功能
    function initializeDrag(container) {
        const header = container.querySelector('#simulator-header');
        let isDragging = false;
        let currentX;
        let currentY;
        let initialX;
        let initialY;

        header.addEventListener('mousedown', (e) => {
            isDragging = true;
            initialX = e.clientX - container.offsetLeft;
            initialY = e.clientY - container.offsetTop;
        });

        document.addEventListener('mousemove', (e) => {
            if (isDragging) {
                currentX = e.clientX - initialX;
                currentY = e.clientY - initialY;
                container.style.left = currentX + 'px';
                container.style.top = currentY + 'px';
            }
        });

        document.addEventListener('mouseup', () => {
            isDragging = false;
        });
    }

    // 初始化事件监听
    function initializeEvents(container) {
        const toggleBtn = container.querySelector('#toggle-simulator');
        const content = container.querySelector('#simulator-content');
        const deviceSelect = container.querySelector('#device-select');
        const uaInput = container.querySelector('#ua-input');
        const widthInput = container.querySelector('#width-input');
        const heightInput = container.querySelector('#height-input');
        const scaleInput = container.querySelector('#scale-input');
        const applyBtn = container.querySelector('#apply-settings');

        // 加载保存的设置
        const savedSettings = GM_getValue('deviceSettings', null);
        if (savedSettings) {
            try {
                const settings = JSON.parse(savedSettings);
                uaInput.value = settings.userAgent || '';
                widthInput.value = settings.width || '';
                heightInput.value = settings.height || '';
                scaleInput.value = settings.deviceScaleFactor || '';
                // 立即应用保存的设置
                applySettings(settings);
            } catch (e) {
                console.error('加载设置失败:', e);
            }
        }

        // 切换显示/隐藏
        toggleBtn.addEventListener('click', () => {
            if (content.style.display === 'none') {
                content.style.display = 'block';
                toggleBtn.textContent = '收起';
            } else {
                content.style.display = 'none';
                toggleBtn.textContent = '展开';
            }
        });

        // 选择预设设备
        deviceSelect.addEventListener('change', () => {
            const device = presetDevices[deviceSelect.value];
            if (device) {
                uaInput.value = device.userAgent;
                widthInput.value = device.width;
                heightInput.value = device.height;
                scaleInput.value = device.deviceScaleFactor;
            }
        });

        // 应用设置
        applyBtn.addEventListener('click', () => {
            const settings = {
                userAgent: uaInput.value,
                width: parseInt(widthInput.value) || window.innerWidth,
                height: parseInt(heightInput.value) || window.innerHeight,
                deviceScaleFactor: parseFloat(scaleInput.value) || window.devicePixelRatio
            };
            
            try {
                GM_setValue('deviceSettings', JSON.stringify(settings));
                applySettings(settings);
                console.log('设置已保存');
            } catch (e) {
                console.error('保存设置失败:', e);
            }
        });
    }

    // 应用设备设置
    function applySettings(settings) {
        try {
            // 修改 User Agent
            Object.defineProperty(navigator, 'userAgent', {
                get: function() {
                    return settings.userAgent || navigator.userAgent;
                },
                configurable: true
            });

            // 修改屏幕参数
            const screenProps = {
                width: settings.width,
                height: settings.height,
                availWidth: settings.width,
                availHeight: settings.height
            };

            // 修改 window 属性
            Object.defineProperties(window, {
                'innerWidth': { 
                    value: settings.width,
                    configurable: true,
                    writable: true
                },
                'outerWidth': { 
                    value: settings.width,
                    configurable: true,
                    writable: true
                },
                'innerHeight': { 
                    value: settings.height,
                    configurable: true,
                    writable: true
                },
                'outerHeight': { 
                    value: settings.height,
                    configurable: true,
                    writable: true
                },
                'devicePixelRatio': { 
                    value: settings.deviceScaleFactor,
                    configurable: true,
                    writable: true
                }
            });

            // 修改 screen 属性
            Object.defineProperties(screen, {
                'width': { 
                    value: screenProps.width,
                    configurable: true,
                    writable: true
                },
                'height': { 
                    value: screenProps.height,
                    configurable: true,
                    writable: true
                },
                'availWidth': { 
                    value: screenProps.availWidth,
                    configurable: true,
                    writable: true
                },
                'availHeight': { 
                    value: screenProps.availHeight,
                    configurable: true,
                    writable: true
                }
            });

            // 设置 viewport
            const viewportContent = `width=${settings.width}, initial-scale=1.0, user-scalable=yes, maximum-scale=1.0`;
            let viewport = document.querySelector('meta[name="viewport"]');
            if (viewport) {
                viewport.content = viewportContent;
            } else {
                viewport = document.createElement('meta');
                viewport.name = 'viewport';
                viewport.content = viewportContent;
                document.head.appendChild(viewport);
            }

            // 添加强制样式
            const styleId = 'device-simulator-styles';
            let styleSheet = document.getElementById(styleId);
            if (!styleSheet) {
                styleSheet = document.createElement('style');
                styleSheet.id = styleId;
                document.head.appendChild(styleSheet);
            }
            
            styleSheet.textContent = `
                :root {
                    --device-width: ${settings.width}px;
                }
                
                html, body {
                    overflow-x: hidden !important;
                    width: var(--device-width) !important;
                    min-width: var(--device-width) !important;
                    max-width: var(--device-width) !important;
                    margin: 0 !important;
                    padding: 0 !important;
                }
                
                body > * {
                    max-width: var(--device-width) !important;
                }

                img {
                    max-width: 100% !important;
                    height: auto !important;
                }

                * {
                    -webkit-text-size-adjust: none !important;
                    text-size-adjust: none !important;
                    box-sizing: border-box !important;
                }
            `;

            // 触发重绘事件
            window.dispatchEvent(new Event('resize'));
            window.dispatchEvent(new Event('orientationchange'));
            
            // 强制重新计算布局
            document.documentElement.style.zoom = '99.99999%';
            setTimeout(() => {
                document.documentElement.style.zoom = '100%';
            }, 10);

        } catch (e) {
            console.error('应用设置失败:', e);
        }
    }

    // 初始化
    const container = createFloatingWindow();
    initializeDrag(container);
    initializeEvents(container);
    
    // 默认收起状态
    container.querySelector('#simulator-content').style.display = 'none';
    container.querySelector('#toggle-simulator').textContent = '展开';
})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址