docs(collabora): organize deployment guides and fix proxy chain
This commit is contained in:
@@ -0,0 +1,244 @@
|
||||
## 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; // 当前用户
|
||||
// 其他用户的光标、选择通过单独的消息同步
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user