您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Allows you rotate your view 90 degrees and zoom in on neal.fun/internet-roadtrip
当前为
// ==UserScript== // @name Internet Roadtrip - Look Out The Window v1 // @description Allows you rotate your view 90 degrees and zoom in on neal.fun/internet-roadtrip // @namespace me.netux.site/user-scripts/internet-roadtrip/look-out-the-window-v1 // @version 1.13.3 // @author Netux // @license MIT // @match https://neal.fun/internet-roadtrip/ // @icon https://neal.fun/favicons/internet-roadtrip.png // @grant GM.setValues // @grant GM.getValues // @grant GM.registerMenuCommand // @run-at document-end // @require https://cdn.jsdelivr.net/combine/npm/@violentmonkey/dom@2,npm/@violentmonkey/[email protected] // @require https://cdn.jsdelivr.net/npm/[email protected] // ==/UserScript== (async () => { const LEGACY_LOCAL_STORAGE_KEY = "internet-roadtrip/mod/look-out-the-window"; const SIDE_WINDOW_IMAGE_SRC = `https://cloudy.netux.site/neal_internet_roadtrip/side%20window.png`; const BACK_WINDOW_IMAGE_SRC = `https://cloudy.netux.site/neal_internet_roadtrip/back%20window.png`; const Direction = Object.freeze({ AHEAD: 0, RIGHT: 1, BACK: 2, LEFT: 3 }); const state = { settings: { lookingDirection: Direction.AHEAD, zoom: 1, showVehicleUi: true, alwaysShowGameUi: false }, dom: {} }; { // migrate locals storage data form versions <=1.12.0 if (LEGACY_LOCAL_STORAGE_KEY in localStorage) { const localStorageSettings = JSON.parse(localStorage.getItem(LEGACY_LOCAL_STORAGE_KEY)); await GM.setValues(localStorageSettings); localStorage.removeItem(LEGACY_LOCAL_STORAGE_KEY); } } { const storedSettings = await GM.getValues(Object.keys(state.settings)) Object.assign( state.settings, storedSettings ); } function setupDom() { injectStylesheet(); preloadImages(); const containerEl = document.querySelector('.container'); state.dom.containerEl = containerEl; state.dom.panoIframeEls = Array.from(containerEl.querySelectorAll('.pano')); state.dom.windowEl = document.createElement('div'); state.dom.windowEl.className = 'window'; state.dom.panoIframeEls.at(-1).insertAdjacentElement('afterend', state.dom.windowEl); async function lookRight() { state.settings.lookingDirection = (state.settings.lookingDirection + 1) % 4; updateLookAt(); await saveSettings(); } async function lookLeft() { state.settings.lookingDirection = state.settings.lookingDirection - 1; if (state.settings.lookingDirection < 0) { state.settings.lookingDirection = 3; } updateLookAt(); await saveSettings(); } function chevronImage(rotation) { const imgEl = document.createElement('img'); imgEl.src = '/sell-sell-sell/arrow.svg'; // yoink imgEl.style.width = `10px`; imgEl.style.aspectRatio = `1`; imgEl.style.filter = `invert(1)`; imgEl.style.rotate = `${rotation}deg`; return imgEl; } state.dom.lookLeftButtonEl = document.createElement('button'); state.dom.lookLeftButtonEl.className = 'look-left-btn'; state.dom.lookLeftButtonEl.appendChild(chevronImage(90)); state.dom.lookLeftButtonEl.addEventListener('click', lookLeft); containerEl.appendChild(state.dom.lookLeftButtonEl); state.dom.lookRightButtonEl = document.createElement('button'); state.dom.lookRightButtonEl.className = 'look-right-btn'; state.dom.lookRightButtonEl.appendChild(chevronImage(-90)); state.dom.lookRightButtonEl.addEventListener('click', lookRight); containerEl.appendChild(state.dom.lookRightButtonEl); window.addEventListener("keydown", async (event) => { if (event.target !== document.body) { return; } switch (event.key) { case "ArrowLeft": { await lookLeft(); break; } case "ArrowRight": { await lookRight(); break; } } }); window.addEventListener("wheel", async (event) => { if (event.target !== document.documentElement) { // pointing at nothing but the backdrop return; } const scrollingForward = event.deltaY < 0; state.settings.zoom = Math.min(Math.max(1, state.settings.zoom * (scrollingForward ? 1.1 : 0.9)), 20); updateZoom(); await saveSettings(); }) updateUiFromSettings(); updateLookAt(); updateZoom(); } function injectStylesheet() { const styleEl = document.createElement('style'); styleEl.innerText = ` body { & .look-right-btn, & .look-left-btn { position: fixed; bottom: 200px; transform: translateY(-50%); padding-block: 1.5rem; border: none; background-color: whitesmoke; cursor: pointer; } & .look-right-btn { right: 0; padding-inline: 0.35rem 0.125rem; border-radius: 15px 0 0 15px; } & .look-left-btn { left: 0; padding-inline: 0.125rem 0.25rem; border-radius: 0 15px 15px 0; } &:not(.look-out-the-window-always-show-game-ui):not([data-looking-direction="0"]) :is(.freshener-container, .wheel-container, .options) { display: none; } &.look-out-the-window-show-vehicle-ui .window { position: fixed; width: 100%; background-image: url("${SIDE_WINDOW_IMAGE_SRC}"); background-size: cover; height: 100%; background-position: center; pointer-events: none; &.window--flip { rotate: y 180deg; } &.window--back { transform-origin: center 20%; background-image: url("${BACK_WINDOW_IMAGE_SRC}"); } } & .pano, & window { transition: opacity 300ms linear, scale 100ms linear; } } `; document.head.appendChild(styleEl); } function preloadImages() { for (const imageSrc of [SIDE_WINDOW_IMAGE_SRC, BACK_WINDOW_IMAGE_SRC]) { const image = new Image(); image.onload = () => { console.debug(`Successfully preloaded Look Out the Window image at "${imageSrc}"`); }; image.onerror = (event) => { console.error(`Failed to preload Look Out the Window image at "${imageSrc}"`, event); }; image.src = imageSrc; } } function patch(vue) { function replaceHeadingInPanoUrl(urlStr) { if (!urlStr) { return urlStr; } const url = new URL(urlStr); const currentHeading = parseFloat(url.searchParams.get('heading')); url.searchParams.set('heading', (currentHeading + state.settings.lookingDirection * 90) % 360); return url.toString(); } vue.state.getPanoUrl = new Proxy(vue.methods.getPanoUrl, { apply(ogGetPanoUrl, thisArg, args) { const urlStr = ogGetPanoUrl.apply(thisArg, args); return replaceHeadingInPanoUrl(urlStr); } }); const panoEls = Object.keys(vue.$refs).filter((name) => name.startsWith('pano')).map((key) => vue.$refs[key]); state.transitionPano = (animate = true) => { const currFrame = vue.state.currFrame; const nextFrame = (currFrame + 1) % panoEls.length; const activePanoEl = panoEls[currFrame]; if (animate) { const transitionPanoEl = panoEls[nextFrame]; vue.state.currFrame = nextFrame; transitionPanoEl.src = replaceHeadingInPanoUrl(activePanoEl.src); setTimeout(() => { vue.methods.switchFrameOrder(); }, 500); } else { activePanoEl.src = replaceHeadingInPanoUrl(activePanoEl.src); } }; } function updateUiFromSettings() { document.body.classList.toggle('look-out-the-window-show-vehicle-ui', state.settings.showVehicleUi); document.body.classList.toggle('look-out-the-window-always-show-game-ui', state.settings.alwaysShowGameUi); } function updateLookAt(animate = true) { document.body.dataset.lookingDirection = state.settings.lookingDirection; const isLookingAhead = state.settings.lookingDirection === Direction.AHEAD; state.dom.windowEl.style.display = isLookingAhead ? 'none' : ''; if (!isLookingAhead) { state.dom.windowEl.classList.toggle('window--flip', state.settings.lookingDirection === Direction.LEFT); state.dom.windowEl.classList.toggle('window--back', state.settings.lookingDirection === Direction.BACK); } state.transitionPano(animate); } function updateZoom() { for (const panoIframeEl of state.dom.panoIframeEls) { panoIframeEl.style.scale = (state.settings.zoom * 0.4 + 0.6 /* parallax */).toString(); } state.dom.windowEl.style.scale = state.settings.zoom.toString(); } async function saveSettings() { await GM.setValues(state.settings); } GM.registerMenuCommand('Toggle Vehicle UI', async () => { state.settings.showVehicleUi = !state.settings.showVehicleUi; updateUiFromSettings(); await saveSettings(); }, { id: 'look-out-the-window-toggle-vehicle-ui' }); GM.registerMenuCommand('Toggle Always show Game UI', async () => { state.settings.alwaysShowGameUi = !state.settings.alwaysShowGameUi; updateUiFromSettings(); await saveSettings(); }, { id: 'look-out-the-window-toggle-always-show-game-ui' }); state.vue = await IRF.vdom.container; patch(state.vue); setupDom(); })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址