Greasy Fork is available in English.
可拖动、默认折叠、记忆位置状态、修复坐标BUG、防误触
// ==UserScript==
// @name 手机可拖动折叠回到顶部|修复版
// @namespace http://tampermonkey.net/
// @version 2.0
// @description 可拖动、默认折叠、记忆位置状态、修复坐标BUG、防误触
// @match *://*/*
// @grant none
// @run-at document-end
// ==/UserScript==
(function() {
'use strict';
const STORAGE_KEY = "back_top_float_data";
// 防重复注入
if (document.querySelector("#dragFloatWrap")) return;
// ===== 修复1:增加JSON解析容错 try/catch =====
let saveData;
try {
const local = localStorage.getItem(STORAGE_KEY);
saveData = local ? JSON.parse(local) : null;
} catch (e) {
saveData = null;
}
// 默认配置
const defaultData = {
right: 16,
bottom: 100,
isFold: true
};
saveData = Object.assign({}, defaultData, saveData);
// 外层容器
const wrap = document.createElement("div");
wrap.id = "dragFloatWrap";
wrap.style.cssText = `
position: fixed;
right: ${saveData.right}px;
bottom: ${saveData.bottom}px;
z-index: 999999;
user-select: none;
touch-action: none;
box-sizing: border-box;
`;
// 折叠开关按钮
const foldBtn = document.createElement("div");
foldBtn.style.cssText = `
width: 40px;
height: 40px;
line-height: 40px;
text-align: center;
background: #1677ff;
color: #fff;
border-radius: 50%;
font-size: 18px;
box-shadow: 0 2px 8px rgba(0,0,0,0.2);
box-sizing: border-box;
`;
// 功能区容器
const contentBox = document.createElement("div");
contentBox.style.cssText = `
position: absolute;
bottom: 50px;
right: 0;
display: none;
box-sizing: border-box;
`;
// 回到顶部按钮
const topBtn = document.createElement("div");
topBtn.style.cssText = `
width: 46px;
height: 46px;
line-height: 46px;
text-align: center;
background: #22c55e;
color: #fff;
border-radius: 50%;
font-size: 20px;
margin-bottom: 8px;
box-shadow: 0 2px 8px rgba(0,0,0,0.2);
box-sizing: border-box;
`;
topBtn.innerText = "↑";
// 结构挂载
contentBox.appendChild(topBtn);
wrap.appendChild(contentBox);
wrap.appendChild(foldBtn);
document.body.appendChild(wrap);
// 初始化折叠状态
function initFoldState() {
if (saveData.isFold) {
contentBox.style.display = "none";
foldBtn.innerText = "☰";
} else {
contentBox.style.display = "block";
foldBtn.innerText = "✕";
}
}
initFoldState();
// 折叠切换 + 保存状态
foldBtn.addEventListener("click", (e) => {
e.stopPropagation();
saveData.isFold = !saveData.isFold;
initFoldState();
localStorage.setItem(STORAGE_KEY, JSON.stringify(saveData));
});
// 回到顶部
topBtn.addEventListener("click", (e) => {
e.stopPropagation();
window.scrollTo({
top: 0,
behavior: "smooth"
});
});
// ===== 修复2:标准坐标 + 拖动阈值防误触 =====
let isDrag = false;
let startX = 0, startY = 0;
let initRight = 0, initBottom = 0;
const MOVE_THRESHOLD = 8; // 移动阈值,小于则判定为点击
wrap.addEventListener("touchstart", (e) => {
const rect = wrap.getBoundingClientRect();
startX = e.touches[0].clientX;
startY = e.touches[0].clientY;
initRight = rect.right;
initBottom = document.documentElement.clientHeight - rect.bottom;
isDrag = false;
});
wrap.addEventListener("touchmove", (e) => {
const dx = startX - e.touches[0].clientX;
const dy = startY - e.touches[0].clientY;
// 超过阈值判定为拖动
if (Math.abs(dx) > MOVE_THRESHOLD || Math.abs(dy) > MOVE_THRESHOLD) {
isDrag = true;
}
wrap.style.right = `${initRight + dx}px`;
wrap.style.bottom = `${initBottom - dy}px`;
});
wrap.addEventListener("touchend", () => {
// 拖动结束才保存位置
if (isDrag) {
const rect = wrap.getBoundingClientRect();
saveData.right = document.documentElement.clientWidth - rect.right;
saveData.bottom = document.documentElement.clientHeight - rect.bottom;
localStorage.setItem(STORAGE_KEY, JSON.stringify(saveData));
}
isDrag = false;
});
})();