一、简介
在 ToC 的项目中,点赞功用是一个常用的的功用,在干流视频网站中,点赞作用做的十分优异。
二、挑选技术与作用完成
三、创立项目安装依靠
npx create-remix app
pnpm add uuid
四、款式重置
- global.css
* {
padding: 0;
margin: 0;
}
root 中引进重置款式
import css from '~/styles/global.css'
export const links: LinksFunction = () => [
{ rel: "stylesheet", href: css },
...(cssBundleHref ? [{ rel: "stylesheet", href: cssBundleHref }] : []),
];
五、辅助函数
5.1) 辅助函数:emoji 随机生成
export function getRandomEmoji() {
// 随机挑选一个起始点(Unicode 规模中的值)
const start = 0x1F600; // 起始点
const end = 0x1F64F; // 完毕点
// 生成随机的 Unicode 编码
const randomCodePoint = Math.floor(Math.random() * (end - start 1)) start;
// 将 Unicode 编码转换为对应的 emoji 并返回
return String.fromCodePoint(randomCodePoint);
}
从 emoji 的Unicode 规模中获取一个随机的 emoji 替代抖音中图画。
5.2) 辅助函数:特定时间内记载次数
在特点时间(好比 300ms)内履行记载一次,调用一次加 1,如果 300 ms 内,超越 10 次,输出 x10
。当最有一次点击超越 300ms 后,没有从头点击,次数归 0。
export function huntCount(instance: MangerHunterCount, time: number) {
let timer: any;
return function () {
if (timer) {
clearTimeout(timer);
timer = setTimeout(() => {
console.log("reset");
instance.reset();
timer = null;
}, time);
} else {
timer = setTimeout(() => {
console.log("reset");
instance.reset();
timer = null;
}, time);
}
instance.addOne();
};
}
export class MangerHunterCount {
count: number;
constructor() {
this.count = 0;
}
addOne() {
this.count = 1;
}
reset() {
this.count = 0;
}
}
六、移动端
6.1) 逻辑
- 移动端在点击屏幕位置有 pop 弹出作用,一起又连击数字作用(连击超越 10 次)。
- 移动端点击屏幕,点赞作用从右下角底部接连点赞接连 pop 出来,然后躲藏。
6.2) TSX 完成
import type { MetaFunction } from "@remix-run/node";
import { useState } from "react";
import styles from "~/styles/index.module.css";
import * as uuid from "uuid";
import { getRandomEmoji } from "~/utils/index";
export const meta: MetaFunction = () => {
return [
{ title: "New Remix App" },
{ name: "description", content: "Welcome to Remix!" },
];
};
export default function Index() {
const [list, setList] = useState<any>([]);
return (
<div className={styles.page}>
<h1>Welcome to Remix</h1>
<div className={styles.wrap}>
<div className={styles.listWrap}>
{list.map((li: any) => {
return (
<div
onAnimationEnd={() => {
setList((pre: any) => pre.filter((l: any) => l.id !== li.id));
}}
key={li.id}
className={styles.emoji}
>
{li.content}
</div>
);
})}
</div>
<div
className={styles.heart}
onClick={() => {
setList([
...list,
{ id: uuid.v4(), content: getRandomEmoji() || "⛄" },
]);
}}
>
❤️
</div>
</div>
</div>
);
}
- list 列表维护点赞的 pop。
- ❤️ 点击之后,运用 uuid 增加随机的 emoji 的图画。
- 当 pop 动画完毕之后,更具 uuid 过滤对应的值, 直到列表清空。
6.3) CSS
.page {
display: flex;
flex-direction: column;
align-items: center;
width: 100%;
height: 90vh;
cursor: pointer;
background-color: antiquewhite;
}
.wrap {
position: relative;
display: flex;
margin-top: 300px;
}
.listWrap {
position: relative;
}
.heart {
position: relative;
top: 0px;
display: flex;
cursor: pointer;
font-size: 30px;
}
.heart:active {
position: relative;
top: 0px;
display: flex;
cursor: pointer;
font-size: 30px;
transform: scale(1.2);
}
.emoji {
position: absolute;
animation-duration: 3s;
animation-name: pop;
animation-fill-mode: forwards;
cursor: pointer;
font-size: 30px;
}
@keyframes pop {
from {
transform: translateY(-30px) scale(1);
opacity: 0.6;
}
50% {
transform: translateY(-200px) scale(2);
opacity: 1;
}
to {
transform: translateY(-300px) scale(1);
opacity: 0;
}
}
css 中定义了 pop 动画,这个动画在完毕时运用 forwards
特点操控在最终一帧。
七、PC 端
7.1) 逻辑
- 没有连击作用
- PC 端与移动的作用不一样,pop 的呈现位置时 PC 端点击时,根据点击位置确定的
- Pop 作用是进入变大,突然变小,然后渐进式变大(一起透明度变低,然后消失)
- PC 端在接连点击 10 次以上,会显示
x10
字样提示 - 接连点击监听 dbclick 不在合理,应该自己模拟 dbclick 点击事情
7.2) TSX
import type { MetaFunction } from "@remix-run/node";
import { useEffect, useState } from "react";
import styles from "~/styles/client.pc.module.css";
import * as uuid from "uuid";
import { getRandomEmoji } from "~/utils/index";
import { MangerHunterCount, huntCount } from "~/utils/hunt-count";
const inst = new MangerHunterCount();
const fn = huntCount(inst, 2000);
let clicks = 0;
let timer: any = null;
export const meta: MetaFunction = () => {
return [
{ title: "New Remix App" },
{ name: "description", content: "Welcome to Remix!" },
];
};
export default function Index() {
const [list, setList] = useState<any>([]);
function handleClick(e: any) {
const { clientX, clientY } = e;
clicks ;
if (clicks === 1) {
timer = setTimeout(function() {
// 单击事情
clicks = 0;
// 在这里履行点赞操作
console.log("单击点赞");
}, 300); // 设置延迟时间,这里是300毫秒
} else {
clearTimeout(timer);
// 双击事情
clicks = 0;
fn();
setList((pre: any) => {
return [
...pre,
{
id: uuid.v4(),
content: getRandomEmoji(),
x: clientX - 10,
y: clientY - 10,
count: inst.count,
},
];
});
}
}
useEffect(() => {
window.addEventListener("click", handleClick);
// eslint-disable-next-line react-hooks/exhaustive-deps
return () => {
window.removeEventListener("click", handleClick)
}
}, []);
return (
<div className={styles.page}>
{/* <h1>Welcome to Remix</h1> */}
<div className={styles.wrap}>
<div className={styles.listWrap}>
{list.map((li: any) => {
return (
<div
onAnimationEnd={() => {
setList((pre: any) => pre.filter((l: any) => l.id !== li.id));
}}
key={li.id}
style={{
top: li.y,
left: li.x,
}}
className={styles.emoji}
>
{li.content}
<span className={styles.count}>
{li.count >= 10 ? "x" li.count : null}
</span>
</div>
);
})}
</div>
</div>
</div>
);
}
7.3) css
.page {
display: flex;
flex-direction: column;
align-items: center;
width: 100%;
height: 90vh;
cursor: pointer;
background-color: antiquewhite;
}
.wrap {
position: relative;
display: flex;
width: 100%;
height: 500px;
}
.listWrap {
position: relative;
display: flex;
background-color: aqua;
}
.emoji {
position: absolute;
animation-name: popnew;
animation-duration: 0.9s;
animation-fill-mode: forwards;
animation-timing-function: ease-in-out;
cursor: pointer;
font-size: 30px;
user-select: none;
}
@keyframes popnew {
from {
transform: scale(2);
opacity: 0.08;
}
15% {
transform: scale(1);
opacity: 1;
}
80% {
transform: translateY(-20px) scale(1) rotate(30deg);
opacity: 1;
}
to {
transform: translateY(-100px) scale(5);
opacity: 0;
}
}
.count {
position: absolute;
top: -20px;
color: #fff;
font-size: 18px;
}
八、小结
本文首要完成根据 React 的前端点赞作用,涉及不少的知识点, css 动画与过渡,操控 css 动画的行为,emoji 随机生成。本文是对于点赞基本探究与完成,期望对读者有所协助。