图寻音效

增加地图点击音效以及倒计时提醒音效,支持表情快捷键

目前为 2024-08-29 提交的版本。查看 最新版本

// ==UserScript==
// @name         图寻音效
// @namespace    https://gf.qytechs.cn/users/1179204
// @version      1.0.6
// @description  增加地图点击音效以及倒计时提醒音效,支持表情快捷键
// @author       KaKa
// @icon         https://www.svgrepo.com/show/521999/bell.svg
// @match        *://tuxun.fun/*
// @exclude      *://tuxun.fun/replay-pano?*
// @grant        GM_addStyle
// @license      BSD
// ==/UserScript==
(function() {
    let google,map,streetViewPanorama,gameData,gameMode,gameId,roundState,currentRound,previousSize,isRecording,recordMode,events=[]
    let replay_data=JSON.parse(localStorage.getItem('replay_data'))
    if(!replay_data) replay_data={}
    let videoIcon=`<svg xmlns="http://www.w3.org/2000/svg" width="16px" height="16px" fill="#fff" class="bi bi-camera-reels" viewBox="0 0 16 16">
  <path d="M6 3a3 3 0 1 1-6 0 3 3 0 0 1 6 0M1 3a2 2 0 1 0 4 0 2 2 0 0 0-4 0"/>
  <path d="M9 6h.5a2 2 0 0 1 1.983 1.738l3.11-1.382A1 1 0 0 1 16 7.269v7.462a1 1 0 0 1-1.406.913l-3.111-1.382A2 2 0 0 1 9.5 16H2a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2zm6 8.73V7.27l-3.5 1.555v4.35zM1 8v6a1 1 0 0 0 1 1h7.5a1 1 0 0 0 1-1V8a1 1 0 0 0-1-1H2a1 1 0 0 0-1 1"/>
  <path d="M9 6a3 3 0 1 0 0-6 3 3 0 0 0 0 6M7 3a2 2 0 1 1 4 0 2 2 0 0 1-4 0"/>
</svg>`
    let iconUrl=svgToUrl(videoIcon)

    const button = document.createElement('button');
    button.id = 'replay';

    let intervalId=setInterval(function(){
        const streetViewContainer= document.getElementById('viewer')
        const wrapper=document.querySelector('.wrapper___NMMQn')
        if(streetViewContainer){
            wrapper.appendChild(button)
            clearInterval(intervalId)}
    },500);


    GM_addStyle(`
        #replay {
            position:absolute;
            left:24px;
            top:18%;
            width: 40px;
            height: 40px;
            border: none;
            border-radius:20px;
            background-image: url('${iconUrl}');
            background-size: auto;
            background-position: center;
            background-color:#000000;
             background-repeat: no-repeat;
            cursor: pointer;
            color: white;
            font-size: 16px;
            display: inline-flex;
            align-items: center;
            justify-content: center;
            z-Index:9999
        }
    `);
    button.onclick = () => {
        if(!isRecording){
            console.log('开始录制')
            initObserver()
            mapListener()
            isRecording=true
            const svgContent = videoIcon.replace(/fill="#fff"/g, 'fill="#D87A16"')
            button.style.backgroundImage=`url(${svgToUrl(svgContent)})`

        }
        else{
            isRecording=false
            console.log('停止录制')
            if(!replay_data[gameId])replay_data[gameId]={}
            replay_data[gameId][currentRound]=events
            localStorage.setItem('replay_data',JSON.stringify(replay_data))
            events=[]
            button.style.backgroundImage=`url('${iconUrl}')`
        }
    }

    const observer = new MutationObserver((mutationsList) => {
        for (const mutation of mutationsList) {
            if (mutation.type === 'childList') {
                mutation.addedNodes.forEach(node => {
                    node.querySelectorAll && node.querySelectorAll('.countdownTimer___pqf8b').forEach(childNode => {
                        let intervalId = setInterval(function() {
                            const timeLeft = childNode.innerHTML.toString();
                            if (timeLeft.includes(':')) {
                                recordEvent('CountDown', timeLeft);
                                clearInterval(intervalId);
                            }
                        }, 100);
                    });
                });
            }
        }
    });


    const config = { childList: true, subtree: true };
    observer.observe(document.body, config);

    function getSvContainer(){
        const streetViewContainer= document.getElementById('viewer')
        const keys = Object.keys(streetViewContainer)
        const key = keys.find(key => key.startsWith("__reactFiber"))
        const props = streetViewContainer[key]
        streetViewPanorama=props.return.child.memoizedProps.children[1].props.googleMapInstance
        gameData=props.return.return.return.return.return.memoizedState.next.next.memoizedState.current.gameData
        gameMode=gameData.type
        if(gameMode==='infinity'||gameMode==='challenge'){
            recordMode=true
        }
    }

    function svgToUrl(svgText) {
        const svgBlob = new Blob([svgText], {type: 'image/svg+xml'});
        const svgUrl = URL.createObjectURL(svgBlob);
        return svgUrl;
    }

    function getMap(){
        var mapContainer = document.getElementById('map')
        const keys = Object.keys(mapContainer)
        const key = keys.find(key => key.startsWith("__reactFiber$"))
        const props = mapContainer[key]
        const x = props.child.memoizedProps.value.map
        map=x.getMap()
        google=unsafeWindow.google
    }

    function mapListener() {
        const mapContainer = document.querySelector('.maplibregl-canvas');
        if (!mapContainer) {
            console.error('Map container not found.');
            return;
        }

        const observer = new MutationObserver((mutationsList) => {
            for (let mutation of mutationsList) {
                if (mutation.type === 'attributes' && mutation.attributeName === 'style'&&isRecording) {
                    handleMapChange(mapContainer)
                }
            }
        });

        observer.observe(mapContainer, { attributes: true, attributeFilter: ['style'] });
    }


    function handleMapChange(mapContainer) {
        const style = window.getComputedStyle(mapContainer);

        const { width, height } = mapContainer.getBoundingClientRect();
        const currentScreenWidth = window.innerWidth;

        const widthRatio =Math.round((width / currentScreenWidth) * 100);
        if (widthRatio===50&&roundState&&!recordMode){
            recordEvent('RoundEnd')
            roundState=false
            if(!replay_data[gameId])replay_data[gameId]={}
            replay_data[gameId][currentRound]=events
            localStorage.setItem('replay_data',JSON.stringify(replay_data))
            currentRound+=1
            events=[]
        }

        else if (widthRatio!=50&&!roundState&&!recordMode){
            roundState=true
        }


        if (widthRatio>=90&&previousSize<90&&recordMode){
            recordEvent('RoundEnd')
            roundState=false
            if(!replay_data[gameId])replay_data[gameId]={}
            replay_data[gameId][currentRound]=events
            localStorage.setItem('replay_data',JSON.stringify(replay_data))
            currentRound+=1
            events=[]
        }
        else if (widthRatio<90&&previousSize>=90&&recordMode){
            roundState=true
        }


        if (previousSize!=widthRatio&&roundState&&widthRatio!=50&&previousSize!=50){
            recordEvent('MapSize',[Math.round(width),Math.round(height)]);
        }

        previousSize=widthRatio
    }


    function recordEvent(type,data) {
        if(roundState&&isRecording){
            events.push({time:new Date().getTime(),
                         type:type,
                         data:data});
        }
    }

    function setObserver(panorama) {
        let zoomLevel,bounds,mapCenter
        if (gameData.status==='finish') return
        panorama.addListener('position_changed', () => {
            recordEvent('PanoPosition', panorama.getPano());
        });

        panorama.addListener('pov_changed', () => {
            const pov = panorama.getPov()
            const formattedPov = {
                heading: pov.heading.toFixed(2),
                pitch: pov.pitch.toFixed(2)
            };
            recordEvent('PanoPov',[formattedPov.heading,formattedPov.pitch]);
        });

        panorama.addListener('zoom_changed', () => {
            recordEvent('PanoZoom', panorama.getZoom().toFixed(2));
        });

        map.on('moveend', () => {
            bounds = map.getBounds();
            mapCenter=bounds.getCenter();
            recordEvent('MapView', [mapCenter.lat.toFixed(5),mapCenter.lng.toFixed(5)]);
        });
        map.on('zoom', (event) => {
            zoomLevel = map.getZoom();
            bounds = map.getBounds();
            mapCenter = bounds.getCenter();
            recordEvent('MapZoom', [mapCenter.lat.toFixed(5),mapCenter.lng.toFixed(5),zoomLevel]);
        });

        map.on('click', (event) => {
            const { lngLat } = event;
            recordEvent('Pin', [lngLat.lat.toFixed(5), lngLat.lng.toFixed(5)]);
        });
    }

    function initObserver(){
        try {
            getSvContainer()
            getMap()}
        catch(error){
            console.log('Failed  to start listening')}
        if(streetViewPanorama){
            gameId=gameData.id||gameData.challengeId
            currentRound=gameData.currentRound
            var round
            if (gameData.rounds.length===1&&currentRound!=1){
                roundState=true
            }
            else{round=gameData.rounds[currentRound-1].endTime
                 if (round) roundState=false
                 else roundState=true}
            setObserver(streetViewPanorama)}
    }

    let onKeyDown = (e) => {
        if (e.key === 'v' || e.key === 'V') {
            e.stopImmediatePropagation();
            if(!isRecording){
                console.log('开始录制')
                initObserver()
                mapListener()
                isRecording=true
                const svgContent = videoIcon.replace(/fill="#fff"/g, 'fill="#D87A16"')
                button.style.backgroundImage=`url(${svgToUrl(svgContent)})`

            }
            else{
                isRecording=false
                console.log('停止录制')
                if(!replay_data[gameId])replay_data[gameId]={}
                replay_data[gameId][currentRound]=events
                localStorage.setItem('replay_data',JSON.stringify(replay_data))
                events=[]
                button.style.backgroundImage=`url('${iconUrl}')`
            }
        }
    }

    document.addEventListener("keydown", onKeyDown);
})();

QingJ © 2025

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