Fabric.js 只能横向绘制文字。fabric 竖排文字从 2013 年起就是一个公开的需求(fabric.js #511),canvas 竖向文字也没有内置的书写模式。VerticalText 为中文、日文和韩文带来可编辑的 canvas文字竖排。
试一试
正在加载在线示例,代码在下方。
"use client";
import {
type CombineUpright,
type TextOrientation,
VerticalText,
} from "fabricjs-document-engine/text";
import {
DownloadIcon,
PencilIcon,
RotateCwIcon,
SigmaIcon,
} from "lucide-react";
import { useTranslations } from "next-intl";
import { useEffect, useRef, useState } from "react";
import { DemoCanvas, DemoFrame, Status, Toolbar, ToolButton } from "./demo-ui";
import { PAGE, useFabricCanvas } from "./use-fabric-canvas";
const TEXT = "縦書きの文章、ABCも読める。\n令和8年10月2日\n한국어 세로쓰기";
export default function VerticalTextDemo() {
const t = useTranslations("demos");
const { elementRef, canvas } = useFabricCanvas();
const textRef = useRef<VerticalText | null>(null);
const [orientation, setOrientation] = useState<TextOrientation>("mixed");
const [combine, setCombine] = useState<CombineUpright>("digits2");
const [message, setMessage] = useState("");
useEffect(() => {
if (!canvas) return;
const text = new VerticalText(TEXT, {
left: PAGE.width - 40,
top: 20,
originX: "right",
originY: "top",
fontSize: 24,
lineHeight: 1.3,
fill: "#0d0d0d",
combineUpright: "digits2",
styles: {
0: {
0: { fill: "#6f00ff" },
1: { fill: "#6f00ff" },
2: { fill: "#6f00ff" },
},
},
});
textRef.current = text;
canvas.add(text);
canvas.setActiveObject(text);
canvas.requestRenderAll();
return () => {
canvas.remove(text);
};
}, [canvas]);
useEffect(() => {
const text = textRef.current;
if (!canvas || !text) return;
text.set({ textOrientation: orientation, combineUpright: combine });
text.setCoords();
canvas.requestRenderAll();
}, [canvas, orientation, combine]);
function edit() {
const text = textRef.current;
if (!canvas || !text) return;
canvas.setActiveObject(text);
text.enterEditing();
text.selectAll();
setMessage(t("vertical.editing"));
}
function downloadSvg() {
if (!canvas) return;
const url = URL.createObjectURL(
new Blob([canvas.toSVG()], { type: "image/svg+xml" })
);
const link = document.createElement("a");
link.href = url;
link.download = "vertical-text.svg";
link.click();
setTimeout(() => URL.revokeObjectURL(url), 1000);
setMessage(t("vertical.downloaded"));
}
return (
<DemoFrame>
<DemoCanvas elementRef={elementRef} label={t("canvas")} />
<Toolbar>
<ToolButton
icon={PencilIcon}
label={t("vertical.edit")}
primary
disabled={!canvas}
onClick={edit}
/>
<ToolButton
icon={RotateCwIcon}
label={
orientation === "mixed"
? t("vertical.mixed")
: t("vertical.upright")
}
onClick={() =>
setOrientation((value) => (value === "mixed" ? "upright" : "mixed"))
}
/>
<ToolButton
icon={SigmaIcon}
label={
combine === "digits2"
? t("vertical.combined")
: t("vertical.separate")
}
onClick={() =>
setCombine((value) => (value === "digits2" ? "none" : "digits2"))
}
/>
<ToolButton
icon={DownloadIcon}
label={t("vertical.svg")}
disabled={!canvas}
onClick={downloadSvg}
/>
</Toolbar>
<Status tone={message ? "ok" : "idle"}>
{message || t("vertical.hint")}
</Status>
</DemoFrame>
);
}点击 编辑 后输入文字,或者使用输入法。方向键可以沿着一列移动,也可以移到下一列。
添加竖排文字
import { VerticalText, textObjects } from "fabricjs-document-engine/text";
const engine = createDocumentEngine({ canvas, customObjects: textObjects });
canvas.add(
new VerticalText("縦書きの文章、ABCも読める。\n令和8年10月2日", {
left: 400,
top: 20,
fontSize: 28,
combineUpright: "digits2",
})
);- 每一行变成一列。列内从上到下阅读,各列从右到左排列。
- 中文、日文和韩文字符直立,每个字占一个方格。
- 拉丁单词、数字和括号旋转 90°,并保留自己的字距,和日文印刷书籍一样。
- 句号和顿号位于方格的右上角。
combineUpright: "digits2"会把一位或两位数字并排放进一个方格,也就是日期常用的纵中横。textOrientation: "upright"会让所有字符直立,包括拉丁字母。
哪些字符直立、哪些字符旋转,遵循 Unicode 的竖排表(UAX #50)。样式、颜色、下划线和文字背景和普通 IText 一样可以使用。canvas文字竖排中的下划线画在列的右侧。
编辑竖排文字
| 按键 | 光标移动到 |
|---|---|
| ↓ 和 ↑ | 本列的下一个和上一个字符 |
| ← 和 → | 下一列和上一列 |
光标是一条横线,选区覆盖整段列,点击会找到指针下方的字符。中文和日文输入法和普通文本框一样可以使用。
导出
const { blob } = await engine.export({ format: "svg" });SVG 导出会给每个直立字符和每段旋转文字单独定位,而不是使用 writing-mode,所以 canvas文字竖排在浏览器、Illustrator 和 PDF 中看起来都一样。PDF 导出使用同样的绘制方式;要在 PDF 中输出中文或日文,请在 fonts 中传入字体文件。
配置项列在文字 API里。