Реалізація Signature Pad (електронний підпис) на сайті
Ми часто стикаємося із завданнями, де потрібно зібрати підпис клієнта прямо в браузері: договори, заяви, медичні форми. На одному з проєктів підписи на iPad виглядали розмитими через відсутність підтримки HiDPI. Після налаштування параметрів canvas якість стала прийнятною. Використовуємо бібліотеку signature_pad (8 КБ) з тонким налаштуванням velocityFilterWeight і підтримкою touch. В одному проєкті вдалося досягнути 60 FPS при малюванні стилусом на iPad Pro. Реалізація Signature Pad — це не просто вставка canvas, а цілий ряд технічних рішень: згладжування, HiDPI, експорт, інтеграція з PDF. Наш досвід — 5+ років і 30+ проєктів — гарантує, що підпис працюватиме на будь-яких пристроях. Економія на розробці з нуля — до 3 днів, а вартість інтеграції окупається за 1 місяць економії часу. Близько 30% користувачів заповнюють документи з мобільних пристроїв, тому адаптація під retina-екрани критична.
Типові проблеми при реалізації електронного підпису
-
Розмиті підписи на HiDPI-екранах — canvas за замовчуванням рендериться в низькому розділенні. Рішення: масштабування canvas з урахуванням
devicePixelRatio. -
Нерівномірне згладжування штрихів — при повільному малюванні лінія стає товстою. Налаштування
velocityFilterWeightіthrottleвиправляє ситуацію. - Пустий підпис при сабміті — часто забувають перевіряти
isEmpty(). Валідація на клієнті та сервері обов’язкова. - Експорт з неправильним фоном — фон PNG має бути прозорим для коректного накладання на PDF. Використовуйте
toDataURL('image/png')з прозорістю. - Несумісність з браузерами без Pointer Events — fallback на mouse/touch події.
Як вибрати бібліотеку: signature_pad vs аналоги
Бібліотека signature_pad від Szimek — мінімалістичне рішення (8 КБ), що працює поверх <canvas>. Підтримує Pointer Events, touch, стилус з тиском, експорт у PNG/SVG/JPEG. Аналоги: jSignature (тільки jQuery), SignaturePad (кутастий). Наш вибір — signature_pad за продуктивність і гнучкість.
| Характеристика | signature_pad | jSignature | SignaturePad |
|---|---|---|---|
| Розмір | 8 КБ | 15 КБ | 12 КБ |
| Touch | Так | Так | Так |
| Експорт SVG | Так | Ні | Так |
| Підтримка React | Нативна | Через обгортку | Через обгортку |
| HiDPI | Так (ручне налаштування) | Ні | Ні |
Параметри згладжування підписів
| Параметр | Значення за замовчуванням | Рекомендація |
|---|---|---|
minWidth |
0.5 | Менше — тонша лінія при швидкому русі |
maxWidth |
2.5 | Більше — товща лінія при повільному |
throttle |
16 | ms між точками; менше — плавніше, але більше точок |
velocityFilterWeight |
0.7 | Вага фільтра швидкості; 0 — вимкнено |
Чому SVG кращий за PNG для підписів?
SVG — векторний формат, масштабується без втрати якості, важить у 2–3 рази менше за PNG. Його можна вставити прямо в PDF без перекодування. Бібліотека pdf-lib дозволяє працювати з SVG через пакет svg2pdf. PNG — растровий, але сумісний зі старими системами. Якщо бекенд підтримує SVG, вибирайте його: економія на зберіганні та передачі складе до 60%.
Як ми вирішуємо проблему HiDPI?
Canvas за замовчуванням рендериться з розділенням 1:1. Для чіткого підпису на retina-екранах необхідно збільшити розмір canvas у devicePixelRatio разів і масштабувати контекст. Приклад налаштування:
const canvas = canvasRef.current! const ctx = canvas.getContext('2d')! const dpr = window.devicePixelRatio || 1 canvas.width = canvas.offsetWidth * dpr canvas.height = canvas.offsetHeight * dpr ctx.scale(dpr, dpr) Без цього налаштування підпис буде розмитим на пристроях з DPR > 1.
Як ми реалізуємо Signature Pad: процес і гарантії
- Аналіз вимог: тип підпису (миша/стилус), формат експорту, інтеграція з формою.
- Налаштування бібліотеки: згладжування, HiDPI, колір пера.
- Реалізація UI: кнопки очищення, скасування, збереження.
- Експорт і вставка: генерація SVG/PNG, вбудовування в PDF через
pdf-lib. - Тестування: на мобільних пристроях, планшетах, сенсорних екранах.
- Деплой і документація.
Гарантуємо стабільну роботу і підтримку після впровадження. Зв'яжіться з нами — оцінимо ваш проєкт за 1 день безкоштовно. Економія на папері та логістиці з електронним підписом може досягати 70%.
Приклад інтеграції з PDF (Node.js)
// Node.js, библиотека pdf-lib import { PDFDocument } from 'pdf-lib' async function embedSignatureInPdf( pdfBytes: Uint8Array, signatureDataUrl: string, page: number = 0 ): Promise<Uint8Array> { const pdfDoc = await PDFDocument.load(pdfBytes) const pages = pdfDoc.getPages() const targetPage = pages[page] const signatureBase64 = signatureDataUrl.replace(/^data:image\/png;base64,/, '') const signatureBytes = Buffer.from(signatureBase64, 'base64') const signatureImage = await pdfDoc.embedPng(signatureBytes) const { width, height } = signatureImage.scale(0.5) targetPage.drawImage(signatureImage, { x: 60, y: 60, width, height, opacity: 1, }) return pdfDoc.save() } Що входить в реалізацію Signature Pad?
- Встановлення та налаштування
signature_pad - Підтримка HiDPI та touch-пристроїв
- Експорт у PNG/SVG та вставка в PDF
- Інтеграція з формою (React Hook Form, Vue Form, нативна)
- Валідація підпису (не пустий, мінімальна довжина)
- Документація та навчання команди
- Вихідний код та інструкція з розгортання
Базова реалізація
import SignaturePad from 'signature_pad' import { useEffect, useRef, useCallback } from 'react' interface SignaturePadProps { onSave: (dataUrl: string) => void width?: number height?: number } export function SignatureCanvas({ onSave, width = 500, height = 200 }: SignaturePadProps) { const canvasRef = useRef<HTMLCanvasElement>(null) const padRef = useRef<SignaturePad | null>(null) useEffect(() => { const canvas = canvasRef.current! padRef.current = new SignaturePad(canvas, { minWidth: 0.5, maxWidth: 2.5, penColor: '#1e293b', backgroundColor: 'rgb(255,255,255)', throttle: 16, // ms между точками }) // HiDPI поддержка function resizeCanvas() { const ratio = Math.max(window.devicePixelRatio || 1, 1) canvas.width = canvas.offsetWidth * ratio canvas.height = canvas.offsetHeight * ratio canvas.getContext('2d')!.scale(ratio, ratio) padRef.current!.clear() } resizeCanvas() window.addEventListener('resize', resizeCanvas) return () => { window.removeEventListener('resize', resizeCanvas) padRef.current!.off() } }, []) const handleSave = useCallback(() => { if (!padRef.current) return if (padRef.current.isEmpty()) { alert('Будь ласка, поставте підпис') return } // PNG с прозрачным фоном const dataUrl = padRef.current.toDataURL('image/png') onSave(dataUrl) }, [onSave]) const handleClear = useCallback(() => { padRef.current?.clear() }, []) const handleUndo = useCallback(() => { const data = padRef.current?.toData() if (data && data.length > 0) { data.pop() // Удаляем последний штрих padRef.current?.fromData(data) } }, []) return ( <div className="border rounded-lg overflow-hidden"> <canvas ref={canvasRef} style={{ width, height, touchAction: 'none' }} className="block w-full" /> <div className="flex justify-between p-2 bg-gray-50 border-t"> <div className="flex gap-2"> <button type="button" onClick={handleUndo} className="text-sm text-gray-600 hover:text-gray-900" > Скасувати штрих </button> <button type="button" onClick={handleClear} className="text-sm text-red-500 hover:text-red-700" > Очистити </button> </div> <button type="button" onClick={handleSave} className="px-4 py-1 bg-blue-600 text-white rounded text-sm" > Застосувати підпис </button> </div> </div> ) } Експорт у SVG
SVG кращий за PNG для підписів — масштабується без втрати якості, менше важить, можна вставити прямо в PDF:
const svgData = padRef.current.toSVG() // <svg xmlns="http://www.w3.org/2000/svg" ...>...</svg> // Або через Blob для збереження файлу const blob = new Blob([svgData], { type: 'image/svg+xml' }) const url = URL.createObjectURL(blob) Інтеграція з React Hook Form
import { Controller } from 'react-hook-form' function ContractForm() { const { control, handleSubmit } = useForm<{ name: string signature: string }>() return ( <form onSubmit={handleSubmit(onSubmit)}> <Controller control={control} name="signature" rules={{ required: 'Підпис обов\'язковий' }} render={({ field, fieldState }) => ( <div> <SignatureCanvas onSave={(dataUrl) => field.onChange(dataUrl)} width={500} height={180} /> {fieldState.error && ( <p className="text-red-500 text-sm mt-1">{fieldState.error.message}</p> )} </div> )} /> <button type="submit">Підписати договір</button> </form> ) } Терміни та вартість
Базова інтеграція: 0.5–1 день. З кастомізацією (згладжування, HiDPI, PDF): 1–3 дні. Вартість розраховується індивідуально. Економія на папері та печатці з підписом у PDF — до 70%. Отримайте консультацію з інтеграції електронного підпису — ми підберемо оптимальне рішення під ваш стек і бюджет.







