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

    @@ -4,77 +4,86 @@ * @private */ javascript:(function(){ - const id = 'afk-btn'; - if(document.getElementById(id)) return; + if(document.getElementById('afk-rotate-btn')) return; - /* 1. ボタン作成(body直下に置いてクリック遮断を防ぐ) */ - const btn = document.createElement('div'); - btn.id = id; + /* 1. ボタン作成(🌀マークのみの最小サイズ) */ + const btn = document.createElement('button'); + btn.id = 'afk-rotate-btn'; btn.innerHTML = '🌀'; Object.assign(btn.style, { position: 'fixed', - zIndex: '100000', /* 最前面 */ - width: '38px', - height: '38px', - lineHeight: '38px', + top: '60px', /* 左上から少し下げた位置 */ + left: '10px', + zIndex: '10000', + width: '40px', + height: '40px', + lineHeight: '40px', textAlign: 'center', - background: 'rgba(0,0,0,0.4)', + backgroundColor: 'rgba(0, 0, 0, 0.3)', /* 控えめな透過黒 */ + color: '#fff', + border: '1px solid rgba(255, 255, 255, 0.5)', borderRadius: '50%', fontSize: '20px', cursor: 'pointer', - color: '#fff', - border: '1px solid rgba(255,255,255,0.3)', - transition: 'transform 0.1s' + padding: '0', + outline: 'none' }); document.body.appendChild(btn); - /* 2. PC・スマホ両方の「ゲーム画面左上」を計算して配置する関数 */ - const updatePos = () => { - const game = document.getElementById('game-container'); - if (game) { - const rect = game.getBoundingClientRect(); - /* ゲーム画面の左端から10px、上から60pxの位置に固定 */ - btn.style.left = (rect.left + 10) + 'px'; - btn.style.top = (rect.top + 60) + '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; + if(typeof sendInput === 'function') sendInput(); } }; - window.addEventListener('resize', updatePos); - updatePos(); - let timer = null, step = 0; - const dirs = [{x:0,y:-1},{x:1,y:0},{x:0,y:1},{x:-1,y:0}]; - - const stop = () => { - if (!timer) return; - clearInterval(timer); - timer = null; - btn.style.background = 'rgba(0,0,0,0.4)'; - btn.style.transform = 'scale(1)'; - if(window.inputState) { - inputState.dx = 0; inputState.dy = 0; inputState.drawing = false; - if(window.sendInput) sendInput(); + /* 開始関数 */ + const startRotate = (e) => { + if (e) e.stopPropagation(); + if (rotateInterval) { + stopRotate(); + return; } - }; - - btn.onclick = (e) => { - e.preventDefault(); - e.stopPropagation(); - if (timer) return stop(); - - btn.style.background = 'rgba(255,0,0,0.6)'; - btn.style.transform = 'scale(1.1)'; - timer = setInterval(() => { - if (window.inputState && window.sendInput) { - const d = dirs[step++ % 4]; - inputState.dx = d.x; inputState.dy = d.y; inputState.drawing = true; + btn.style.backgroundColor = 'rgba(255, 0, 0, 0.6)'; /* 稼働中は赤く光る */ + btn.style.borderColor = '#fff'; + 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(); + step++; } }, 200); }; - /* 3. 自動解除(ボタン自身以外の操作で停止) */ - const handleReset = (e) => { if(timer && e.target !== btn) stop(); }; - window.addEventListener('mousedown', handleReset, true); - window.addEventListener('touchstart', handleReset, true); - window.addEventListener('keydown', handleReset, true); + btn.onclick = startRotate; + + /* 2. 操作介入による自動解除のリスナー */ + /* タッチ・マウス・キーボードのいずれかがあったら停止 */ + const inputEvents = ['touchstart', 'mousedown', 'keydown']; + inputEvents.forEach(eventType => { + window.addEventListener(eventType, (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(){
        if(document.getElementById('afk-rotate-btn')) return;
    
        /* 1. ボタン作成(🌀マークのみの最小サイズ) */
        const btn = document.createElement('button');
        btn.id = 'afk-rotate-btn';
        btn.innerHTML = '🌀';
        Object.assign(btn.style, {
            position: 'fixed',
            top: '60px', /* 左上から少し下げた位置 */
            left: '10px',
            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);
    
        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;
                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';
            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();
                    step++;
                }
            }, 200);
        };
    
        btn.onclick = startRotate;
    
        /* 2. 操作介入による自動解除のリスナー */
        /* タッチ・マウス・キーボードのいずれかがあったら停止 */
        const inputEvents = ['touchstart', 'mousedown', 'keydown'];
        inputEvents.forEach(eventType => {
            window.addEventListener(eventType, (e) => {
                /* ぐるぐるボタン自体へのクリックは無視して、それ以外の操作で停止 */
                if (e.target !== btn && rotateInterval) {
                    stopRotate();
                }
            }, { capture: true, passive: true });
        });
    
        console.log('🌀 Minimal Auto-Rotate: Input-interruption mode active.');
    })();
  • Permalink
    このページへの個別リンクです。
    RAW
    書かれたコードへの直接のリンクです。
    Packed
    文字列が圧縮された書かれたコードへのリンクです。
    Userscript
    Greasemonkey 等で利用する場合の .user.js へのリンクです。
    Loader
    @require やソースコードが長い場合に多段ロードする Loader コミのコードへのリンクです。
    Metadata
    コード中にコメントで @xxx と書かれたメタデータの JSON です。