用户不能直接编辑 Konva.Text
内容,原因有很多 原因。实际上,画布 API 并不为此目的而设计。
可以在画布上模拟文本编辑(通过绘制闪烁的光标、模拟选择等)。
Konva 并不支持这种情况。我们建议在画布外部使用原生 DOM 元素(如 input
或 textarea
)来编辑用户输入。
在这里,我们将创建两个演示。基本演示用于理解技术的概念。更复杂的演示则用于覆盖更多边缘案例的真实应用。
如果您想启用完整的富文本编辑功能,请参见 富文本演示。
使用说明:双击文本以进行编辑。输入一些内容。按下回车键。
简单演示:
显示简单演示的源代码!
<html>
<head>
<script src="https://unpkg.com/konva@9.3.18/konva.min.js"></script>
<meta charset="utf-8" />
<title>Konva Editable Text on html5 canvas Demo</title>
<style>
body {
margin: 0;
padding: 0;
overflow: hidden;
background-color: #f0f0f0;
}
</style>
</head>
<body>
<div id="container"></div>
<script>
var width = window.innerWidth;
var height = window.innerHeight;
var stage = new Konva.Stage({
container: 'container',
width: width,
height: height,
});
var layer = new Konva.Layer();
stage.add(layer);
var textNode = new Konva.Text({
text: 'Some text here',
x: 50,
y: 50,
fontSize: 20,
});
layer.add(textNode);
textNode.on('dblclick dbltap', () => {
// create textarea over canvas with absolute position
// first we need to find position for textarea
// how to find it?
// at first lets find position of text node relative to the stage:
var textPosition = textNode.getAbsolutePosition();
// then lets find position of stage container on the page:
var stageBox = stage.container().getBoundingClientRect();
// so position of textarea will be the sum of positions above:
var areaPosition = {
x: stageBox.left + textPosition.x,
y: stageBox.top + textPosition.y,
};
// create textarea and style it
var textarea = document.createElement('textarea');
document.body.appendChild(textarea);
textarea.value = textNode.text();
textarea.style.position = 'absolute';
textarea.style.top = areaPosition.y + 'px';
textarea.style.left = areaPosition.x + 'px';
textarea.style.width = textNode.width();
textarea.focus();
textarea.addEventListener('keydown', function (e) {
// hide on enter
if (e.keyCode === 13) {
textNode.text(textarea.value);
document.body.removeChild(textarea);
}
});
});
</script>
</body>
</html>
复杂演示:
显示复杂演示的源代码!
<html>
<head>
<script src="https://unpkg.com/konva@9.3.18/konva.min.js"></script>
<meta charset="utf-8" />
<title>Konva Editable Text on html5 canvas Demo</title>
<style>
body {
margin: 0;
padding: 0;
background-color: #f0f0f0;
}
</style>
</head>
<body>
<div id="container"></div>
<script>
var width = window.innerWidth;
var height = window.innerHeight;
var stage = new Konva.Stage({
container: 'container',
width: width,
height: height,
});
var layer = new Konva.Layer();
stage.add(layer);
var textNode = new Konva.Text({
text: 'Some text here',
x: 50,
y: 80,
fontSize: 20,
draggable: true,
width: 200,
});
layer.add(textNode);
var tr = new Konva.Transformer({
node: textNode,
enabledAnchors: ['middle-left', 'middle-right'],
// set minimum width of text
boundBoxFunc: function (oldBox, newBox) {
newBox.width = Math.max(30, newBox.width);
return newBox;
},
});
textNode.on('transform', function () {
// reset scale, so only with is changing by transformer
textNode.setAttrs({
width: textNode.width() * textNode.scaleX(),
scaleX: 1,
});
});
layer.add(tr);
textNode.on('dblclick dbltap', () => {
// hide text node and transformer:
textNode.hide();
tr.hide();
// create textarea over canvas with absolute position
// first we need to find position for textarea
// how to find it?
// at first lets find position of text node relative to the stage:
var textPosition = textNode.absolutePosition();
// so position of textarea will be the sum of positions above:
var areaPosition = {
x: stage.container().offsetLeft + textPosition.x,
y: stage.container().offsetTop + textPosition.y,
};
// create textarea and style it
var textarea = document.createElement('textarea');
document.body.appendChild(textarea);
// apply many styles to match text on canvas as close as possible
// remember that text rendering on canvas and on the textarea can be different
// and sometimes it is hard to make it 100% the same. But we will try...
textarea.value = textNode.text();
textarea.style.position = 'absolute';
textarea.style.top = areaPosition.y + 'px';
textarea.style.left = areaPosition.x + 'px';
textarea.style.width = textNode.width() - textNode.padding() * 2 + 'px';
textarea.style.height =
textNode.height() - textNode.padding() * 2 + 5 + 'px';
textarea.style.fontSize = textNode.fontSize() + 'px';
textarea.style.border = 'none';
textarea.style.padding = '0px';
textarea.style.margin = '0px';
textarea.style.overflow = 'hidden';
textarea.style.background = 'none';
textarea.style.outline = 'none';
textarea.style.resize = 'none';
textarea.style.lineHeight = textNode.lineHeight();
textarea.style.fontFamily = textNode.fontFamily();
textarea.style.transformOrigin = 'left top';
textarea.style.textAlign = textNode.align();
textarea.style.color = textNode.fill();
rotation = textNode.rotation();
var transform = '';
if (rotation) {
transform += 'rotateZ(' + rotation + 'deg)';
}
var px = 0;
// also we need to slightly move textarea on firefox
// because it jumps a bit
var isFirefox =
navigator.userAgent.toLowerCase().indexOf('firefox') > -1;
if (isFirefox) {
px += 2 + Math.round(textNode.fontSize() / 20);
}
transform += 'translateY(-' + px + 'px)';
textarea.style.transform = transform;
// reset height
textarea.style.height = 'auto';
// after browsers resized it we can set actual value
textarea.style.height = textarea.scrollHeight + 3 + 'px';
textarea.focus();
function removeTextarea() {
textarea.parentNode.removeChild(textarea);
window.removeEventListener('click', handleOutsideClick);
textNode.show();
tr.show();
tr.forceUpdate();
}
function setTextareaWidth(newWidth) {
if (!newWidth) {
// set width for placeholder
newWidth = textNode.placeholder.length * textNode.fontSize();
}
// some extra fixes on different browsers
var isSafari = /^((?!chrome|android).)*safari/i.test(
navigator.userAgent
);
var isFirefox =
navigator.userAgent.toLowerCase().indexOf('firefox') > -1;
if (isSafari || isFirefox) {
newWidth = Math.ceil(newWidth);
}
var isEdge =
document.documentMode || /Edge/.test(navigator.userAgent);
if (isEdge) {
newWidth += 1;
}
textarea.style.width = newWidth + 'px';
}
textarea.addEventListener('keydown', function (e) {
// hide on enter
// but don't hide on shift + enter
if (e.keyCode === 13 && !e.shiftKey) {
textNode.text(textarea.value);
removeTextarea();
}
// on esc do not set value back to node
if (e.keyCode === 27) {
removeTextarea();
}
});
textarea.addEventListener('keydown', function (e) {
scale = textNode.getAbsoluteScale().x;
setTextareaWidth(textNode.width() * scale);
textarea.style.height = 'auto';
textarea.style.height =
textarea.scrollHeight + textNode.fontSize() + 'px';
});
function handleOutsideClick(e) {
if (e.target !== textarea) {
textNode.text(textarea.value);
removeTextarea();
}
}
setTimeout(() => {
window.addEventListener('click', handleOutsideClick);
});
});
</script>
</body>
</html>