最近在重构博客,想要添加一些新功能。平时有看 Josh W. Comeau (opens in a new tab) 的个人网站,他的每篇文章右侧都会有一个心形按钮,用户通过点击次数来表达对文章的喜爱程度。让我们来尝试实现这个有趣的点赞功能吧!
绘制点赞图标
点赞按钮的核心是 SVG
主要由两部分组成:
- 两个爱心形状 ❤️ 的
path
,一个为前景,一个为背景 - 一个遮罩
mask
,引用rect
作为遮罩区域
首先使用 defs
标签定义一个 id 为 heart
的爱心形状元素,在后续任何地方都可以使用 use
标签来复用这个“组件”。
其次使用 mask
标签定义了一个 id 为 mask
的遮罩元素,通过 rect
标签设置了一个透明的矩形作为遮罩区域。
最后使用一个 use
标签引用了之前定义的 heart
图形元素作为默认的初始颜色,使用另一个 use
标签,同样引用 heart
图形元素,并使用 mask
属性引用了之前定义的遮罩元素,用于实现填充颜色的遮罩效果。
制作点赞动画
接下来实现随着点赞数量递增时爱心逐渐被填充的效果,我们可以借助 CSS 中 transfrom 的 translateY
属性来完成。设置最多点击次数(这里我设置为 5 次)通过 translateY
来移动遮罩的位置完成填充,也就是说,读者需要点击 5 次才能看到完整的红色爱心形状 ❤️ 的点赞按钮。
除此之外我们还可以为点赞按钮添加更有趣交互效果:
- 每次点击时右侧会出现『 +1 』字样
- 用户在点击第 3 次的时候,填充爱心形状 ❤️ 点赞按钮的同时,还会向四周随机扩散 mini 爱心 💗
这里可以用 Framer Motion (opens in a new tab) 动画库来帮助我们实现动画效果。
animate([
...sparklesReset,
['button', { scale: 0.9 }, { duration: 0.1 }],
...sparklesAnimation,
['.counter-one', { y: -12, opacity: 0 }, { duration: 0.2 }],
['button', { scale: 1 }, { duration: 0.1, at: '<' }],
['.counter-one', { y: 0, opacity: 1 }, { duration: 0.2, at: '<' }],
['.counter-one', { y: -12, opacity: 0 }, { duration: 0.6 }],
...sparklesFadeOut,
])
具体效果如下:
数据持久化
想要让不同用户看到一致的点赞数据,我们需要借助数据库来保存每一个用户的点赞次数和该文章的总获赞次数。每当用户点击一次按钮,就会发送一次 POST
请求,将用户的 IP 地址和当前点赞的文章 ID (这里我使用的文章标题,可以替换为任意文章唯一标识) 存入数据库,同时返回当前的用户合计点赞次数和该文章的总获赞次数。
export async function POST(req: NextRequest, { params }: ParamsProps) {
const res = await req.json()
const slug = params.slug
const count = Number(res.count)
const ip = getIP(req)
const sessionId = slug + '___' + ip
try {
const [post, user] = await Promise.all([
db.insert(pages)
.values({ slug, likes: count })
.onConflictDoUpdate({
target: pages.slug,
set: { likes: sql`pages.likes + ${count}` },
})
.returning({ likes: pages.likes }),
db.insert(users)
.values({ id: sessionId, likes: count })
.onConflictDoUpdate({
target: users.id,
set: { likes: sql`users.likes + ${count}` },
})
.returning({ likes: users.likes })
])
return NextResponse.json({
post_likes: post[0].likes || 0,
user_likes: user[0]?.likes || 0
});
} catch (error) {
return NextResponse.json({ error }, { status: 400 })
}
}
同理,当用户再次进入该页面时,发起 GET
请求,获取当前点赞状态并及时渲染到页面。
回顾总结
点赞功能在互联网应用中十分广泛,自己手动尝试实现这个功能还是挺有趣的。本文从三方面详细介绍了这一实现过程:
- 绘制点赞图标:
SVG
的各种属性应用 - 制作点赞动画:
framer-motion
动画库的使用 - 数据持久化:数据库查询
如果这篇文章对你有帮助,试试点击 爱心 按钮吧!
阅读更多
在 Electron 中实现下载文件实时显示进度条2021/05/08
15 分钟香港丝滑开户流程2024/06/28