// ==UserScript== // @name 虚拟地理位置 // @name:en Virtual Geographic Location // @namespace https://github.com/LaLa-HaHa-Hei/ // @version 1.0.0 // @description 自定义浏览器中的地理位置,在菜单中点击即可启用 // @author 代码见三 // @include * // @grant GM_getValue // @grant GM_setValue // @grant GM_registerMenuCommand // @grant GM_unregisterMenuCommand // ==/UserScript== GM_registerMenuCommand("启动虚拟地理位置", function () { 'use strict'; // 防止重复注入 if (document.querySelector('#VGL-popup-window') !== null) { alert("已启动虚拟地理位置,请勿重复启用") return } injectPopupWindow() injectPopupWindowCSS() let accuracyInput = null let latitudeInput = null let longitudeInput = null setTimeout(() => { accuracyInput = document.querySelector("#VGL-accuracy-input") latitudeInput = document.querySelector("#VGL-latitude-input") longitudeInput = document.querySelector("#VGL-longitude-input") setPopupWindowDraggable() initInputs() bindEvent() }, 0); // 注入弹窗 function injectPopupWindow() { const injectedHTML = `
虚拟地理位置
` document.body.insertAdjacentHTML('beforeend', injectedHTML) } // 注入弹窗的css function injectPopupWindowCSS() { const injectedCSS = ` #VGL-popup-window { border: 1px solid #ccc; position: absolute; bottom: 10px; left: 10px; background-color: #fff; z-index: 100; color: black; border-radius: 10px; overflow: hidden; } #VGL-popup-window-header { height: 30px; line-height: 30px; background-color: #409EFF; font-size: 18px; font-weight: 500; color: #fff; cursor: move; user-select: none; text-align: center; } #VGL-popup-window-main { padding: 20px; display: flex; flex-direction: column; gap: 10px; } #VGL-popup-window-main label { display: inline; } #VGL-popup-window-main input { height: 25px; width: 150px; border: 1px solid #ccc; } #VGL-confirm-button { width: 60px; height: 30px; background-color: #409EFF; color: #fff; font-size: 14px; font-weight: 500; border: none; cursor: pointer; border-radius: 10px; } ` const style = document.createElement('style') style.textContent = injectedCSS document.head.appendChild(style) } // 实现窗口可拖动功能 function setPopupWindowDraggable() { const header = document.getElementById("VGL-popup-window-header") const popupWindow = document.getElementById("VGL-popup-window") let isMouseDown = false let offsetX = 0 let offsetY = 0 header.addEventListener("mousedown", (event) => { isMouseDown = true offsetX = event.clientX - popupWindow.offsetLeft offsetY = popupWindow.offsetHeight - (event.clientY - popupWindow.offsetTop) }) document.addEventListener("mousemove", (event) => { if (isMouseDown) { const x = event.clientX - offsetX const y = window.innerHeight - (event.clientY + offsetY) popupWindow.style.left = `${x}px` popupWindow.style.bottom = `${y}px` } }); document.addEventListener("mouseup", () => { isMouseDown = false; }); } // 初始化输入框的值 function initInputs() { const savedAccuracy = GM_getValue("VGL-accuracy", 20000) const savedLatitude = GM_getValue("VGL-latitude", 0) const savedLongitude = GM_getValue("VGL-longitude", 0) accuracyInput.value = savedAccuracy latitudeInput.value = savedLatitude longitudeInput.value = savedLongitude } // 保存输入框的值 function saveInputs() { GM_setValue("VGL-accuracy", parseFloat(accuracyInput.value)) GM_setValue("VGL-latitude", parseFloat(latitudeInput.value)) GM_setValue("VGL-longitude", parseFloat(longitudeInput.value)) } // 绑定事件 function bindEvent() { document.querySelector("#VGL-confirm-button").addEventListener("click", (event) => { saveInputs() // 覆盖原本的API navigator.geolocation.getCurrentPosition 方法,使其返回自定义的位置数据 navigator.geolocation.getCurrentPosition = function (successCallback, errorCallback, options) { let positionPromise = new Promise((resolve, reject) => { // 自定义的位置数据 let fakePosition = { coords: { accuracy: parseFloat(accuracyInput.value), altitude: null, altitudeAccuracy: null, 'latitude': parseFloat(latitudeInput.value), 'longitude': parseFloat(longitudeInput.value), heading: null, speed: null }, timestamp: Date.now() } console.log(fakePosition) resolve(fakePosition) }); // 处理 Promise 的结果 positionPromise.then((position) => { if (successCallback) { successCallback(position) } }).catch((error) => { if (errorCallback) { errorCallback(error) } }); } alert("已设定虚拟地理位置") }) } }, "h" );