座標自動維持ボタン(離席用)
by
cutfloss
05/03 [2026/05/03 22:36:02]
押すとその場でグルグル回り続けるボタンが表示される
@@ -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 です。