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

    @@ -7,28 +7,39 @@ const id = 'afk-btn'; if(document.getElementById(id)) return; - /* 1. ゲームコンテナを取得(PCでの位置ずれ防止) */ - const container = document.getElementById('game-container') || document.body; - + /* 1. ボタン作成(body直下に置いてクリック遮断を防ぐ) */ const btn = document.createElement('div'); btn.id = id; btn.innerHTML = '🌀'; Object.assign(btn.style, { - position: 'absolute', - top: '60px', - left: '10px', - zIndex: '10000', - width: '36px', - height: '36px', - lineHeight: '36px', + position: 'fixed', + zIndex: '100000', /* 最前面 */ + width: '38px', + height: '38px', + lineHeight: '38px', textAlign: 'center', - background: 'rgba(0,0,0,0.3)', + background: 'rgba(0,0,0,0.4)', borderRadius: '50%', - fontSize: '18px', - cursor: 'pointer' + fontSize: '20px', + cursor: 'pointer', + color: '#fff', + border: '1px solid rgba(255,255,255,0.3)', + transition: 'transform 0.1s' }); - /* コンテナの中に入れることでゲーム画面と一緒に移動させる */ - container.appendChild(btn); + 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'; + } + }; + 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}]; @@ -37,7 +48,8 @@ if (!timer) return; clearInterval(timer); timer = null; - btn.style.background = 'rgba(0,0,0,0.3)'; + 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(); @@ -45,9 +57,12 @@ }; 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]; @@ -57,8 +72,9 @@ }, 200); }; - /* 操作があったら停止(1つのリスナーで軽量化) */ - ['mousedown','touchstart','keydown'].forEach(ev => - window.addEventListener(ev, (e) => { if(e.target !== btn) stop(); }, {passive:true}) - ); + /* 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); })();
  • /*
     * @title 座標自動維持ボタン(離席用)
     * @description 押すとその場でグルグル回り続けるボタンが表示される
     * @private
     */
    javascript:(function(){
        const id = 'afk-btn';
        if(document.getElementById(id)) return;
    
        /* 1. ボタン作成(body直下に置いてクリック遮断を防ぐ) */
        const btn = document.createElement('div');
        btn.id = id;
        btn.innerHTML = '🌀';
        Object.assign(btn.style, {
            position: 'fixed',
            zIndex: '100000', /* 最前面 */
            width: '38px',
            height: '38px',
            lineHeight: '38px',
            textAlign: 'center',
            background: 'rgba(0,0,0,0.4)',
            borderRadius: '50%',
            fontSize: '20px',
            cursor: 'pointer',
            color: '#fff',
            border: '1px solid rgba(255,255,255,0.3)',
            transition: 'transform 0.1s'
        });
        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';
            }
        };
        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();
            }
        };
    
        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;
                    sendInput();
                }
            }, 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);
    })();
  • Permalink
    このページへの個別リンクです。
    RAW
    書かれたコードへの直接のリンクです。
    Packed
    文字列が圧縮された書かれたコードへのリンクです。
    Userscript
    Greasemonkey 等で利用する場合の .user.js へのリンクです。
    Loader
    @require やソースコードが長い場合に多段ロードする Loader コミのコードへのリンクです。
    Metadata
    コード中にコメントで @xxx と書かれたメタデータの JSON です。