# 让正文显示为 Canvas/SVG

从 DOM 视图切换到 Canvas，理解滚动、缩放和分页由谁处理。

View 是文档的显示与输入层。第一章使用浏览器原生 DOM 视图；这一章换成 Canvas。Core 中的节点、命令和撤销历史仍采用同一套机制。

开始前，取得与 Core 同批次的 `@quirekit/view`。该包有独立的许可要求，见[支持与许可](/support)。

## 1. 安装对应视图

将原来的 `DomViewExtension` 替换为 Canvas 适配器，并把 `mode` 改为 `canvas`。下面是创建配置的关键部分，放在上一章组件的 `onMounted` 中：

```ts [创建 Canvas 视图]
import { canvasViewExtension } from '@quirekit/view'
import '@quirekit/view/styles.css'

editor = new Editor({
  element: element.value,
  mode: 'canvas',
  extensions: [
    StarterKit,
    canvasViewExtension({ runtime: { width: 720, height: 900 } }),
  ],
  content: '<p>由 Canvas 显示的正文。</p>',
})
```

`runtime` 是视图的运行配置，`width`、`height` 使用 CSS 像素。结构样式负责正文层、输入层和选区层的定位，因此需要和视图一起导入。

下面的单文件示例根据容器宽度设置显示尺寸，可以直接点击输入。源码包含完整导入和 Vue 生命周期。

### tutorial/canvas.vue

```vue
<script setup lang="ts">
import { onBeforeUnmount, onMounted, ref } from "vue";
import { Editor } from "@quirekit/core";
import { StarterKit } from "@quirekit/starter-kit";
import { canvasViewExtension, svgViewExtension } from "@quirekit/view";
import "@quirekit/view/styles.css";

const props = withDefaults(defineProps<{
  /** Initial text language, separate from the document after creation. */
  locale?: "zh-CN" | "en";
}>(), { locale: "zh-CN" });
const element = ref<HTMLElement>();
let editor: Editor | undefined;

onMounted(() => {
  if (!element.value) return;
  const runtime = { width: element.value.clientWidth, height: 240, pageStack: false };
  editor = new Editor({
    element: element.value,
    mode: "canvas",
    extensions: [StarterKit, canvasViewExtension({ runtime }), svgViewExtension({ runtime })],
    content: props.locale === "en" ? "<p>This text is displayed by Canvas. Click to edit.</p>" : "<p>这段正文由 Canvas 显示。点击这里开始编辑。</p>",
  });
});
onBeforeUnmount(() => editor?.destroy());
</script>

<template><div ref="element" style="min-height: 240px; position: relative" /></template>

```

## 2. 切换 SVG 而保留正文

如果产品允许用户切换后端，创建时同时安装 `svgViewExtension()`，再调用：

```ts [切换显示方式]
editor.changeMode('svg')
editor.changeMode('canvas')
```

`changeMode()` 更换主视图，不会重新创建正文。不要再次调用 `mount()` 代替切换：后者用于增加另一个视图。组件退出时仍然只需销毁它拥有的 Editor。

## 3. 让工作区负责滚动和缩放

完整产品通常在自己的容器里滚动。把该容器同时传给 `scrollViewport`，并设置 `scrollSource: 'viewport'`，View 就能使用正确的滚动位置处理输入和选区。

工作区可以根据可用宽度缩放显示。示例在 `workspace.vue` 中计算展示比例，在 `runtime.ts` 中把页数和内容尺寸交给界面。正文里的纸张尺寸保持原值，缩小显示不应改写文档数据。

## 4. 分页来自文档模型

Canvas 并不等于 Word 分页。普通文章可以连续显示；Word 模型通过自己的排版规则产生页面。`pageStack: true` 将这些页面显示为独立的页面表面，不负责创造分节或页眉页脚规则。

需要分页工作区时，参考 [Word 模型](/build/model)与[完整工作区](/build/reference)中的 `assembly/word.ts`、`runtime.ts`、`workspace.vue`。接下来[连接工具栏](/build/commands)，参数细节见 [View API](/api/view)。
