React useRef获取Canvas时getContext is not a function报错
报错原因
- 你将ref直接绑定到了
react-signature-canvas组件上,此时canvasRef.current拿到的是该组件的实例对象,不是原生canvas DOM元素,组件实例本身不存在getContext方法,直接调用就会触发你看到的类型错误。 - 你注释掉的尺寸调整逻辑本身也存在问题:
getContext返回的是绘图上下文对象,不是DOM元素,不存在style属性,直接给ctx设置style本身就不符合API规范;且直接修改canvas的width属性如果不同步处理画布缩放、重绘逻辑,会导致已绘制的签名拉伸变形。
修复方案
- 修正ref的类型定义,匹配
react-signature-canvas的实例类型,不要直接标注为HTMLCanvasElement - 通过组件实例提供的
getCanvas()方法获取底层原生canvas DOM元素,再执行DOM相关操作 - 调整尺寸时先固定画布原有宽高比,同步处理设备像素比避免绘制模糊,若需要保留已绘制的签名,调整尺寸前先暂存签名数据,尺寸修改完成后重新绘制到画布上,保证内容不丢失、不变形
修复后的完整代码如下:
import { useRef, useEffect } from 'react' import SignaturePad from 'react-signature-canvas' // TS场景可引入对应实例类型 // import type { SignaturePad as SignaturePadRef } from 'react-signature-canvas' const SignatureInput = () => { // TS场景下将ref类型替换为SignaturePadRef const canvasRef = useRef<any>(null) useEffect(() => { if (!canvasRef.current) return // 通过组件实例方法获取原生canvas const canvas = canvasRef.current.getCanvas() const ctxContainer = document.getElementById('ctxContainer') if (!canvas || !ctxContainer) return // 暂存已绘制的签名数据,避免调整尺寸后内容丢失 const existingSignature = canvasRef.current.toData() // 获取父容器宽度,保留原画布宽高比 const containerWidth = ctxContainer.clientWidth const originalRatio = canvas.height / canvas.width // 处理设备像素比,避免高清屏绘制模糊 const dpr = window.devicePixelRatio || 1 // 设置canvas样式宽高 canvas.style.width = '100%' canvas.style.height = `${containerWidth * originalRatio}px` // 设置canvas实际像素宽高 canvas.width = containerWidth * dpr canvas.height = containerWidth * originalRatio * dpr // 校正上下文缩放比例 const ctx = canvas.getContext('2d') if (ctx) { ctx.scale(dpr, dpr) } // 重绘之前暂存的签名 if (existingSignature?.length) { canvasRef.current.fromData(existingSignature) } }, []) const save = () => { if (!canvasRef.current) return // 调用组件实例方法获取签名图片 const signImg = canvasRef.current.toDataURL() console.log('saved!', signImg) } return ( <div id="ctxContainer" className="signatureInput__option-draw"> <SignaturePad ref={canvasRef} canvasProps={{ className: 'signatureInput__option-draw__canvas', }} onEnd={save} /> </div> ) } export default SignatureInput
注意:如果你的父容器尺寸是响应式变化的,需要给上述尺寸调整逻辑添加resize事件监听,在容器尺寸变化时重新执行校正逻辑。
内容的提问来源于stack exchange,提问作者Nick
相关产品推荐
相关产品推荐

