使用LiveBlocks构建React+Redux实时协作白板应用

使用LiveBlocks构建React+Redux实时协作白板应用

liveblocks Liveblocks is a real-time collaboration infrastructure for developers. liveblocks 项目地址: https://gitcode.com/gh_mirrors/li/liveblocks

前言

在现代Web应用中,实时协作功能变得越来越重要。本文将介绍如何利用LiveBlocks这一强大的实时协作框架,结合React和Redux,构建一个功能完善的在线协作白板应用。通过本教程,你将掌握实时数据同步、状态管理和多人协作的核心技术要点。

技术栈概述

本项目采用以下技术组合:

  • React:用于构建用户界面
  • Redux:状态管理工具
  • LiveBlocks:提供实时协作能力
    • 实时数据同步
    • 用户状态管理
    • 历史记录功能

项目初始化与配置

1. 创建项目基础结构

首先创建一个标准的React应用:

npx create-react-app redux-whiteboard

安装必要的依赖包:

npm install redux react-redux @reduxjs/toolkit @liveblocks/client @liveblocks/redux

2. 配置LiveBlocks客户端

src/store.js中初始化LiveBlocks客户端:

import { createClient } from "@liveblocks/client";
import { liveblocksEnhancer } from "@liveblocks/redux";
import { configureStore, createSlice } from "@reduxjs/toolkit";

export const client = createClient({
  publicApiKey: "你的公开API密钥",
});

3. 设置Redux Store

配置Redux store并集成LiveBlocks增强器:

const initialState = {
  shapes: {},
  selectedShape: null,
};

const slice = createSlice({
  name: "state",
  initialState,
  reducers: {
    // 后续添加reducer逻辑
  },
});

export function makeStore() {
  return configureStore({
    reducer: slice.reducer,
    enhancers: [
      liveblocksEnhancer({
        client,
        storageMapping: { shapes: true },
        presenceMapping: { selectedShape: true },
      }),
    ],
  });
}

核心功能实现

1. 房间连接管理

LiveBlocks使用"房间"的概念来隔离不同的协作空间。我们需要在组件挂载时连接房间,卸载时断开连接:

import { useEffect } from "react";
import { useDispatch } from "react-redux";
import { actions } from "@liveblocks/redux";

const roomId = "redux-whiteboard";

function App() {
  const dispatch = useDispatch();

  useEffect(() => {
    dispatch(actions.enterRoom(roomId));
    return () => dispatch(actions.leaveRoom());
  }, [dispatch]);

  // 组件其余部分
}

2. 形状管理

添加矩形

实现添加随机位置和颜色的矩形功能:

const COLORS = ["#DC2626", "#D97706", "#059669", "#7C3AED", "#DB2777"];

const slice = createSlice({
  // ...其他配置
  reducers: {
    insertRectangle: (state) => {
      const shapeId = Date.now().toString();
      const shape = {
        x: Math.floor(Math.random() * 300),
        y: Math.floor(Math.random() * 300),
        fill: COLORS[Math.floor(Math.random() * COLORS.length)],
      };
      state.shapes[shapeId] = shape;
      state.selectedShape = shapeId;
    },
  },
});
删除矩形

实现删除选中矩形的功能:

deleteShape: (state) => {
  if (state.selectedShape) {
    delete state.shapes[state.selectedShape];
    state.selectedShape = null;
  }
},

3. 选择与移动功能

选择矩形

通过用户presence同步选择状态:

onShapePointerDown: (state, action) => {
  state.selectedShape = action.payload;
},
移动矩形

实现矩形拖拽移动功能:

onCanvasPointerMove: (state, action) => {
  if (state.selectedShape && state.shapes[state.selectedShape]) {
    state.shapes[state.selectedShape].x += action.payload.dx;
    state.shapes[state.selectedShape].y += action.payload.dy;
  }
},

4. 用户界面组件

