您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Show speed stats (Desktop only)
// ==UserScript== // @name kurviger.com Speed Stats // @namespace shiftgeist // @match https://kurviger.com/* // @match https://kurviger.de/* // @grant GM_addStyle // @version 20250417 // @author shiftgeist // @description Show speed stats (Desktop only) // @license GNU GPLv3 // @icon https://www.google.com/s2/favicons?sz=64&domain=kurviger.com // ==/UserScript== GM_addStyle(` .leaflet-marker-icon.rounded-full { display: none !important; } .leaflet-zoom-animated [stroke="#ff7575"] /* 30 */ { stroke-width: 10px; } /* .leaflet-zoom-animated [stroke="#ffcf6e"], 50 */ .leaflet-zoom-animated [stroke="#fff06e"], /* 70 */ .leaflet-zoom-animated [stroke="#6eff8d"] /* 100 */ { stroke: #477BD6; } `) const debug = window.localStorage.getItem('debug-log') === 'true' const doc = document const elDropdown = '.selection-dropdown' const elSource = '.heightgraph-container-inner' let sourceObserver = null let stats = null let timeoutCounter = 0 function log(...params) { if (debug) { console.debug('[Speed]', ...params) } } function getSpeed(color) { switch (color) { case 'rgb(255, 117, 117)': return '30' case 'rgb(255, 207, 110)': return '50' case 'rgb(255, 240, 110)': return '70' case 'rgb(110, 255, 141)': return '100' default: case 'rgb(179, 179, 179)': return 'unknown' } } function getData() { const source = doc.querySelector(elSource) const childrenEl = Array.from(source.querySelectorAll('.area')) const containerWidth = childrenEl.reduce((acc, v) => acc + v.getBoundingClientRect().width - 4, 0) const children = childrenEl.map(v => ({ width: v.getBoundingClientRect().width / containerWidth, speed: getSpeed(v.style.fill), })) const speeds = { 30: 0, 50: 0, 70: 0, 100: 0, unknown: 0, } for (const s of children) { speeds[s.speed] += s.width } speeds.sum = speeds['30'] + speeds['50'] + speeds['70'] + speeds['100'] speeds.average = speeds['30'] * 30 + speeds['50'] * 50 + speeds['70'] * 70 + speeds['100'] * 100 return speeds } function isSpeedGraph() { return doc.querySelector(elDropdown).value === '2' } function prepareStats() { if (stats) { log('remove first', stats) stats.remove() stats = null } createListener(createStats) if (!isSpeedGraph()) { log('wrong graph type') return } return true } function createListener(callback) { if (!sourceObserver) { log('registering observer') sourceObserver = new MutationObserver(() => { if (prepareStats()) { callback() } }) sourceObserver.observe(document.querySelector(elSource), { childList: true }) } } function createStats() { if (!prepareStats()) return log('create stats') stats = doc.createElement('div') stats.id = 'SpeedStats' stats.style.zIndex = '9999' stats.style.position = 'absolute' stats.style.top = '155px' stats.style.left = '50%' stats.style.transform = 'translate(-50%)' doc.querySelector('.heightgraph').appendChild(stats) const d = getData() stats.textContent += `${(d['30'] * 100).toFixed()}% 30 km/h` stats.textContent += `, ` stats.textContent += `${(d['50'] * 100).toFixed()}% 50 km/h` stats.textContent += `, ` stats.textContent += `${(d['70'] * 100).toFixed()}% 70 km/h` stats.textContent += `, ` stats.textContent += `${(d['100'] * 100).toFixed()}% 100 km/h` stats.textContent += `, ` stats.textContent += `Average ${d.average.toFixed()} km/h` log('Stats created', d) } function waitForLoad(callback) { log('wait for load') if (!window.location.href.includes('/plan') || timeoutCounter > 20) { log('wrong url or too many retries') return } if (doc.querySelectorAll(`${elSource}, ${elDropdown}`).length) { log('has source') callback() } else { timeoutCounter += 1 setTimeout(() => waitForLoad(callback), 20 * (timeoutCounter / 2 + 1)) } } waitForLoad(createStats)