HTML5 Canvas 文本教程
Canvas 没有文本元素。浏览器提供的 fillText() 在某个点绘制字符串后就不再管它——没有换行,
没有对齐,也无法询问它有多宽。Konva.Text 是一个图形,因此文本会保留为一个节点,你可以
移动、设置样式、测量它,并对它做命中检测。
const text = new Konva.Text({
x: 20,
y: 20,
text: 'Hello Konva',
fontSize: 24,
fontFamily: 'Arial',
fill: 'black',
});
设置 width 后文本会按该宽度换行。不设置 width 时,图形会根据内容自动调整尺寸。
有关完整的属性和方法列表,请参阅 Text API 参考。
- Vanilla
- React
- Vue
import Konva from 'konva';
const stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight
});
const layer = new Konva.Layer();
stage.add(layer);
// Simple text
const simpleText = new Konva.Text({
x: stage.width() / 2,
y: 15,
text: 'Simple Text',
fontSize: 30,
fontFamily: 'Calibri',
fill: 'green'
});
simpleText.offsetX(simpleText.width() / 2);
// Complex text with background
const complexText = new Konva.Text({
x: 20,
y: 60,
text: "COMPLEX TEXT\n\nAll the world's a stage, and all the men and women merely players. They have their exits and their entrances.",
fontSize: 18,
fontFamily: 'Calibri',
fill: '#555',
width: 300,
padding: 20,
align: 'center'
});
const rect = new Konva.Rect({
x: 20,
y: 60,
stroke: '#555',
strokeWidth: 5,
fill: '#ddd',
width: 300,
height: complexText.height(),
shadowColor: 'black',
shadowBlur: 10,
shadowOffsetX: 10,
shadowOffsetY: 10,
shadowOpacity: 0.2,
cornerRadius: 10
});
layer.add(rect);
layer.add(simpleText);
layer.add(complexText);
import { Stage, Layer, Text, Rect } from 'react-konva';
const text = `COMPLEX TEXT
All the world's a stage, and all the men and women merely players. They have their exits and their entrances.`;
const App = () => {
return (
<Stage width={window.innerWidth} height={window.innerHeight}>
<Layer>
<Text
x={0}
y={15}
width={window.innerWidth}
align="center"
text="Simple Text"
fontSize={30}
fontFamily="Calibri"
fill="green"
/>
<Rect
x={20}
y={60}
stroke="#555"
strokeWidth={5}
fill="#ddd"
width={300}
height={200} // Approximate height
shadowColor="black"
shadowBlur={10}
shadowOffsetX={10}
shadowOffsetY={10}
shadowOpacity={0.2}
cornerRadius={10}
/>
<Text
x={20}
y={60}
text={text}
fontSize={18}
fontFamily="Calibri"
fill="#555"
width={300}
padding={20}
align="center"
/>
</Layer>
</Stage>
);
};
export default App;
<template>
<v-stage :config="stageSize">
<v-layer>
<v-rect :config="rectConfig" />
<v-text :config="simpleTextConfig" />
<v-text :config="complexTextConfig" />
</v-layer>
</v-stage>
</template>
<script setup>
const stageSize = {
width: window.innerWidth,
height: window.innerHeight
};
const simpleTextConfig = {
x: 0,
y: 15,
width: window.innerWidth,
align: 'center',
text: 'Simple Text',
fontSize: 30,
fontFamily: 'Calibri',
fill: 'green'
};
const complexTextConfig = {
x: 20,
y: 60,
text: "COMPLEX TEXT\n\nAll the world's a stage, and all the men and women merely players. They have their exits and their entrances.",
fontSize: 18,
fontFamily: 'Calibri',
fill: '#555',
width: 300,
padding: 20,
align: 'center'
};
const rectConfig = {
x: 20,
y: 60,
stroke: '#555',
strokeWidth: 5,
fill: '#ddd',
width: 300,
height: 200, // Approximate height
shadowColor: 'black',
shadowBlur: 10,
shadowOffsetX: 10,
shadowOffsetY: 10,
shadowOpacity: 0.2,
cornerRadius: 10
};
</script>
测量文本
Konva.Text 在创建时就完成了自我测量,因此在它被加入图层之前,你就可以读取它的尺寸。
const text = new Konva.Text({ text: 'Hello Konva', fontSize: 24 });
text.width(); // full shape width, including padding
text.height(); // full shape height, all lines, including padding
text.getTextWidth(); // width of the widest line, excluding padding
text.fontSize(); // height of a single line
请使用这些值,而不要去估算。手动居中是人们估算的常见原因:
text.offsetX(text.width() / 2); // exact
有两点需要注意:
getTextHeight()已废弃。 它会在控制台发出警告。请使用height()获取整个图形的 高度,使用fontSize()获取单行高度。measureSize(string)用于测量尚未绘制的字符串,使用当前图形的字体。它适合在确定 内容之前预估尺寸,并且无法处理多行文本。
const { width } = text.measureSize('Some other string');
在 React 和 Vue 中,请通过节点的 ref 读取同样的数值,而不要去估算;或者像上面的示例那样,
给文本设置 width 和 align,从而完全避免测量。
换行与省略号
只有在设置了 width 时,文本才会换行。
new Konva.Text({
text: 'A long line that will not fit on one row',
width: 200, // wrapping requires this
wrap: 'word', // 'word' (default), 'char', or 'none'
ellipsis: true, // needs a height too, or there is nothing to overflow
});
当文本放不下时,ellipsis 会用 … 截断它。只有同时设置了 width 和 height 才会生效,
因为没有 height 时图形会自动增高以容纳内容,永远不会溢出。
import Konva from 'konva';
const stage = new Konva.Stage({ container: 'container', width: 500, height: 260 });
const layer = new Konva.Layer();
stage.add(layer);
const sample = 'Konva.Text wraps to the width you give it, and can truncate when it runs out of room.';
[
{ y: 10, label: "wrap: 'word' (default)", config: { width: 220 } },
{ y: 95, label: "wrap: 'char'", config: { width: 220, wrap: 'char' } },
{ y: 180, label: 'ellipsis with a fixed height', config: { width: 220, height: 44, ellipsis: true } },
].forEach(({ y, label, config }) => {
layer.add(new Konva.Text({ x: 250, y, text: label, fontSize: 13, fill: '#666' }));
layer.add(
new Konva.Rect({ x: 20, y, width: 220, height: config.height || 70, stroke: '#ddd' })
);
layer.add(new Konva.Text({ x: 20, y, text: sample, fontSize: 14, padding: 4, ...config }));
});
文本与 Web 字体
这是最常见的文本问题,而且它并不是 Konva 的缺陷。
当 Web 字体加载完成时,DOM 元素会自动重新布局,Canvas 则不会。如果文本是在字体到达之前
创建的,它就是用回退字体测量的,而换行、居中和 width() 依然基于那份过时的测量结果。
文本通常看起来没问题,位置却是错的。
请等待字体加载完成,然后强制重新测量:
await document.fonts.load('16px "Roboto"');
// Re-setting any measured attribute re-runs the measurement.
text.fontFamily('Roboto');
只要下列影响文本的属性之一发生变化,Konva 就会重新测量:text、fontFamily、fontSize、
fontStyle、fontVariant、lineHeight、letterSpacing、align、verticalAlign、
padding、width、height、wrap、ellipsis 和 direction。即使把其中某个属性
设置为它原本的值,也足以触发。
完整示例(包括如何加载字体本身)参见自定义字体。