// ==UserScript==
// @name 设备模拟器
// @namespace http://tampermonkey.net/
// @version 1.1
// @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 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
});
// 修改屏幕参数
Object.defineProperty(window, 'innerWidth', {
get: function() {
return settings.width || window.innerWidth;
},
configurable: true
});
Object.defineProperty(window, 'innerHeight', {
get: function() {
return settings.height || window.innerHeight;
},
configurable: true
});
Object.defineProperty(window, 'devicePixelRatio', {
get: function() {
return settings.deviceScaleFactor || window.devicePixelRatio;
},
configurable: true
});
// 触发 resize 事件
window.dispatchEvent(new Event('resize'));
} catch (e) {
console.error('应用设置失败:', e);
}
}
// 初始化
const container = createFloatingWindow();
initializeDrag(container);
initializeEvents(container);
// 默认收起状态
container.querySelector('#simulator-content').style.display = 'none';
container.querySelector('#toggle-simulator').textContent = '展开';
})();