Visx is a library that lets us add graphics to our React app easily.
In this article, we’ll look at how to use it to add a simple whiteboard into our React app.
Install Required Packages
We have to install a few modules.
To get started, we run:
npm i @visx/curve @visx/drag @visx/gradient @visx/responsive @visx/shape
to install the packages.
Create the Whiteboard
We can create the whiteboard by adding the items provided by the modules.
To do this, we write:
import React, { useCallback, useState } from "react";
import { LinePath } from "@visx/shape";
import { useDrag } from "@visx/drag";
import { curveBasis } from "@visx/curve";
import { LinearGradient } from "@visx/gradient";
function Example({ data = [], width, height }) {
const [lines, setLines] = useState(data);
const onDragStart = useCallback(
(currDrag) => {
setLines((currLines) => [
...currLines,
[{ x: currDrag.x, y: currDrag.y }]
]);
},
[setLines]
);
const onDragMove = useCallback(
(currDrag) => {
setLines((currLines) => {
const nextLines = [...currLines];
const newPoint = {
x: currDrag.x + currDrag.dx,
y: currDrag.y + currDrag.dy
};
const lastIndex = nextLines.length - 1;
nextLines[lastIndex] = [...(nextLines[lastIndex] || []), newPoint];
return nextLines;
});
},
[setLines]
);
const {
x = 0,
y = 0,
dx,
dy,
isDragging,
dragStart,
dragEnd,
dragMove
} = useDrag({
onDragStart,
onDragMove,
resetOnStart: true
});
return width < 10 ? null : (
<div className="DragII" style={{ touchAction: "none" }}>
<svg width={width} height={height}>
<LinearGradient id="stroke" from="#ff614e" to="#ffdc64" />
<rect fill="#04002b" width={width} height={height} rx={14} />
{lines.map((line, i) => (
<LinePath
key={`line-${i}`}
fill="transparent"
stroke="url(#stroke)"
strokeWidth={3}
data={line}
curve={curveBasis}
x={(d) => d.x}
y={(d) => d.y}
/>
))}
<g>
{isDragging && (
<rect
width={width}
height={height}
onMouseMove={dragMove}
onMouseUp={dragEnd}
fill="transparent"
/>
)}
{isDragging && (
<g>
<rect
fill="white"
width={8}
height={8}
x={x + dx - 4}
y={y + dy - 4}
pointerEvents="none"
/>
<circle
cx={x}
cy={y}
r={4}
fill="transparent"
stroke="white"
pointerEvents="none"
/>
</g>
)}
<rect
fill="transparent"
width={width}
height={height}
onMouseDown={dragStart}
onMouseUp={isDragging ? dragEnd : undefined}
onMouseMove={isDragging ? dragMove : undefined}
onTouchStart={dragStart}
onTouchEnd={isDragging ? dragEnd : undefined}
onTouchMove={isDragging ? dragMove : undefined}
/>
</g>
</svg>
<style jsx>{`
.DragII {
display: flex;
flex-direction: column;
user-select: none;
}
svg {
margin: 1rem 0;
cursor: crosshair;
}
.deets {
display: flex;
flex-direction: row;
font-size: 12px;
}
.deets > div {
margin: 0.25rem;
}
`}</style>
</div>
);
}
export default function App() {
return (
<div className="App">
<Example width={500} height={300} />
</div>
);
}
In the Example
component, we have the whiteboard.
We have the lines
state to keep track of the lines.
The lines
array is set by the onDragStart
function.
Whenever we start dragging the mouse, we add to the lines
array.
The line starts at the x and y coordinates of the mouse.
The onDragMove
event handler function lets us add the line according to the coordinates of the mouse.
We pass those 2 functions into the useDrag
hook to let us draw lines when dragging.
In the return
statement, we render the LinePath
by calling map
on the lines
array.
data
is passed into the line
function to render the line.
When isDragging
is true
, which is when we’re dragging our mouse, then the circle
is shown to render the marker.
rect
draws a transparent rectangle as we’re dragging.
The style
tag has the styles we want to set for the whiteboard.
Conclusion
We can create a simple whiteboard in our React app easily with the Visx library.