非公開 座標自動維持ボタン(離席用)

    @@ -4,22 +4,28 @@ * @private */ javascript:(function(){ - if(document.getElementById('afk-rotate-btn')) return; + const id = 'afk-rotate-btn'; + if(document.getElementById(id)) return; + + /* 1. 位置計算:ゲームコンテナの左端を取得 */ + const container = document.getElementById('game-container'); + const rect = container ? container.getBoundingClientRect() : { left: 0, top: 0 }; + /* 画面端からではなく、コンテナの左端から10pxの位置にする */ + const targetLeft = rect.left + 10; - /* 1. ボタン作成(🌀マークのみの最小サイズ) */ const btn = document.createElement('button'); - btn.id = 'afk-rotate-btn'; + btn.id = id; btn.innerHTML = '🌀'; Object.assign(btn.style, { position: 'fixed', - top: '60px', /* 左上から少し下げた位置 */ - left: '10px', + top: '60px', + left: targetLeft + 'px', zIndex: '10000', width: '40px', height: '40px', lineHeight: '40px', textAlign: 'center', - backgroundColor: 'rgba(0, 0, 0, 0.3)', /* 控えめな透過黒 */ + backgroundColor: 'rgba(0, 0, 0, 0.3)', color: '#fff', border: '1px solid rgba(255, 255, 255, 0.5)', borderRadius: '50%', @@ -30,60 +36,43 @@ }); document.body.appendChild(btn); + /* ウィンドウのリサイズ時に位置を再計算 */ + window.addEventListener('resize', () => { + const r = container.getBoundingClientRect(); + btn.style.left = (r.left + 10) + 'px'; + }); + let rotateInterval = null; let step = 0; const dirs = [{x: 0, y: -1}, {x: 1, y: 0}, {x: 0, y: 1}, {x: -1, y: 0}]; - /* 停止・解除関数 */ const stopRotate = () => { if (!rotateInterval) return; clearInterval(rotateInterval); rotateInterval = null; btn.style.backgroundColor = 'rgba(0, 0, 0, 0.3)'; - btn.style.borderColor = 'rgba(255, 255, 255, 0.5)'; - if(typeof inputState !== 'undefined') { - inputState.dx = 0; - inputState.dy = 0; - inputState.drawing = false; + inputState.dx = 0; inputState.dy = 0; inputState.drawing = false; if(typeof sendInput === 'function') sendInput(); } }; - /* 開始関数 */ - const startRotate = (e) => { - if (e) e.stopPropagation(); - if (rotateInterval) { - stopRotate(); - return; - } - btn.style.backgroundColor = 'rgba(255, 0, 0, 0.6)'; /* 稼働中は赤く光る */ - btn.style.borderColor = '#fff'; + btn.onclick = (e) => { + e.stopPropagation(); + if (rotateInterval) return stopRotate(); + btn.style.backgroundColor = 'rgba(255, 0, 0, 0.6)'; rotateInterval = setInterval(() => { if (typeof inputState !== 'undefined' && typeof sendInput === 'function') { - const d = dirs[step % 4]; - inputState.dx = d.x; - inputState.dy = d.y; - inputState.drawing = true; + const d = dirs[step++ % 4]; + inputState.dx = d.x; inputState.dy = d.y; inputState.drawing = true; sendInput(); - step++; } }, 200); }; - btn.onclick = startRotate; - - /* 2. 操作介入による自動解除のリスナー */ - /* タッチ・マウス・キーボードのいずれかがあったら停止 */ - const inputEvents = ['touchstart', 'mousedown', 'keydown']; - inputEvents.forEach(eventType => { - window.addEventListener(eventType, (e) => { - /* ぐるぐるボタン自体へのクリックは無視して、それ以外の操作で停止 */ - if (e.target !== btn && rotateInterval) { - stopRotate(); - } + ['touchstart', 'mousedown', 'keydown'].forEach(type => { + window.addEventListener(type, (e) => { + if (e.target !== btn && rotateInterval) stopRotate(); }, { capture: true, passive: true }); }); - - console.log('🌀 Minimal Auto-Rotate: Input-interruption mode active.'); })();
  • /*
     * @title 座標自動維持ボタン(離席用)
     * @description 押すとその場でグルグル回り続けるボタンが表示される
     * @private
     */
    javascript:(function(){
        const id = 'afk-rotate-btn';
        if(document.getElementById(id)) return;
    
        /* 1. 位置計算:ゲームコンテナの左端を取得 */
        const container = document.getElementById('game-container');
        const rect = container ? container.getBoundingClientRect() : { left: 0, top: 0 };
        /* 画面端からではなく、コンテナの左端から10pxの位置にする */
        const targetLeft = rect.left + 10;
    
        const btn = document.createElement('button');
        btn.id = id;
        btn.innerHTML = '🌀';
        Object.assign(btn.style, {
            position: 'fixed',
            top: '60px',
            left: targetLeft + 'px',
            zIndex: '10000',
            width: '40px',
            height: '40px',
            lineHeight: '40px',
            textAlign: 'center',
            backgroundColor: 'rgba(0, 0, 0, 0.3)',
            color: '#fff',
            border: '1px solid rgba(255, 255, 255, 0.5)',
            borderRadius: '50%',
            fontSize: '20px',
            cursor: 'pointer',
            padding: '0',
            outline: 'none'
        });
        document.body.appendChild(btn);
    
        /* ウィンドウのリサイズ時に位置を再計算 */
        window.addEventListener('resize', () => {
            const r = container.getBoundingClientRect();
            btn.style.left = (r.left + 10) + 'px';
        });
    
        let rotateInterval = null;
        let step = 0;
        const dirs = [{x: 0, y: -1}, {x: 1, y: 0}, {x: 0, y: 1}, {x: -1, y: 0}];
    
        const stopRotate = () => {
            if (!rotateInterval) return;
            clearInterval(rotateInterval);
            rotateInterval = null;
            btn.style.backgroundColor = 'rgba(0, 0, 0, 0.3)';
            if(typeof inputState !== 'undefined') {
                inputState.dx = 0; inputState.dy = 0; inputState.drawing = false;
                if(typeof sendInput === 'function') sendInput();
            }
        };
    
        btn.onclick = (e) => {
            e.stopPropagation();
            if (rotateInterval) return stopRotate();
            btn.style.backgroundColor = 'rgba(255, 0, 0, 0.6)';
            rotateInterval = setInterval(() => {
                if (typeof inputState !== 'undefined' && typeof sendInput === 'function') {
                    const d = dirs[step++ % 4];
                    inputState.dx = d.x; inputState.dy = d.y; inputState.drawing = true;
                    sendInput();
                }
            }, 200);
        };
    
        ['touchstart', 'mousedown', 'keydown'].forEach(type => {
            window.addEventListener(type, (e) => {
                if (e.target !== btn && rotateInterval) stopRotate();
            }, { capture: true, passive: true });
        });
    })();
  • Permalink
    このページへの個別リンクです。
    RAW
    書かれたコードへの直接のリンクです。
    Packed
    文字列が圧縮された書かれたコードへのリンクです。
    Userscript
    Greasemonkey 等で利用する場合の .user.js へのリンクです。
    Loader
    @require やソースコードが長い場合に多段ロードする Loader コミのコードへのリンクです。
    Metadata
    コード中にコメントで @xxx と書かれたメタデータの JSON です。