feat(comments): 匿名评论 + 表情最近使用;fix: 自动填充聚焦提示 & 邮件模板回退原版 & 返回顶部改 CSS 动画

- 匿名评论:comments.is_anonymous 列;对外昵称/头像/网址/徽标/等级/IP归属全部脱敏,
  真实身份留 users 表(回复通知照发、autofill 不受污染);邮件昵称也用「匿名」防泄露
- 表情包最近使用:面板顶部「最近」栏,localStorage 记录 8 个,零侵入复用 Artalk 插入逻辑
- 匿名开关插在 textarea 与底栏之间独立一行,不挤移动端底栏
- 自动填充:提示改 focusin 触发(原 input+sessionStorage 一次性门导致永不触发);
  已填昵称/网址时不覆盖并弹提示
- 邮件模板 UI 回退原版深色气泡设计,保留表情内联尺寸/主题文案等逻辑修复
- 返回顶部:rAF 自绘改 CSS scroll-behavior(滚动期临时挂类),修移动端掉帧
This commit is contained in:
zqlit committed 2026-10-05 09:02:23 +08:00
1 parent 090262d278
commit 215929a67f
11 files changed
+587 -207

No files matched your search

+36 -23
View File
@@ -1,27 +1,40 @@
// 自绘平滑滚动:浏览器原生 behavior:'smooth' 又快又硬(约 300ms 且不可调),
// 还会被系统的「减少动态效果」降级成瞬移。用 rAF + easeInOutCubic 自绘,
// 时长随距离自适应,滚动一旦被用户滚轮/触摸打断立即让位。
window.smoothScrollTo = function(targetY, duration) {
var startY = window.scrollY;
var diff = targetY - startY;
if (Math.abs(diff) < 2) return;
// 短距离快、长距离慢一些,但都 clamp 在 420~950ms,保证观感一致
var dur = duration || Math.min(950, Math.max(420, Math.abs(diff) * 0.38));
if (window.__smoothScrollRAF) cancelAnimationFrame(window.__smoothScrollRAF);
var t0 = null, canceled = false;
var cancel = function() { canceled = true; };
window.addEventListener('wheel', cancel, { once: true, passive: true });
window.addEventListener('touchstart', cancel, { once: true, passive: true });
var ease = function(t) { return t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2; };
var step = function(ts) {
if (canceled) return;
if (t0 === null) t0 = ts;
var p = Math.min(1, (ts - t0) / dur);
window.scrollTo(0, startY + diff * ease(p));
if (p < 1) window.__smoothScrollRAF = requestAnimationFrame(step);
else window.__smoothScrollRAF = null;
// 平滑滚动 —— 纯 CSS 实现(scroll-behavior: smooth)。
// 之前用 rAF + window.scrollTo 自绘动画:每一帧都在主线程上强制重排重绘,
// 移动端长页面(尤其带 backdrop-filter / 大图的)掉帧明显,非常卡。
// CSS 平滑滚动由浏览器合成器接管,不占 JS 主线程,移动端丝滑得多。
// 做法:只在滚动期间临时给 <html> 挂 .css-smooth-scroll 类,滚完就摘 ——
// 避免全局常开平滑滚动干扰 PJAX 切页回顶、锚点跳转这些场景。
window.smoothScrollTo = function(targetY) {
targetY = Math.max(0, Math.floor(targetY || 0));
if (Math.abs(window.scrollY - targetY) < 2) return;
var root = document.documentElement;
// 上一次滚动还没收尾就直接再来一次:先摘干净旧状态
if (window.__smoothScrollCleanup) window.__smoothScrollCleanup();
var reduced = false;
try { reduced = window.matchMedia('(prefers-reduced-motion: reduce)').matches; } catch (e) {}
if (reduced) { window.scrollTo(0, targetY); return; } // 用户要求减少动态效果:瞬移
root.classList.add('css-smooth-scroll');
window.scrollTo(0, targetY); // 类在位,浏览器自动走平滑滚动
var done = function () {
if (window.__smoothScrollTimer) { clearTimeout(window.__smoothScrollTimer); window.__smoothScrollTimer = null; }
window.removeEventListener('scrollend', done);
window.removeEventListener('wheel', done, { passive: true } );
window.removeEventListener('touchstart', done, { passive: true });
root.classList.remove('css-smooth-scroll');
window.__smoothScrollCleanup = null;
};
window.__smoothScrollRAF = requestAnimationFrame(step);
window.__smoothScrollCleanup = done;
// scrollend(Chrome 114+/FF 109+)在滚动真正停下的瞬间触发;
// 不支持的浏览器用超时兜底摘类。滚轮/触摸会打断 CSS 平滑滚动,
// 提前收尾把类摘掉,不影响用户接着手动滚。
window.addEventListener('scrollend', done, { once: true });
window.addEventListener('wheel', done, { once: true, passive: true });
window.addEventListener('touchstart', done, { once: true, passive: true });
window.__smoothScrollTimer = setTimeout(done, 1000);
};
window.refreshFloatingTools = function() {