toast组件
As of
This script should not be not be installed directly. It is a library for other scripts to include with the meta directive // @require https://update.greasyfork.org/scripts/548898/1657484/Toast%E7%BB%84%E4%BB%B6%E6%A8%A1%E5%9D%97.js
// ==UserScript==
// @name Toast组件模块
// @namespace http://tampermonkey.net/
// @version 1.0.2
// @description toast组件
// @author Lulu
// @match *
// @grant none
// @noframes
// ==/UserScript==
(function(window) {
'use strict';
// 防止重复加载
if (window.MonkeyToast) {
return;
}
const TOAST_CONFIG = {
maxCount: 5, // 最大同时显示数量
baseOffset: 20, // 基础偏移量(px)
spacing: 10, // 每个Toast之间的间距
defaultDuration: 3000,// 默认显示时长(ms)
animationDuration: 300// 动画过渡时间(ms)
};
// 颜色配置 - 移除蓝色,使用灰色系
const COLORS = {
default: {
background: '#4a4a4a', // 默认深灰色背景
text: '#ffffff', // 白色文字
hoverBackground: '#888888',// 悬停时浅灰色背景
hoverOpacity: 0.7 // 悬停时透明度(30%透明)
}
};
// 存储活跃的Toast (message -> {element, timer})
const activeToasts = new Map();
// 等待显示的Toast队列
const toastQueue = [];
/**
* 显示Toast提示
* @param {string} message - 提示内容
* @param {number} duration - 显示时长(ms),可选
* @param {Object} options - 额外选项,可选
* @param {string} options.backgroundColor - 背景颜色
* @param {string} options.color - 文字颜色
* @param {string} options.hoverBackground - 悬停背景色
* @param {number} options.hoverOpacity - 悬停透明度
*/
function showToast(message, duration = TOAST_CONFIG.defaultDuration, options = {}) {
// 检查是否已达到最大显示数量
if (activeToasts.size >= TOAST_CONFIG.maxCount) {
// 加入队列等待
toastQueue.push({ message, duration, options });
return;
}
// 检查是否为重复消息
if (activeToasts.has(message)) {
return;
}
// 合并默认样式和自定义样式
const bgColor = options.backgroundColor || COLORS.default.background;
const textColor = options.color || COLORS.default.text;
const hoverBgColor = options.hoverBackground || COLORS.default.hoverBackground;
const hoverOpacity = options.hoverOpacity ?? COLORS.default.hoverOpacity; // 使用空值合并运算符处理0的情况
// 创建Toast元素
const toast = document.createElement('div');
toast.className = 'tm-toast';
toast.style.cssText = `
position: fixed;
top: ${TOAST_CONFIG.baseOffset}px;
left: 50%;
transform: translateX(-50%);
background: ${bgColor};
color: ${textColor};
padding: 10px 20px;
border-radius: 5px;
z-index: 999999;
opacity: 1;
transition: all ${TOAST_CONFIG.animationDuration}ms ease;
box-shadow: 0 2px 8px rgba(0,0,0,0.15);
pointer-events: auto;
max-width: 80%;
word-wrap: break-word;
font-size: 14px;
`;
toast.textContent = message;
// 添加到文档
const container = document.body || document.documentElement;
container.appendChild(toast);
// 入场动画
setTimeout(() => {
toast.style.transform = 'translateX(-50%) translateY(0)';
}, 10);
// 记录到活跃列表
const timer = setTimeout(() => {
removeToast(message);
}, duration);
activeToasts.set(message, {
element: toast,
timer,
options,
originalBg: bgColor, // 保存原始背景色用于恢复
hoverBg: hoverBgColor, // 保存悬停背景色
hoverOpacity: hoverOpacity // 保存悬停透明度
});
// 更新所有Toast位置
updateToastPositions();
// 鼠标悬停暂停计时并改变样式
toast.addEventListener('mouseenter', () => {
const toastData = activeToasts.get(message);
if (toastData && toastData.timer) {
clearTimeout(toastData.timer);
toastData.timer = null;
// 应用hover样式:浅灰色背景 + 30%透明度
toast.style.background = toastData.hoverBg;
toast.style.opacity = toastData.hoverOpacity;
}
});
// 鼠标离开恢复计时和样式
toast.addEventListener('mouseleave', () => {
const toastData = activeToasts.get(message);
if (toastData && !toastData.timer) {
toastData.timer = setTimeout(() => {
removeToast(message);
}, duration);
// 恢复原始样式
toast.style.background = toastData.originalBg;
toast.style.opacity = 1;
}
});
}
/**
* 移除指定Toast
* @param {string} message - 要移除的提示内容
*/
function removeToast(message) {
const toastData = activeToasts.get(message);
if (!toastData) return;
const { element, timer } = toastData;
if (timer) clearTimeout(timer);
// 淡出动画
element.style.opacity = 0;
element.style.transform = 'translateX(-50%) translateY(-10px)';
// 动画结束后移除元素
setTimeout(() => {
try {
element.remove();
} catch (e) { /* 忽略已移除的情况 */ }
activeToasts.delete(message);
// 更新位置
updateToastPositions();
// 检查队列并显示下一个
if (toastQueue.length > 0) {
const nextToast = toastQueue.shift();
showToast(nextToast.message, nextToast.duration, nextToast.options);
}
}, TOAST_CONFIG.animationDuration);
}
/**
* 更新所有活跃Toast的位置,实现自动堆叠
*/
function updateToastPositions() {
let currentOffset = TOAST_CONFIG.baseOffset;
// 按添加顺序遍历并更新位置
Array.from(activeToasts.values()).forEach(({ element }) => {
// 设置新位置
element.style.top = `${currentOffset}px`;
// 计算下一个位置(当前元素高度 + 间距)
currentOffset += element.offsetHeight + TOAST_CONFIG.spacing;
});
}
/**
* 清除所有toast
*/
function clearAllToasts() {
// 清除活跃的toast
Array.from(activeToasts.keys()).forEach(message => {
removeToast(message);
});
// 清空队列
toastQueue.length = 0;
}
/**
* 配置toast全局参数
* @param {Object} config - 配置对象
*/
function configToast(config) {
Object.assign(TOAST_CONFIG, config);
}
/**
* 配置全局颜色
* @param {Object} colorConfig - 颜色配置对象
*/
function configColors(colorConfig) {
Object.assign(COLORS.default, colorConfig);
}
// 暴露公共API
window.MonkeyToast = {
show: showToast,
remove: removeToast,
clearAll: clearAllToasts,
config: configToast,
configColors: configColors // 新增颜色配置API
};
})(window);