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.