87 lines
4.0 KiB
JSON
87 lines
4.0 KiB
JSON
{
|
||
"slug": "signaturepad",
|
||
"end": "react",
|
||
"curated": true,
|
||
"summary": "React 端手写签名板:canvas 上同时接鼠标与触摸事件,支持撤销、清除与导出尺寸提示。",
|
||
"usage": [
|
||
"组件不接 props,笔画数据全部放在 ref 里(strokes 存已完成笔画、cur 存当前笔画),避免每次移动都触发重渲染。",
|
||
"坐标计算 pos() 先减 canvas 的 getBoundingClientRect() 偏移,触摸时取 e.touches[0] 的 clientX / clientY,因此画布滚动或缩放后仍能对齐。",
|
||
"按下即 push 一条新笔画并置 hasInk 为 true 以隐藏灰字占位,移动时 lineTo 加 stroke 边画边长进当前笔画。",
|
||
"撤销是 pop 掉最后一笔后整幅重绘,重绘时统一设置 strokeStyle、lineWidth、lineCap 与 lineJoin,所以线的外观只由 redraw 决定。",
|
||
"canvas 尺寸在挂载时按父容器 getBoundingClientRect 设置,窗口 resize 时重新适配并重绘,卸载时移除监听。",
|
||
"鼠标与触摸各自绑三组事件,但共用 down / move / up 三个处理函数。"
|
||
],
|
||
"bestPractices": [
|
||
"hasInk 与 saved 是仅有的两个 state,其余全在 ref 中,这是移动端高频 move 事件下避免卡顿的关键。",
|
||
"触摸事件里调了 preventDefault,否则移动端会连带滚动页面导致画出的线断裂。",
|
||
"resize 会重置 canvas 的 width / height,这两个赋值本身就会清空位图,所以 fit 里必须跟着调 redraw 才能保住已有笔画。",
|
||
"保存按钮只回显画布尺寸,没有真正导出数据,接后端需要补 toDataURL 并把 base64 作为表单字段提交。"
|
||
],
|
||
"apiCarrier": {
|
||
"kind": "props",
|
||
"note": "React 端本实现无公开 props,载体是 canvas 上的原生事件 props(onMouseDown / onMouseMove / onMouseUp 与 onTouchStart / onTouchMove / onTouchEnd)及三颗按钮的 onClick;笔画与画布尺寸都在内部 ref。",
|
||
"table": [
|
||
{
|
||
"name": "onMouseDown",
|
||
"type": "(e) => void",
|
||
"desc": "鼠标按下开始一笔,preventDefault 后记录起点"
|
||
},
|
||
{
|
||
"name": "onMouseMove",
|
||
"type": "(e) => void",
|
||
"desc": "鼠标移动时把当前点接入当前笔画并立即描边"
|
||
},
|
||
{
|
||
"name": "onMouseUp",
|
||
"type": "() => void",
|
||
"desc": "鼠标抬起结束当前笔画"
|
||
},
|
||
{
|
||
"name": "onTouchStart",
|
||
"type": "(e) => void",
|
||
"desc": "触摸开始,与鼠标共用 down 处理函数"
|
||
},
|
||
{
|
||
"name": "onTouchMove",
|
||
"type": "(e) => void",
|
||
"desc": "触摸移动时取第一个触点坐标续画"
|
||
},
|
||
{
|
||
"name": "onTouchEnd",
|
||
"type": "() => void",
|
||
"desc": "触摸结束,与鼠标共用 up 处理函数"
|
||
},
|
||
{
|
||
"name": "onClick",
|
||
"type": "() => void",
|
||
"desc": "撤销 / 清除 / 保存三颗按钮各自的点击回调"
|
||
}
|
||
]
|
||
},
|
||
"a11y": [
|
||
"canvas 本身没有任何可访问名与替代文本,也没有 tabindex,键盘与读屏用户完全无法签名。",
|
||
"三颗按钮是原生 button,键盘可操作,但撤消与清除后的结果没有 aria-live 通报。",
|
||
"灰字占位请在此处签名是纯 div,不参与无障碍树,无法告知屏幕阅读器这是签名区域。"
|
||
],
|
||
"resources": [
|
||
{
|
||
"file": "frameworks/SignaturePad.jsx",
|
||
"kind": "React 实现",
|
||
"desc": "SignaturePad.jsx —— 本端实现全文(结构、事件与状态逻辑)"
|
||
},
|
||
{
|
||
"file": "frameworks/SignaturePad.css",
|
||
"kind": "样式文件",
|
||
"desc": "frameworks/SignaturePad.css —— 本端 kole-* 类名与 is-* 状态类的样式定义"
|
||
}
|
||
],
|
||
"exampleIds": [
|
||
"signaturepad-ex-1"
|
||
],
|
||
"notes": [
|
||
"事件绑定见 frameworks/SignaturePad.jsx:75-80,日志坐标换算见第 21-25 行。",
|
||
"撤销 / 清除 / 保存见 frameworks/SignaturePad.jsx:43-45,重绘样式见第 47-60 行。",
|
||
"挂载时适配画布与 resize 监听见 frameworks/SignaturePad.jsx:62-67。"
|
||
]
|
||
}
|