今天晚上摸鱼把一些网上的花火表情包搜集了一下,转化了一下表情包的尺寸大小,图床是用的是论坛官方图床,点击即可插入表情包,给论坛带来更多欢愉
增加了预览功能,修复了部分脚本错误
|
使用演示 GIF

表情包示例

代码 复制全部内容,在油猴插件里新建脚本粘贴即可~
// ==UserScript==
// @name 花火御用表情包面板 一键爆炸 + 悬浮/长按预览(修复)
// @namespace https://deepflood.com/
// @version 0.3.4
// @description 自定义表情;点击即发并复制;鼠标悬浮/触摸长按预览;修复预览偶发不出现与前置空格错位;移除重复声明导致的启动失败
// @author Sparkle
// @match *://www.deepflood.com/*
// @match *://www.nodeseek.com/*
// @grant none
// @icon https://cdn.nodeimage.com/i/IEyH4NN7T0gzIj2GNDLEkMNfxi5e7S5R.png
// ==/UserScript==
(function () {
'use strict';
// === 默认表情 ===
const defaultEmojiList = [
"https://cdn.nodeimage.com/i/kiTPNRkkUMXrFbcqiQjInY997EXX8ugQ.png",
"https://cdn.nodeimage.com/i/yNwUePEGVy7u15Dav0dsnKe44c3YJvAK.gif",
"https://cdn.nodeimage.com/i/YtrI0qXlSjH1vjIVEPCIFrra7p93S9kn.webp",
"https://cdn.nodeimage.com/i/BAyT6QNxu41dsi2jLwmhfE2qaRiIxyY6.webp",
"https://cdn.nodeimage.com/i/p4NKizdYxK7YtdBjHmuYqaogU4QBYaUd.webp",
"https://cdn.nodeimage.com/i/vTcG6KHwda3ZzsWCYHxPIXI36cPvFgaR.avif",
"https://cdn.nodeimage.com/i/MdE5TIiLs1sogvre8Rc1TunNkROvwXbR.png",
"https://cdn.nodeimage.com/i/DLckJTHxmoTysBWShRGhXcmY3bmB56TG.png",
"https://cdn.nodeimage.com/i/rM7RVynnxChinGGZXXc1kzcJNaPsxx09.webp",
"https://cdn.nodeimage.com/i/nJj1NJMN3FrvCjiYxLjDGnnMQrOuodX0.webp",
"https://cdn.nodeimage.com/i/dI2NfaGlg4LMHsJJYbj6Deg7AvSrvfPW.webp",
"https://cdn.nodeimage.com/i/v8bbgwPmDhn8CJfRLoJfTExECZXY9cAJ.avif",
"https://cdn.nodeimage.com/i/s6yvTaNndMqSW2237VHvVaGbGUrtR482.avif",
"https://cdn.nodeimage.com/i/IqtoK3LNY6UH8F49SMH6PnrdHbJDxbL5.avif",
"https://cdn.nodeimage.com/i/EcfVbQVEhIQuhhZfz3GfmUbXbOugaTKs.avif",
"https://cdn.nodeimage.com/i/jjmCWuOeedjWFyUMI1tM9KDLpK0YzMuU.avif",
"https://cdn.nodeimage.com/i/d2Ybd1xx8ejNkfu7Rvv6YXHEEcj4dDuE.avif",
"https://cdn.nodeimage.com/i/a53uo9CgZlL78BqIResfFmVJSk6YSVpi.webp",
"https://cdn.nodeimage.com/i/2Jmuo2O6rd2BAwUv5IPnzrqOndF4GaXH.avif",
"https://cdn.nodeimage.com/i/aa5u2pyhmLgxiqNye1Bm600GdFgQwnoH.avif",
"https://cdn.nodeimage.com/i/sg1QIipECwsa0mVAXhGtDEUtrXz32I9j.avif",
"https://cdn.nodeimage.com/i/Z24cydZ2nUL5jYmIxYbx8PTCgHUYWHVk.avif",
"https://cdn.nodeimage.com/i/ixMkhZuRNt39ObpRpY5XTyNdyEpVenGh.avif",
"https://cdn.nodeimage.com/i/qJbzpaPkUflHQxf0aUszKZrEgQzta5jd.webp",
"https://cdn.nodeimage.com/i/Bjm9HjOzzaXTqNUWYx1yZexGs0lIN9so.webp",
"https://cdn.nodeimage.com/i/3TyPkkNXQ2iWsT2cuUBb5lqNEXkhQwMu.gif",
"https://cdn.nodeimage.com/i/Rs1eZBpod5QsSoV1kIv1husXtopA6AcI.png",
"https://cdn.nodeimage.com/i/Gy9MjsQyA7tW6kkRBux6Vurjw8tIuGAX.png",
"https://cdn.nodeimage.com/i/4dQT9ylVI51vzTvwdWAzZalZCOXS3RYc.png",
"https://cdn.nodeimage.com/i/5Z2V8HGqrFYzTdJDdoNAvIX4WQIQnBZz.png",
"https://cdn.nodeimage.com/i/yIdwRnUajcvuVOlHaO5647PdMcRua2yS.png",
"https://cdn.nodeimage.com/i/IEyH4NN7T0gzIj2GNDLEkMNfxi5e7S5R.png",
"https://cdn.nodeimage.com/i/Q05BMry7UbL1QzNAAr0b4CO1jwB7po5v.png",
"https://cdn.nodeimage.com/i/jCc6NDHOEP8AmpWifMkopF0w1vcNJVMC.png",
"https://cdn.nodeimage.com/i/cLmlEhOL5qv1r7UNW6MgRuYePyupd35k.png",
"https://cdn.nodeimage.com/i/5qP7UR9sOgTBVH63ZXf5pWPiMq18iTyK.png",
"https://cdn.nodeimage.com/i/43KwtgzDgaU2C91WXwp4ItBKHjsevHQE.png",
"https://cdn.nodeimage.com/i/mRtWtxLeHYXdhM4yXmKAvPAZlkTzHfSl.gif",
"https://cdn.nodeimage.com/i/Jrn7JvYVY7dAhQHMrQ0i2ixfaDDvxGYU.png"
];
// === 全局 ===
const STORAGE_KEY = 'hanabi_custom_emojis';
let isDeleteMode = false;
let customEmojiList = [];
const PREVIEW_MAX_W = 260;
const PREVIEW_MAX_H = 260;
const PREVIEW_OFFSET = 14;
// 预览状态 & 防抖
let lastPointerX = 0, lastPointerY = 0;
let hideTimer = null;
// === 存储 ===
function loadCustomEmojis() {
try { return JSON.parse(localStorage.getItem(STORAGE_KEY) || '[]'); }
catch { return []; }
}
function saveCustomEmojis(emojis) {
try { localStorage.setItem(STORAGE_KEY, JSON.stringify(emojis)); } catch {}
}
// === 输入框 ===
function findInputElement() {
const selectors = [
'textarea[name="message"]','textarea[placeholder*="输入"]','textarea[placeholder*="回复"]','textarea[placeholder*="说点什么"]',
'input[type="text"][name="message"]','input[type="text"][placeholder*="输入"]',
'.editor-input textarea','.message-input textarea','.chat-input textarea','.reply-box textarea','.comment-box textarea',
'textarea.form-control','textarea','input[type="text"]'
];
for (const s of selectors) {
const el = document.querySelector(s);
if (el && !el.disabled && !el.readOnly && el.offsetWidth > 0 && el.offsetHeight > 0) return el;
}
const f = document.activeElement;
return (f && (f.tagName === 'TEXTAREA' || (f.tagName === 'INPUT' && f.type === 'text'))) ? f : null;
}
function insertTextAtCursor(el, text) {
if (!el) return false;
el.focus();
if (document.execCommand) document.execCommand('insertText', false, text);
else if (el.setRangeText) {
const s = el.selectionStart || 0, e = el.selectionEnd || 0;
el.setRangeText(text, s, e, 'end');
} else {
const s = el.selectionStart || el.value.length;
el.value = el.value.slice(0, s) + text + el.value.slice(el.selectionEnd || el.value.length);
el.selectionStart = el.selectionEnd = s + text.length;
}
el.dispatchEvent(new Event('input', { bubbles: true }));
return true;
}
// === Toast ===
function showToast(msg) {
const t = document.createElement('div');
t.textContent = msg;
Object.assign(t.style, {
position:'fixed', bottom:'90px', right:'20px', padding:'10px 20px', borderRadius:'12px',
background:'rgba(0,0,0,0.65)', color:'#fff', fontWeight:'500', fontSize:'15px',
boxShadow:'0 4px 12px rgba(0,0,0,0.2)', zIndex:'100002',
opacity:'0', transform:'translateY(10px)', transition:'opacity .2s ease, transform .2s ease'
});
document.body.appendChild(t);
requestAnimationFrame(()=>{ t.style.opacity='1'; t.style.transform='translateY(0)'; });
setTimeout(()=>{ t.style.opacity='0'; t.style.transform='translateY(10px)'; setTimeout(()=>t.remove(),220); },1500);
}
// === 复制 ===
function copyToClipboard(text){
if(navigator.clipboard?.writeText) return navigator.clipboard.writeText(text).then(()=>true).catch(()=>false);
try{
const ta=document.createElement('textarea');
ta.value=text; ta.style.position='fixed'; ta.style.opacity='0';
document.body.appendChild(ta); ta.select();
const ok=document.execCommand('copy'); document.body.removeChild(ta);
return Promise.resolve(ok);
}catch{ return Promise.resolve(false); }
}
// === 浮动按钮 ===
const toggleBtn = document.createElement('img');
toggleBtn.src = "https://cdn.nodeimage.com/i/IEyH4NN7T0gzIj2GNDLEkMNfxi5e7S5R.png";
Object.assign(toggleBtn.style,{
position:'fixed', right:'15px', bottom:'15px', width:'60px', height:'60px', borderRadius:'50%',
cursor:'pointer', zIndex:'99998', background:'rgba(255,255,255,0.4)', backdropFilter:'blur(10px) saturate(180%)',
border:'1px solid rgba(255,255,255,0.5)', boxShadow:'0 4px 18px rgba(0,0,0,0.25)', transition:'transform .25s, box-shadow .25s'
});
toggleBtn.addEventListener('mouseenter',()=>{ toggleBtn.style.transform='scale(1.1)'; toggleBtn.style.boxShadow='0 6px 20px rgba(0,0,0,.35)'; });
toggleBtn.addEventListener('mouseleave',()=>{ toggleBtn.style.transform='scale(1)'; toggleBtn.style.boxShadow='0 4px 18px rgba(0,0,0,.25)'; });
document.body.appendChild(toggleBtn);
// === 面板 ===
const panel = document.createElement('div');
panel.id='emoji-panel';
Object.assign(panel.style,{
position:'fixed', right:'80px', bottom:'80px', width:'240px', maxHeight:'50vh',
display:'none', flexDirection:'column', padding:'10px',
background:'rgba(255,255,255,0.15)', border:'1px solid rgba(255,255,255,0.4)', borderRadius:'16px',
backdropFilter:'blur(12px) saturate(180%)', boxShadow:'0 10px 30px rgba(0,0,0,0.25)', zIndex:'99999',
color:'#222', transition:'opacity .3s ease, transform .3s ease', transform:'translateY(10px)'
});
const style = document.createElement('style');
style.textContent = `
#emoji-panel * { box-sizing: border-box; }
#emoji-panel-grid::-webkit-scrollbar { width: 6px; }
#emoji-panel-grid::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.4); border-radius: 3px; }
#emoji-panel-grid::-webkit-scrollbar-thumb:hover { background: rgba(255,255,255,0.6); }
.emoji-item img:hover { transform: scale(1.08); box-shadow: 0 4px 10px rgba(0,0,0,0.2); }
#emoji-panel.delete-mode .emoji-item[data-is-custom="true"] > img { border: 2px dashed #ff4757; opacity: 0.8; cursor: pointer; }
#emoji-panel.delete-mode .emoji-item[data-is-custom="true"]:hover > img { opacity: 1; box-shadow: 0 0 10px #ff4757; }
#emoji-panel.delete-mode .emoji-item:not([data-is-custom="true"]) { filter: grayscale(80%); opacity: 0.5; pointer-events: none; }
.control-button { background: rgba(255,255,255,0.3); border: none; padding: 4px 8px; font-size: 12px; border-radius: 6px; color: white; cursor: pointer; transition: background .2s; }
.control-button:hover { background: rgba(255,255,255,0.5); }
#emoji-preview {
position: fixed; top: 0; left: 0; display: none; padding: 8px; border-radius: 12px;
background: rgba(0,0,0,0.45); backdrop-filter: blur(10px) saturate(160%);
box-shadow: 0 10px 30px rgba(0,0,0,0.35); z-index: 100001; pointer-events: none;
opacity: 0; transform: translate3d(0,0,0) scale(0.98); transition: opacity .12s ease, transform .12s ease;
}
#emoji-preview.show { opacity: 1; transform: translate3d(0,0,0) scale(1); }
#emoji-preview img { display:block; max-width:${PREVIEW_MAX_W}px; max-height:${PREVIEW_MAX_H}px; border-radius:8px; object-fit:contain; }
`;
document.head.appendChild(style);
const header = document.createElement('div');
Object.assign(header.style,{display:'flex',justifyContent:'space-between',alignItems:'center',marginBottom:'8px',color:'#fff',fontWeight:'600',textShadow:'0 1px 3px rgba(0,0,0,0.4)',cursor:'move',flexShrink:'0'});
header.innerHTML = `<span>🌸 花火表情包面板</span><span style="cursor:pointer;font-size:16px;">✖</span>`;
header.querySelector('span:last-child').onclick = ()=>{ panel.style.display='none'; };
panel.appendChild(header);
const grid = document.createElement('div');
grid.id='emoji-panel-grid';
Object.assign(grid.style,{display:'flex',flexWrap:'wrap',justifyContent:'flex-start',overflowY:'auto',flexGrow:'1'});
panel.appendChild(grid);
// 控制区(创建即绑定事件,不再在后面 querySelector/重复声明)
const controls = document.createElement('div'); controls.style.marginTop='8px'; controls.style.flexShrink='0';
const urlInput = document.createElement('input');
Object.assign(urlInput.style,{width:'100%',padding:'6px',borderRadius:'6px',border:'1px solid rgba(255,255,255,0.4)',background:'rgba(0,0,0,0.1)',color:'white',marginBottom:'6px'});
urlInput.placeholder = '粘贴图片链接...';
const buttonContainer = document.createElement('div'); Object.assign(buttonContainer.style,{display:'flex',justifyContent:'space-between'});
const addButton = document.createElement('button'); addButton.textContent='✓ 添加表情'; addButton.className='control-button';
const deleteModeButton = document.createElement('button'); deleteModeButton.textContent='🗑️ 删除模式'; deleteModeButton.className='control-button';
buttonContainer.append(addButton, deleteModeButton);
controls.append(urlInput, buttonContainer);
panel.appendChild(controls);
document.body.appendChild(panel);
// === 预览元素 ===
const preview = document.createElement('div'); preview.id='emoji-preview';
const previewImg = document.createElement('img');
preview.appendChild(previewImg); document.body.appendChild(preview);
function clamp(v, min, max){ return Math.max(min, Math.min(max, v)); }
function positionPreview(x, y){
preview.style.left = `${x + PREVIEW_OFFSET}px`;
preview.style.top = `${y + PREVIEW_OFFSET}px`;
const rect = preview.getBoundingClientRect();
const nx = clamp(rect.left, 8, window.innerWidth - rect.width - 8);
const ny = clamp(rect.top , 8, window.innerHeight - rect.height - 8);
preview.style.left = `${nx}px`; preview.style.top = `${ny}px`;
}
function showPreview(src, x, y){
if (!src) return;
if (hideTimer) { clearTimeout(hideTimer); hideTimer = null; }
lastPointerX = x; lastPointerY = y;
previewImg.onload = ()=>{ positionPreview(lastPointerX, lastPointerY); };
previewImg.onerror = ()=>{ showToast('图片预览加载失败'); };
previewImg.decoding = 'async';
previewImg.src = src;
preview.style.display = 'block';
requestAnimationFrame(()=>{ positionPreview(x, y); preview.classList.add('show'); });
}
function movePreview(x, y){
if (preview.style.display !== 'block') return;
lastPointerX = x; lastPointerY = y;
positionPreview(x, y);
}
function hidePreview(){
if (hideTimer) clearTimeout(hideTimer);
hideTimer = setTimeout(()=>{
preview.classList.remove('show');
setTimeout(()=>{ preview.style.display='none'; }, 120);
}, 120);
}
// === 渲染 ===
function renderEmojis(){
grid.innerHTML = '';
const createEmojiItem = (url, isCustom)=>{
const item = document.createElement('div'); item.className='emoji-item'; if (isCustom) item.dataset.isCustom='true';
const img = document.createElement('img');
img.src = url; img.loading='lazy';
Object.assign(img.style,{width:'64px',height:'64px',borderRadius:'10px',margin:'4px',objectFit:'cover',cursor:'pointer',transition:'transform .2s, box-shadow .2s'});
// 点击发送(去掉多余空格)
let suppressNextClick = false;
img.onclick = ()=>{
if (suppressNextClick) { suppressNextClick = false; return; }
if (isDeleteMode && isCustom){
if (confirm('确定要删除这个自定义表情吗?')){
customEmojiList = customEmojiList.filter(e=>e!==url);
saveCustomEmojis(customEmojiList);
renderEmojis(); showToast('🗑️ 表情已删除!');
}
return;
}
const markdown = ``; // 不带前后空格,避免错位
copyToClipboard(markdown);
const input = findInputElement();
if (input && insertTextAtCursor(input, markdown)){
setTimeout(()=>{
const ev = new KeyboardEvent('keydown',{key:'Enter',code:'Enter',keyCode:13,which:13,bubbles:true,cancelable:true});
input.dispatchEvent(ev);
},50);
showToast('✨ 表情包已发送并复制!');
}
};
// 悬浮预览(鼠标)
img.addEventListener('mouseenter', e=>{ showPreview(url, e.clientX, e.clientY); });
img.addEventListener('mousemove', e=>{ movePreview(e.clientX, e.clientY); });
img.addEventListener('mouseleave', hidePreview);
// 长按预览(触摸)
let pressTimer=null, longPress=false, startX=0, startY=0;
const LONG_MS=450, MOVE_CANCEL=10;
img.addEventListener('contextmenu', e=>e.preventDefault());
img.addEventListener('touchstart', e=>{
if (e.touches.length!==1) return;
const t = e.touches[0];
startX=t.clientX; startY=t.clientY; longPress=false; suppressNextClick=false;
pressTimer=setTimeout(()=>{ longPress=true; suppressNextClick=true; showPreview(url, startX, startY); }, LONG_MS);
}, {passive:true});
img.addEventListener('touchmove', e=>{
if (e.touches.length!==1) return;
const t=e.touches[0], dx=t.clientX-startX, dy=t.clientY-startY;
if (!longPress){
if (Math.hypot(dx,dy)>MOVE_CANCEL){ clearTimeout(pressTimer); pressTimer=null; }
} else {
movePreview(t.clientX, t.clientY);
}
}, {passive:true});
function endTouch(){ clearTimeout(pressTimer); pressTimer=null; if (longPress) hidePreview(); }
img.addEventListener('touchend', endTouch, {passive:true});
img.addEventListener('touchcancel', endTouch, {passive:true});
item.appendChild(img); grid.appendChild(item);
};
defaultEmojiList.forEach(u=>createEmojiItem(u,false));
customEmojiList.forEach(u=>createEmojiItem(u,true));
}
// === 控件事件:直接使用创建时的 addButton / deleteModeButton ===
addButton.onclick = ()=>{
const url = urlInput.value.trim();
if (!/^https?:\/\//i.test(url)) return showToast('❌ 请输入有效的图片链接!');
if (customEmojiList.includes(url)) return showToast('😅 这个表情已经添加过啦!');
customEmojiList.push(url); saveCustomEmojis(customEmojiList); renderEmojis();
urlInput.value=''; showToast('✅ 自定义表情已添加!'); grid.scrollTop = grid.scrollHeight;
};
deleteModeButton.onclick = ()=>{
isDeleteMode = !isDeleteMode;
panel.classList.toggle('delete-mode', isDeleteMode);
deleteModeButton.textContent = isDeleteMode ? '✓ 完成删除' : '🗑️ 删除模式';
deleteModeButton.style.background = isDeleteMode ? 'rgba(255,71,87,0.5)' : 'rgba(255,255,255,0.3)';
};
toggleBtn.onclick = ()=>{
const show = panel.style.display==='none' || !panel.style.display;
panel.style.display = show ? 'flex' : 'none';
panel.style.opacity = show ? '1' : '0';
panel.style.transform = show ? 'translateY(0)' : 'translateY(10px)';
if (!show && isDeleteMode){
isDeleteMode=false; panel.classList.remove('delete-mode');
deleteModeButton.textContent='🗑️ 删除模式'; deleteModeButton.style.background='rgba(255,255,255,0.3)';
}
if (!show) { if (hideTimer) { clearTimeout(hideTimer); hideTimer=null; } preview.style.display='none'; preview.classList.remove('show'); }
};
// === 初始化 ===
customEmojiList = loadCustomEmojis();
renderEmojis();
console.log('🌸 花火表情包面板 已加载(修复预览 & 去除多余空格 & 无重复声明)');
})();
|
@fastoo #26 很不错的表情包搜集网站,我后面会考虑整合的 谢谢佬推荐
好东西
论坛也是应该添加更多表情包了
@spocel #3 哈哈是的,
我也忍了很久了哈哈哈,这个好好玩
膜拜大神
太可爱了谢谢大佬