React Konva实现可编辑Text组件遇阻,寻求解决方案
实现React Konva Text组件的可编辑功能
需求说明
- 按下回车键可换行;
- 双击Text(支持在Konva Stage内拖动)时,显示文本框接收用户修改默认文本;
- 双击编辑区域可返回Text组件,修改后的文本需同步到Text内容中;
- 编辑框需与Text组件处于完全相同的X、Y偏移位置。
已尝试方案
使用react-konva-utils中的Html组件,解决原生HTML元素无法跟随React Konva Text组件移动的兼容问题。
遇到的问题
- Html组件支持padding和margin,但不支持直接设置top、left属性;
- 尝试将Text的X值映射为div的margin-top、Y值映射为margin-left,位置匹配效果与预期差距较大。
修正后的代码
import React, { useState, useRef, useEffect, Fragment } from "react"; import { Html } from 'react-konva-utils'; import { Text, Transformer } from "react-konva"; const KText = ({ stage, id, properties, isSelected, onSelect, onChange, setActiveText }) => { const shapeRef = useRef(); const trRef = useRef(); const [toggleEdit, setToggleEdit] = useState(false); const [editText, setEditText] = useState(properties.text); useEffect(() => { if (isSelected) { trRef.current.nodes([shapeRef.current]); trRef.current.getLayer().batchDraw(); } }, [isSelected]); // 同步编辑框内容到外部状态 const handleSave = () => { setToggleEdit(false); setActiveText({ text: editText, id }); }; return ( <> {/* 展示模式:Konva Text组件 */} {!toggleEdit && ( <Fragment> <Text stroke={"black"} strokeWidth={1} onTap={onSelect} onClick={onSelect} onDblClick={() => setToggleEdit(true)} ref={shapeRef} name="text" x={properties.x} y={properties.y} text={properties.text} fontFamily={properties.fontFamily} fontSize={properties.fontSize} fontWeight={properties.fontWeight} fillLinearGradientStartPoint={{ x: 0, y: 0 }} fillLinearGradientEndPoint={{ x: 100, y: 100 }} fillLinearGradientColorStops={[ 0, "rgba(0,0,0,0.7)", 1, "rgba(255,155,255,0.5)" ]} fillPriority={"linear-gradient"} draggable onDragEnd={e => { onChange?.({ ...properties, x: e.target.x(), y: e.target.y(), }); }} onTransformEnd={e => { const node = shapeRef.current; const scaleX = node.scaleX(); const scaleY = node.scaleY(); node.scaleX(1); node.scaleY(1); onChange?.({ ...properties, x: node.x(), y: node.y(), fontSize: node.fontSize() * scaleX, }); }} /> {isSelected && <Transformer ref={trRef} />} </Fragment> )} {/* 编辑模式:绑定到Text节点的Html输入框 */} {toggleEdit && ( <Html node={shapeRef.current} groupProps={{ x: 0, y: 0 }}> <div style={{ padding: '4px', background: '#fff', border: '1px solid #000', borderRadius: '4px', outline: 'none', minWidth: '120px' }} onDoubleClick={() => setToggleEdit(false)} > <textarea value={editText} onChange={(e) => setEditText(e.target.value)} rows="4" cols="30" style={{ border: 'none', resize: 'none', background: 'transparent', fontSize: `${properties.fontSize}px`, fontFamily: properties.fontFamily, fontWeight: properties.fontWeight, outline: 'none' }} autoFocus onKeyDown={(e) => { // 回车键换行,Ctrl+Enter保存(可选) if (e.key === 'Enter' && !e.ctrlKey) { e.stopPropagation(); } else if (e.key === 'Enter' && e.ctrlKey) { handleSave(); } }} /> <button onClick={handleSave} style={{ marginTop: '8px' }}>保存</button> </div> </Html> )} </> ); }; export default KText;
关键修改说明
- 自动位置同步:通过Html组件的
node属性绑定到Text的ref,让编辑框自动跟随Text的位置、缩放等变换,无需手动计算偏移量; - 样式匹配:编辑框的字体大小、字体族等样式与Text组件保持一致,提升视觉一致性;
- 文本同步逻辑:使用本地状态
editText实时保存输入内容,点击保存或Ctrl+Enter时同步到外部状态; - 交互优化:
- 双击Text切换到编辑模式,双击编辑区域切换回展示模式;
- textarea支持回车键换行,Ctrl+Enter快速保存;
- 修复拖拽和缩放后的属性同步逻辑,确保Text组件的位置、字体大小正确更新。
内容的提问来源于stack exchange,提问作者Abhinav Saxena
相关产品推荐
相关产品推荐

