4.26(figma-clone2/4)
好难!今天被困在canvas中了,简单的点击显示图形也没有实现,不知道是画布没选到还是初始化没有做到
1.实现flyReaction
鼠标实现,点击鼠标飞出图标,主要是从官网查文档,官方文档一直是最好的学习方式。
步骤:
1.复制按钮框代码,该文件主要用于选择表情,保存表情
import React from "react";
type Props = {
setReaction: (reaction: string) => void;
};
export default function ReactionSelector({ setReaction }: Props) {
return (
// 将方框放在页面底部固定显示,并阻止默认事件捕获,防止在输入框输入e的时候触发显示表情框
<div
className='absolute bottom-20 left-0 right-0 mx-auto w-fit transform rounded-full bg-white px-2'
onPointerMove={(e) => e.stopPropagation()}
>
{/* css:.w-fit {width: fit-content;}宽度自适应,根据内容宽度撑开 px就是在pading横向*/}
{/* 新建表情按钮,使用封装子组件方式 */}
<ReactionButton reaction="👍" onSelect={setReaction} />
<ReactionButton reaction="🔥" onSelect={setReaction} />
<ReactionButton reaction="😍" onSelect={setReaction} />
<ReactionButton reaction="👀" onSelect={setReaction} />
<ReactionButton reaction="😱" onSelect={setReaction} />
<ReactionButton reaction="🙁" onSelect={setReaction} />
</div>
);
}
// 声明表情按钮组件,注册传入参数,参数类型
function ReactionButton(
{
reaction,
onSelect,
}: {
reaction: string;
onSelect: (reaction: string) => void;
}
) {
// 每个按钮的小方块
return (
<button
className="transform select-none p-2 text-xl transition-transform hover:scale-150 focus:scale-150 focus:outline-none"
onPointerDown={() => onSelect(reaction)}
>
{/* .select-none {user-select: none;} 禁用用户对元素内容的选择操作
.text-xl {font-size: 1.25rem;line-height: 1.75rem;}font 20px line -28px
*/}
{reaction}
</button>
);
}
2.新建flyReaction文件,该文件主要根据事件展示表情
export default function FlyingReaction({ x, y, timestamp, value }: Props) {
return (
<div
className={`pointer-events-none absolute select-none ${
styles.disappear
} text-${(timestamp % 5) + 2}xl ${styles["goUp" + (timestamp % 3)]}`}
style={{ left: x, top: y }}
>
//展示的图标,一左一右,展示
<div className={styles["leftRight" + (timestamp % 3)]}>
<div className="-translate-x-1/2 -translate-y-1/2 transform">
{value}
</div>
</div>
</div>
);
}
好吧,我今晚没看懂这个代码,待我深入一下再总结
3.在liver主画布中将设置reaction的函数setReactions
// 设置当前反射状态,当前光标模式,
const setReactions = useCallback((reaction: string) => {
setCursorState({ mode: CursorMode.Reaction, reaction,isPressed: false })
}, [])
//这个干嘛的,还没懂
const borcast = useBroadcastEvent()
// 只显示4秒内的reaction
useInterval(() => {
setReaction((reactions) =>
reactions.filter((reaction) => reaction.timestamp > Date.now() - 4000)
);
}, 1000);
useInterval(() => {
// 为什么在这里面可以有cursorState.reaction?
if (cursorState.mode === CursorMode.Reaction &&
cursorState.isPressed && cursor) {
setReaction((reactions) => reactions.concat([
{
point: { x: cursor.x, y: cursor.y },
value: cursorState.reaction,
timestamp: Date.now()
}
]))
borcast({
x:cursor.x,
y:cursor.y,
value:cursorState.reaction,
})
}
}, 500);
2.live Avatar Stack实时头像堆栈
效果展示
1.新建用户头像栈,用户头像容器
import { useOthers, useSelf } from "@/liveblocks.config";
import { Avatar } from "./Avatar";
import styles from "./index.module.css";
import { generateRandomName } from "@/lib/utils";
import { useMemo } from "react";
const ActiveUsers = () => {
// 获取其他用户数据
const users = useOthers();
// 获取当前用户数据,自己的
const currentUser = useSelf();
// 使用useMemo缓存组件,防止移动鼠标后渲染
const haresi = useMemo(() => {
const hasMoreUsers = users.length > 3;
return (
// 展示两个用户图标
<main className='flex items-center justify-center gap-1'>
{/* .gap-1 {gap: 0.25rem} Css的栅栏布局,占1/4父元素 */}
<div className="flex pl-3">
{currentUser && (
// 当前用户
<Avatar name="You" otherStyles="border-[3px] border-primary-green" />
)}
{users.slice(0, 3).map(({ connectionId, info }) => {
// 只展示前三个,遍历用户列表,展示用户id和图标
return (
<Avatar key={connectionId} name={generateRandomName()} otherStyles="" />
);
})}
{/* 当用户人数大于3时候展示其他效果 */}
{hasMoreUsers && <div className={styles.more}>+{users.length - 3}</div>}
</div>
</main>
);
}, [users.length])
return haresi
}
export default ActiveUsers
用户头像
const IMAGE_SIZE = 48;
export function Avatar({ name, otherStyles }: { otherStyles: string; name: string }) {
return (
//// 用户头像容器样式,
<div className={`${styles.avatar} ${otherStyles} h-9 w-9 `} data-tooltip={name}>
{/* 图标src,样式,src用不了,官网数据没了,需要自己的图片,不用也行 */}
<Image
src={`https://liveblocks.io/avatars/avatar-${Math.floor(Math.random() * 30)}.png`}
fill
className={styles.avatar_picture}
alt={name}
/>
</div>
);
}
3.canvas
画布,暂时没完成,
1.在live中新建canvas画布,
///live.tsx
<canvas ref={canvasRef} />
///page.tsx
// 创建画布
const canvasRef = useRef<HTMLCanvasElement>(null);
// 创建fabric,可以对画布进行操作,在安装fabric记得安装@type/fabric类型声明
const fabricRef = useRef<fabric.Canvas | null>(null);
// 判断是否在方块中
const isDrawing = useRef(false)
// 获取图形元素
const shapeRef = useRef<fabric.Object | null>(null)
// 选择图形元素
const selectedShapeRef = useRef<string | null>('rectangle')
// 实现异步操作,初始化画布,绑定事件
useEffect(() => {
console.log(fabricRef);
// 初始化
const canvas = initializeFabric({ canvasRef, fabricRef })
// 画布绑定点击事件
canvas.on("mouse:down", (options) => {
handleCanvasMouseDown({
options,
canvas,
isDrawing,
shapeRef,
selectedShapeRef,
});
});
// 监听窗口是否改变大小
window.addEventListener("resize", () => {
handleResize({
canvas: fabricRef.current,
});
})
}, [])
......
<Live canvasRef={canvasRef} />
.....
反正这个也不是什么技术拓展文章,能看到这句你牛。
4.算法有效的括号
先上代码,力扣的题,他的问题有问题,不要瞎想,必须是前后顺序闭合,不能够交叉闭合,[({})]这样就行了
var isValid = function(s) {
// 还是老样子,结构数组变量
const stack = [],
map = {
"(":")",
"{":"}",
"[":"]"
};
// 遍历给定元素
for(const x of s) {
// 遍历键名,存在,则入栈,
if(x in map) {
stack.push(x);
continue;
};
// 如果最后一个值与当前闭合则出栈
if(map[stack.pop()] !== x) return false;
}
// 当栈为空则说明全部闭合
return !stack.length;
};