矩形组件
const Rectangle = ({ shape, selectionColor, id }) => {
  const dispatch = useDispatch();

  return (
    <div
      className="rectangle"
      style={{
        transform: `translate(${shape.x}px, ${shape.y}px)`,
        backgroundColor: shape.fill || "#CCC",
        borderColor: selectionColor,
      }}
      onPointerDown={(e) => {
        e.stopPropagation();
        dispatch(onShapePointerDown(id));
      }}
    />
  );
};
主应用组件
function App() {
  const shapes = useSelector((state) => state.shapes);
  const isLoading = useSelector((state) => state.liveblocks.isStorageLoading);
  const others = useSelector((state) => state.liveblocks.others);
  const selectedShape = useSelector((state) => state.selectedShape);
  const dispatch = useDispatch();

  // 处理拖拽移动
  const handlePointerMove = (e) => {
    if (selectedShape) {
      dispatch(
        onCanvasPointerMove({
          dx: e.movementX,
          dy: e.movementY,
        })
      );
    }
  };

  return (
    <>
      <div className="canvas" onPointerMove={handlePointerMove}>
        {Object.entries(shapes).map(([shapeId, shape]) => {
          let selectionColor = "transparent";
          if (selectedShape === shapeId) {
            selectionColor = "blue";
          } else if (
            others.some((user) => user.presence?.selectedShape === shapeId)
          ) {
            selectionColor = "green";
          }

          return (
            <Rectangle
              key={shapeId}
              id={shapeId}
              shape={shape}
              selectionColor={selectionColor}
            />
          );
        })}
      </div>
      <div className="toolbar">
        <button onClick={() => dispatch(insertRectangle())}>添加矩形</button>
        <button
          onClick={() => dispatch(deleteShape())}
          disabled={selectedShape == null}
        >
          删除
        </button>
      </div>
    </>
  );
}

进阶功能扩展

1. 撤销/重做功能

LiveBlocks内置支持操作历史记录,可以轻松实现撤销/重做功能:

import { useUndo, useRedo } from "@liveblocks/react";

function Toolbar() {
  const undo = useUndo();
  const redo = useRedo();

  return (
    <div className="toolbar">
      {/* 其他按钮 */}
      <button onClick={undo}>撤销</button>
      <button onClick={redo}>重做</button>
    </div>
  );
}

2. 用户光标显示

可以通过LiveBlocks的presence功能显示其他用户的光标位置:

function OtherCursors() {
  const others = useOthers();

  return others.map(({ connectionId, presence }) => {
    if (!presence?.cursor) return null;
    return (
      <div
        key={connectionId}
        className="cursor"
        style={{
          transform: `translate(${presence.cursor.x}px, ${presence.cursor.y}px)`,
        }}
      />
    );
  });
}

性能优化建议

  1. 选择性渲染:使用React.memo优化矩形组件,避免不必要的重绘
  2. 批量更新:对于频繁的移动操作,考虑使用防抖或节流
  3. 数据分片:对于大型白板,考虑将画布分区加载
  4. 离线支持:利用LiveBlocks的离线缓存功能提升用户体验

总结

通过本教程,我们实现了一个功能完整的实时协作白板应用,包含以下核心功能:

  • 实时添加/删除矩形
  • 多人协作选择与移动
  • 状态持久化
  • 操作历史记录

LiveBlocks与Redux的结合为构建复杂的协作应用提供了强大而灵活的解决方案。开发者可以基于此基础进一步扩展功能,如添加更多形状类型、实现文本编辑或引入更复杂的协作场景。

这种架构模式不仅适用于白板应用,也可以应用于任何需要实时协作功能的Web应用,如文档编辑器、项目管理工具或设计协作平台等。

liveblocks Liveblocks is a real-time collaboration infrastructure for developers. liveblocks 项目地址: https://gitcode.com/gh_mirrors/li/liveblocks

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

滑隽蔚Maia

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值