react-signature
A signature board component for react.
Quick Start
npm install @uiw/react-signature import React, { useRef } from "react"; import Signature from '@uiw/react-signature'; export default function App() { const $svg = useRef(null); const handle = (evn) => { const parentElement = $svg.current while (parentElement.firstChild) { parentElement.removeChild(parentElement.firstChild); } } return ( <> <Signature ref={$svg} /> <button onClick={handle}>Clear</button> </> ); }
Readonly
import React, { useRef, useEffect } from "react"; import Signature, { getStroke, getSvgPathFromStroke, defaultOptions } from '@uiw/react-signature'; const point1 = [[81.546875,38.87890625],[82.34375,37.55078125],[84.27734375,35.2265625],[87.73046875,33.2421875],[94.98046875,30.87890625],[106.29296875,28.6796875],[118.23828125,27.71875],[126.6953125,29.45703125],[134.9375,36.22265625],[141.8359375,47.13671875],[143.9765625,59.53515625],[141.8671875,76],[128.1484375,98.9609375],[106.203125,118.87109375],[95.04296875,125.8203125],[94.5859375,125.04296875],[94.7265625,122.04296875],[97.90234375,115.30859375],[109.28515625,102.3046875],[129.75390625,86.796875],[155.27734375,76.14453125],[184.99609375,71.8828125],[218.16015625,73.33984375],[249.26953125,77.8671875],[264.2421875,81.10546875],[266.51171875,82.35546875],[267.69921875,82.99609375],[267.64453125,83.08203125],[267.73046875,82.6875],[267.73828125,80.4921875],[267.83203125,75.4765625],[268.36328125,66.6484375],[268.84765625,56.4609375],[268.421875,46.88671875],[266.9296875,40.86328125],[264.90625,38.4765625],[262.87890625,37.30859375],[258.68359375,39.13671875],[251.265625,43.80078125],[242.21484375,51.79296875],[232.60546875,63.21484375],[222.6484375,78.53125],[213.01171875,94.58984375],[206.3203125,104.57421875],[201.64453125,109.59375],[198.61328125,111.87109375],[197.24609375,111.3671875],[195.421875,109.23828125],[193.87890625,104.91015625],[193.7265625,99.140625],[198.671875,89.87109375],[214.1015625,75.14453125],[243.25390625,54.8671875],[290.3671875,29.3515625],[329.31640625,10.6875],[338.9609375,6.70703125],[338.5234375,9.07421875],[336.8671875,15.4453125],[331.4296875,28.4140625],[323.234375,43.5234375],[312.70703125,57.9609375],[302.296875,66.97265625],[293.85546875,70.734375],[287.6484375,72.25],[283.45703125,70.9453125],[280.51171875,68.09375],[279.5390625,64.13671875],[280.828125,60.0234375],[284.98828125,56.40625],[294.63671875,54.62109375],[307.8984375,56.17578125],[316.7890625,62.65234375],[317.5859375,74.11328125],[309.1640625,92.50390625],[295.12109375,110.55859375],[282.93359375,123.125],[278.58203125,129.00390625],[279.46875,129.80859375],[285.25390625,129.26953125],[299.71484375,125.71875],[321.41015625,118.89453125],[349.296875,108.58203125],[372.53125,98.8671875],[387.57421875,91],[395.23046875,84.53125],[396.7421875,79.3046875],[395.31640625,75.45703125],[389.86328125,73.57421875],[374.875,77.3203125],[347.36328125,88.46875],[310.44140625,107.75],[273.14453125,130.30078125],[252.08984375,145.77734375],[246.2890625,152.421875],[245.4609375,153.8046875],[245.3671875,153.1640625],[245.78515625,148.46875],[247.5859375,136.29296875],[250.55078125,119.38671875],[253.1796875,107.87890625],[255.4765625,102.29296875],[257.40625,99.61328125],[260.58203125,99.8125],[273.9453125,105.66015625],[296.24609375,118.10546875],[326.40234375,138.03125],[355.63671875,158.3515625],[368.66796875,167.1796875],[373.015625,170.12109375],[374.7578125,170.8203125],[374.73046875,169.78125],[374.6953125,167.3203125],[374.765625,161.75],[375.6328125,154.09375],[377.94921875,146.08984375],[380.94921875,140.23046875],[383.6640625,136.3046875],[385.0546875,134.08203125],[385.19140625,133.45703125],[384.81640625,132.41015625]]; const point2 = [[335.0078125,77.8828125],[335.31640625,77.984375],[336.16796875,78.30078125],[337.640625,79.0546875],[339.65234375,80.44140625],[342.88671875,82.921875],[347.0625,86.28125],[353.9296875,91.546875],[358.88671875,95.125],[359.7734375,95.6875],[360.48828125,96.19921875],[360.2265625,96.42578125]]; export default function App() { const stroke = getStroke(point1, defaultOptions); const pathData = getSvgPathFromStroke(stroke); const pathData2 = getSvgPathFromStroke(getStroke(point2, defaultOptions)); return ( <Signature readonly fill="#6b85e4"> <path d={pathData} /> <path d={pathData2} /> </Signature> ); }
Background Color & Text color
Background transparency can be set with --w-signature-background: transparent;
import React, { useRef } from "react"; import Signature from '@uiw/react-signature'; export default function App() { const $svg = useRef(null); const handle = (evn) => { const parentElement = $svg.current while (parentElement.firstChild) { parentElement.removeChild(parentElement.firstChild); } } return ( <> <Signature ref={$svg} fill="red" style={{ "--w-signature-background": "#e4e6ef" }} /> <button onClick={() => handle()}>Clear</button> </> ); }
Props
import React from 'react'; import { type StrokeOptions } from 'perfect-freehand'; export interface SignatureProps extends React.SVGProps<SVGSVGElement> { prefixCls?: string; options?: StrokeOptions; readonly?: boolean; onPointer?: (points: number[][]) => void; } export default function Signature(props?: SignatureProps): React.JSX.Element;
utils
import { type StrokeOptions } from 'perfect-freehand'; /** * Turn the points returned from perfect-freehand into SVG path data. */ export declare function getSvgPathFromStroke(stroke: number[][]): string; export declare const defaultOptions: StrokeOptions; export declare const getBoundingClientRect: (el: SVGSVGElement | null) => { offsetX: number; offsetY: number; }; export declare const getClinetXY: ({ clientX, clientY }: PointerEvent) => { clientX: number; clientY: number; }; export declare const defaultStyle: React.CSSProperties;
Options
The options object is optional, as are each of its properties.
PropertyTypeDefaultDescriptionsize
number8The base size (diameter) of the stroke.thinning
number.5The effect of pressure on the stroke’s size.smoothing
number.5How much to soften the stroke’s edges.streamline
number.5How much to streamline the stroke.simulatePressure
booleantrueWhether to simulate pressure based on velocity.easing
functiont => tAn easing function to apply to each point’s pressure.start
{ }Tapering options for the start of the line.end
{ }Tapering options for the end of the line.last
booleanfalseWhether the stroke is complete.
Note: When the last
property is true
, the line’s end will be drawn at the last input point, rather than slightly behind it.
The start
and end
options accept an object:
PropertyTypeDefaultDescriptioncap
booleantrueWhether to draw a cap.taper
number or boolean0The distance to taper. If set to true, the taper will be the total length of the stroke.easing
functiont => tAn easing function for the tapering effect.
Note: The cap
property has no effect when taper
is more than zero.
<Signature options={{ size: 6, smoothing: 0.46, thinning: 0.73, streamline: 0.5, easing: (t) => t, simulatePressure: true, last: true, start: { cap: true, taper: 0, easing: (t) => t, }, end: { cap: true, taper: 0, easing: (t) => t, }, }} />
Tip: To create a stroke with a steady line, set thethinning
option to0
.
Tip: To create a stroke that gets thinner with pressure instead of thicker, use a negative number for the thinning
option.
Development
- Dependencies in the installation package and example
npm run install
- To develop, run the self-reloading build:
npm run build # Compile packages 📦 @uiw/react-signature npm run watch # Real-time compilation 📦 @uiw/react-signature
- Run Document Website Environment:
npm run start
- To contribute, please fork repos, add your patch and tests for it (in the
test/
folder) and submit a pull request.
npm run test
Contributors
As always, thanks to our amazing contributors!
Made with contributors.
License
Licensed under the MIT License.