typecho留言墙开发
为 Typecho 开发便利贴留言墙
前言
在浏览过一些师傅们的博客,看到一些让人眼前一亮的留言墙。
每一条留言都是一个独立的小故事,我认为都很有意义。于是,NoteWall 插件诞生了。
开发时遇到很多坑,记录适配PJAX和优化交互体验的一些细节。
[notewall]
一、 核心视觉:如何画出一张“便利贴”?
首先是视觉设计。便利贴的灵魂在于“非秩序感”和“纸张质感”。
- 随机旋转:现实中的便利贴不可能贴得横平竖直。
- 卷角阴影:利用 CSS 的
border-radius和box-shadow模拟纸张微微翘起的效果。 - 图钉效果:用 CSS 画一个小圆点,配合径向渐变(Radial Gradient)做出金属光泽。
但开发过程中遇到了一个问题:头像变形。
很多 Typecho 主题为了响应式,会强制设定 img { width: 100% }。这导致头像被拉伸成了巨大的方块。即使加了 !important 甚至都被主题样式覆盖。
解决方案
我放弃了 <img /> 标签,改用 div 容器配合 background-image。主题的 CSS 只会匹配图片标签,不会影响普通的 div。
/* 核心代码:防拉伸头像容器 */
.note-avatar-bg {
display: block !important;
width: 40px !important; /* 锁死宽度 */
height: 40px !important; /* 锁死高度 */
border-radius: 50% !important;
/* 关键:使用背景图填充,配合 cover 属性,无论图片比例如何都完美适应 */
background-size: cover;
background-position: center center;
background-repeat: no-repeat;
background-color: #e0e0e0;
}二、 交互体验:赋予便利贴“物理属性”
参考师傅们的留言墙设计了拖拽的效果。
这里没有引入庞大的 jQuery UI 或第三方库,而是用原生 JS 手写了拖拽逻辑。
关键逻辑:
- 随机分布:页面加载时,通过
Math.random()计算每个卡片的left、top和rotate角度。 - 零延迟拖拽:监听
mousedown和touchstart。为了防止鼠标甩得太快导致“脱手”,移动事件必须绑定在document上,而不是元素本身。
// 初始化随机布局片段
for(var i=0; i<notes.length; i++) {
var n = notes[i];
// 计算容器内的随机坐标
var x = Math.random() * (cw - 220);
var y = Math.random() * (ch - 220);
// 给它一个 -3度 到 3度 的随机倾斜,看起来更自然
var r = Math.random() * 6 - 3;
n.style.left = x + "px";
n.style.top = y + "px";
n.style.transform = "rotate(" + r + "deg)";
}三、 攻克痛点:PJAX 与 QQ 头像
在测试阶段,我遇到了两个 Typecho 插件开发中经典的大坑。
1. PJAX 刷新导致的“堆叠事故”
博客启用了 PJAX 无刷新加载。每次点击页面时,JS 不会重新运行,导致便利贴墙没有进行初始化排版,所有卡片都堆在了左上角 (0,0) 的位置。
解决方法:
引入 MutationObserver 监听器。它就像一个“哨兵”,时刻盯着页面 DOM 的变化。只要发现便利贴墙的容器插入到了页面中,就强制执行排版函数。
// 针对 PJAX 的 MutationObserver 监听
var observer = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
if (mutation.addedNodes.length) {
var container = document.getElementById("note-wall-container");
// 只要墙出现了,且还没排版过,立即执行!
if (container && !container.classList.contains("init-done")) {
initNoteWall();
}
}
});
});
observer.observe(document.body, { childList: true, subtree: true });2. QQ 邮箱头像不显示
国内访问 Gravatar 并不稳定,且很多用户只有 QQ 头像。
我在 PHP 中增加了一个正则判断:如果是 QQ 邮箱,直接调用腾讯官方的头像接口;否则使用极客族(Geekzu)的高速 CDN 源。
// PHP 后端判断逻辑
if (strpos($mail, '@qq.com') !== false && preg_match('/^\d+@qq\.com$/', $mail)) {
$qq = str_replace('@qq.com', '', $mail);
// 腾讯官方接口,稳!
$avatarUrl = 'https://q1.qlogo.cn/g?b=qq&nk=' . $qq . '&s=100';
} else {
// 极客族高速源
$avatarUrl = 'https://sdn.geekzu.org/avatar/' . md5($mail) . '?s=100&d=mm';
}四、 逻辑升华:关联评论的“画中画”
最初的版本所有留言都是平级的,看不出谁回复了谁。
为了体现“对话感”,我重写了数据库查询逻辑,使用了 SQL 自连接(Self-Left Join)。
在查询每一条评论时,顺便去查询它 parent ID 对应的父级评论内容。如果存在父级评论,就在便利贴下方渲染一个半透明的“引用条”。
// Typecho 数据库查询构造
$select = $db->select('c.*', 'p.author as parentAuthor', 'p.text as parentText')
->from('table.comments as c')
// 左连接:把 table.comments 表连一遍,取名为 p (parent)
->join('table.comments as p', 'c.parent = p.coid', Typecho_Db::LEFT_JOIN)
->where('c.status = ?', 'approved')
->limit($limit);效果如下图所示,回复的评论会带有一个精致的小尾巴,既不破坏整体美感,又保留了上下文逻辑。
从最初简单的 div 堆叠,到解决 CSS 样式冲突,再到适配 PJAX 和 QQ 头像,NoteWall 最终打磨成了一个勉强还能看的 Typecho 插件,后续上传到github开源。