Files
leaudit-platform-backend/docs/Collabora/参考资料/Collabora canvas 绘制.md
T

245 lines
9.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
## canvas 绘制(dist\bundle.js
```plain
┌─────────────────────────────────────────────────────────────┐
│ 1. 用户打开 cool.html │
│ 参数: WOPISrc=..., access_token=... │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 2. global.js 初始化 │
│ - 检测浏览器类型 (BrowserProperties) │
│ - 创建 WebSocket 连接 │
│ - 发送 "load url=..." 命令 │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 3. bundle.js 加载并处理 "status:" 消息 │
│ status: { type: "text", parts: 5, width: 21000, ... } │
│ → 创建 L.WriterTileLayer │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 4. TileManager 请求瓦片 │
│ → "tilecombine part=0 tileposx=0,256,512 ..." │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 5. 服务器返回瓦片数据 │
│ textMsg: "tile: nviewid=0 part=0 width=256 height=256" │
│ img: ImageBitmap (二进制图像数据) │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 6. TileManager.onTileMsg() 处理 │
│ - 解析 tileMsgObj │
│ - 存储到 _tiles Map │
│ - 标记需要重绘 │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 7. CanvasSectionContainer 绘制 │
│ ctx.drawImage(tile.image, sx, sy, sw, sh, dx, dy, dw, dh)│
│ → 用户看到完整的文档页面 │
└─────────────────────────────────────────────────────────────┘
```
+ **文档类型检测**: `_docType` 属性标识文档类型 ("text", "spreadsheet", "presentation", "drawing")
+ **消息处理**: `_onMessage(textMsg, img)` 是核心消息处理函数
+ **WriterTileLayer**: 专门用于处理 Word 文档 (docx) 的图层类
+ **瓦片消息**: 通过 WebSocket 接收 "tile:" 和 "tilecombine:" 消息
+ **Canvas 渲染**: 使用 `canvas.drawImage()` 将瓦片图像绘制到 Canvas 上
### 核心架构
### 1. 文档类型层次结构
```javascript
L.Layer (基类)
L.CanvasTileLayer (瓦片图层基类)
L.WriterTileLayer (Word 文档 - docx)
L.CalcTileLayer (电子表格 - xlsx)
L.ImpressTileLayer (演示文稿 - pptx/odp)
```
### 2. DOCX 文档数据对象结构
#### **TileMsgObj** (瓦片消息对象)
```javascript
{
id: number, // 瓦片唯一标识
width: number, // 瓦片宽度 (像素)
height: number, // 瓦片高度 (像素)
part: number, // 文档部分 (页码)
mode: number, // 渲染模式 (默认 0)
nviewid: number, // 视图ID
wireId: number, // 传输ID (用于增量更新)
zoom: number // 缩放级别
}
```
#### **文档层对象** (L.WriterTileLayer)
```javascript
{
_docType: "text", // 文档类型标识
_selectedPart: number, // 当前选中的页码
_selectedMode: number, // 当前模式
_viewId: number, // 视图ID
_debug: { // 调试信息
tileInvalidationsOn: boolean,
tileOverlaysOn: boolean
},
_onMessage: Function // 核心消息处理函数
}
```
### 渲染流程
### 第一阶段: 初始化与连接
```javascript
1. cool.html 加载
2. global.js 初始化浏览器属性和配置
3. 创建 WebSocket 连接
websocketURI = "ws://collabora-server/cool/{docURL}"
4. 发送 load 命令
"load url={encodedDocURL} lang=zh-CN accessibilityState=..."
```
### 第二阶段: 文档加载
```javascript
1. 服务器返回 "status:" 消息
包含文档类型尺寸页数等元数据
2. 根据文档类型创建对应的 TileLayer
if (command.type === "text")
docLayer = new L.WriterTileLayer(options)
3. 设置文档属性
docLayer._docType = "text"
docLayer.options = {
tileWidthTwips: 3840,
tileHeightTwips: 3840,
tileSize: 256 // 默认瓦片大小 256x256 像素
}
```
### 第三阶段: 瓦片渲染
#### **瓦片请求**
```javascript
// 客户端发送瓦片组合请求
"tilecombine nviewid=0 part=0 width=256 height=256
tileposx=0,256,512 tileposy=0,0,0 tilewidth=3840 tileheight=3840"
```
#### **瓦片接收与处理**
```javascript
TileManager.onTileMsg(textMsg, img) {
// 1. 解析瓦片消息
var tileMsgObj = parseServerCmd(textMsg);
// textMsg 格式: "tile: nviewid=0 part=0 width=256 height=256 ..."
// 2. 转换为坐标
var coords = tileMsgToCoords(tileMsgObj);
// coords = { x, y, z (zoom), part, mode }
// 3. 获取或创建瓦片对象
var tile = this.get(coords);
tile.viewId = tileMsgObj.nviewid;
tile.wireId = tileMsgObj.wireId;
tile.image = img; // ImageBitmap 或 Image 对象
// 4. 标记需要重绘
this.rehydrateTile(tile, true);
}
```
### 第四阶段: Canvas 绘制
#### **CanvasSectionContainer** (Canvas 容器管理)
```javascript
{
canvas: HTMLCanvasElement, // 主 Canvas 元素
context: CanvasRenderingContext2D, // 2D 渲染上下文
width: number, // Canvas 宽度
height: number, // Canvas 高度
clearColor: string // 背景色
}
```
#### **瓦片绘制到 Canvas**
```javascript
// 核心绘制逻辑
function paintTile(tile, canvas) {
// 1. 获取 2D 渲染上下文
var ctx = canvas.getContext("2d");
// 2. 计算源矩形 (瓦片图像中的区域)
var sx = 0, sy = 0;
var sWidth = tile.image.width;
var sHeight = tile.image.height;
// 3. 计算目标矩形 (Canvas 中的位置)
var dx = tile.coords.x * 256; // 目标 X 坐标
var dy = tile.coords.y * 256; // 目标 Y 坐标
var dWidth = 256; // 目标宽度
var dHeight = 256; // 目标高度
// 4. 绘制瓦片
if (tile.image) {
canvas.drawImage(
tile.image, // 源图像
sx, sy, // 源起点
sWidth, sHeight, // 源尺寸
dx, dy, // 目标起点
dWidth, dHeight // 目标尺寸
);
}
}
```
### 第五阶段: 增量更新 (Delta)
```javascript
// 服务器发送增量更新而非完整瓦片
if (textMsg.startsWith("delta:")) {
var deltaObj = parseServerCmd(textMsg);
// 应用增量到现有瓦片
applyDelta(tile, deltaObj);
}
```
### 关键特性
### 1. **瓦片缓存机制**
```javascript
TileManager._tiles = new Map(); // 缓存所有已加载的瓦片
// Key: "z:x:y:part:mode"
// Value: { image, wireId, viewId, coords }
```
### 2. **视口可见性优化**
```javascript
// 仅请求当前视口内的瓦片
clientvisiblearea = "0;0;{width};{height}"
// 优先级: 视口中心 > 视口边缘 > 视口外
```
### 3. **消息队列**
```javascript
global.queueMsg = []; // 在文档层准备好之前缓存消息
socket.onmessage = function(event) {
if (typeof socket._onMessage === "function") {
socket._emptyQueue();
socket._onMessage(event);
} else {
queueMsg.push(event.data); // 延迟处理
}
}
```
### 4. **多用户协作**
```javascript
// 每个用户视图有独立的 viewId
tileMsgObj.nviewid = 0; // 当前用户
// 其他用户的光标、选择通过单独的消息同步
```