在 React 中设置白板
本指南基于基本设置,介绍如何在 React 中设置白板并添加工具栏按钮。
安装
在白板扩展旁边,安装 @tiptap/react:
npm install @tiptap/react设置白板
白板需要三个元素:接收指针和滚轮事件的舞台、其中相机移动的视口以及视口内的编辑器。此设置遵循 示例:
import { EditorContent, useEditor } from '@tiptap/react'
import {
attachStage,
boardContent,
stickyNoteContent,
toCssTransform,
WhiteboardKit,
} from '@tiptap-pro/extension-whiteboard'
import { useEffect, useRef, useState } from 'react'
import { Toolbar } from './Toolbar'
import './whiteboard.css'
const content = boardContent([
stickyNoteContent({ id: 'hello', x: 40, y: 40, text: 'Double-click to write here' }),
])
export default function Whiteboard() {
const stageRef = useRef(null)
const [camera, setCamera] = useState({ x: 0, y: 0, zoom: 1 })
const editor = useEditor({ extensions: [WhiteboardKit], content, immediatelyRender: false })
// Lets the user pan, zoom, draw and select in the stage
useEffect(() => {
if (!editor || !stageRef.current) {
return undefined
}
return attachStage({ stage: stageRef.current, editor, updateCamera: setCamera })
}, [editor])
if (!editor) {
return null
}
return (
<div className="whiteboard">
<Toolbar editor={editor} />
<div className="stage" ref={stageRef}>
<div
className="viewport"
style={{ transform: toCssTransform(camera), '--zoom': String(camera.zoom) }}
>
<EditorContent editor={editor} />
</div>
</div>
</div>
)
}对于 immediatelyRender: false,useEditor 在第一次渲染后创建编辑器,因此在此之前 editor 是 null。 Next.js 需要这个,对于仅在带有 next/dynamic 的浏览器中加载的板也是如此。
attachStage 返回一个再次分离所有内容的函数,因此效果会自行清除。它的 updateCamera 选项采用了一种将旧相机变成新相机的功能,就像 React 状态设置器一样,所以 setCamera 适合。
稳定选项
使 useEditor 的选项在渲染之间保持相同。在组件外部或使用 useMemo 创建一个已配置的套件(例如 WhiteboardKit.configure({ docs }))和内容一次。当选项更改时,useEditor 会再次应用其选项,并且板将变为只读。组件内部配置的套件在每次渲染时都会发生变化,因此白板在 setEditable(true) 之后再次变为只读。
添加工具
白板以 select 工具启动。您的按钮使用 setTool 选择另一个工具,getActiveTool 告诉哪个工具处于活动状态。 toolKeys 保存选择每个工具的默认键:
import { useEditorState } from '@tiptap/react'
import { focusBoard, getActiveTool, toolKeys } from '@tiptap-pro/extension-whiteboard'
const tools = ['select', 'hand', 'note', 'text', 'rectangle', 'ellipse', 'arrow', 'pen', 'eraser']
export function Toolbar({ editor }) {
const activeTool = useEditorState({
editor,
selector: ({ editor: currentEditor }) => getActiveTool(currentEditor.state),
})
return (
<div role="group" aria-label="Tools">
{tools.map((tool) => (
<button
key={tool}
type="button"
aria-pressed={activeTool === tool}
title={`${tool} (${toolKeys[tool].toUpperCase()})`}
onClick={() => {
editor.commands.setTool(tool)
// Focuses the board, so the tool keys keep working
focusBoard(editor.view)
}}
>
{tool}
</button>
))}
</div>
)
}doc 工具添加文档框架,这需要 docs 选项。请参阅文档框架。
添加舞台 CSS
舞台夹住板并保持自身接触。视口位于舞台的左上角并从那里开始缩放:
.stage {
height: 32rem;
overflow: clip;
position: relative;
touch-action: none;
}
.viewport {
left: 0;
position: absolute;
top: 0;
transform-origin: 0 0;
}
/* The board's origin sits in the corner of the viewport, so the editor keeps no margin */
.viewport .tiptap {
margin: 0;
}设计形状
白板添加了工作所需的 CSS,例如白板的尺寸和手柄的位置。颜色、字体和光标取决于您的应用程序。一些规则给出了形状的外观:
[data-type='sticky-note'] {
background: #fff59d;
padding: 12px;
}
[data-shape-outline] path {
fill: #fff;
stroke: #1d1d1d;
stroke-width: 2px;
vector-effect: non-scaling-stroke;
}
/* Arrows and pen lines are drawn in the current color */
[data-type='arrow'],
[data-type='freehand'] {
color: #1d1d1d;
}
/* Divided by the zoom, so outlines and handles keep their size */
[data-shape][data-selected],
[data-shape][data-editing] {
outline: calc(2px / var(--zoom, 1)) solid #6a00f5;
}
[data-shape-handle='resize'],
[data-arrow-handle] {
background: #fff;
border: calc(1.5px / var(--zoom, 1)) solid #6a00f5;
}
[data-selection-box],
[data-shape-preview] {
background: rgba(106, 0, 245, 0.1);
border: 1px dashed #6a00f5;
}形状将其颜色的名称存储在 data-color 中,如 data-color="blue",因此您的 CSS 决定每种颜色的外观。有关板设置的每个属性,请参阅 Styling。