FrameBuffer
用于自定义图形和复杂视觉效果的底层渲染表面。FrameBuffer 提供了一个二维单元格数组,其方法针对性能和内存进行了优化。
基本用法
Renderable API
import { FrameBufferRenderable, RGBA, createCliRenderer } from "@opentui/core"
const renderer = await createCliRenderer()
const canvas = new FrameBufferRenderable(renderer, {
id: "canvas",
width: 50,
height: 20,
})
// Draw on the frame buffer
canvas.frameBuffer.fillRect(5, 2, 20, 10, RGBA.fromHex("#FF0000"))
canvas.frameBuffer.drawText("Hello!", 8, 6, RGBA.fromHex("#FFFFFF"))
renderer.root.add(canvas)
Construct API
import { FrameBuffer, createCliRenderer } from "@opentui/core"
const renderer = await createCliRenderer()
renderer.root.add(
FrameBuffer({
width: 50,
height: 20,
}),
)
绘图方法
setCell
设置单个单元格的内容和颜色:
canvas.frameBuffer.setCell(
x, // X position
y, // Y position
char, // Character to display
fg, // Foreground color (RGBA)
bg, // Background color (RGBA)
attributes, // Text attributes (optional, default: 0)
)
// Example
canvas.frameBuffer.setCell(10, 5, "@", RGBA.fromHex("#FFFF00"), RGBA.fromHex("#000000"))
setCellWithAlphaBlending
使用 alpha 混合设置单元格以实现透明效果:
const semiTransparent = RGBA.fromValues(1.0, 0.0, 0.0, 0.5)
const transparent = RGBA.fromValues(0, 0, 0, 0)
canvas.frameBuffer.setCellWithAlphaBlending(10, 5, " ", transparent, semiTransparent)
drawText
在指定位置绘制文本字符串:
canvas.frameBuffer.drawText(
text, // String to draw
x, // Starting X position
y, // Y position
fg, // Text color (RGBA)
bg, // Background color (RGBA, optional)
attributes, // Text attributes (optional, default: 0)
)
// Example
canvas.frameBuffer.drawText("Score: 100", 2, 1, RGBA.fromHex("#00FF00"))
fillRect
用颜色填充矩形区域:
canvas.frameBuffer.fillRect(
x, // X position
y, // Y position
width, // Rectangle width
height, // Rectangle height
color, // Fill color (RGBA)
)
// Example: Draw a red rectangle
canvas.frameBuffer.fillRect(10, 5, 20, 8, RGBA.fromHex("#FF0000"))
drawFrameBuffer
将另一个帧缓冲区复制到当前缓冲区上:
canvas.frameBuffer.drawFrameBuffer(
destX, // Destination X
destY, // Destination Y
sourceBuffer, // Source FrameBuffer (OptimizedBuffer)
sourceX, // Source X offset (optional)
sourceY, // Source Y offset (optional)
sourceWidth, // Width to copy (optional)
sourceHeight, // Height to copy (optional)
)
colorMatrix / colorMatrixUniform
应用原生 4x4 RGBA 矩阵变换以实现后处理效果。使用 colorMatrixUniform 进行整个缓冲区的变换,使用 colorMatrix 来针对特定单元格进行变换。
import { INVERT_MATRIX, TargetChannel } from "@opentui/core"
// Full-buffer transform
canvas.frameBuffer.colorMatrixUniform(INVERT_MATRIX, 1.0, TargetChannel.Both)
// Per-cell transform with explicit mask
const cellMask = new Float32Array([10, 5, 1.0, 11, 5, 0.5])
canvas.frameBuffer.colorMatrix(INVERT_MATRIX, cellMask, 1.0, TargetChannel.FG)
详见颜色矩阵参考了解矩阵布局、掩码格式和行为细节。
属性
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
width | number | - | 缓冲区宽度,以字符为单位(必填) |
height | number | - | 缓冲区高度,以行为单位(必填) |
respectAlpha | boolean | false | 绘制时启用 alpha 混合 |
position | string | "relative" | 定位模式 |
left、top、right、bottom | number | - | 位置偏移量 |
示例:简单游戏画布
import { FrameBufferRenderable, RGBA, createCliRenderer } from "@opentui/core"
const renderer = await createCliRenderer()
const gameCanvas = new FrameBufferRenderable(renderer, {
id: "game",
width: 40,
height: 20,
position: "absolute",
left: 5,
top: 2,
})
// Game state
let playerX = 20
let playerY = 10
function render() {
const fb = gameCanvas.frameBuffer
const BG = RGBA.fromHex("#111111")
// Clear the canvas
fb.fillRect(0, 0, 40, 20, BG)
// Draw border
for (let x = 0; x < 40; x++) {
fb.setCell(x, 0, "-", RGBA.fromHex("#444444"), BG)
fb.setCell(x, 19, "-", RGBA.fromHex("#444444"), BG)
}
for (let y = 0; y < 20; y++) {
fb.setCell(0, y, "|", RGBA.fromHex("#444444"), BG)
fb.setCell(39, y, "|", RGBA.fromHex("#444444"), BG)
}
// Draw player
fb.setCell(playerX, playerY, "@", RGBA.fromHex("#00FF00"), BG)
// Draw score
fb.drawText("Score: 0", 2, 0, RGBA.fromHex("#FFFF00"))
}
// Handle input
renderer.keyInput.on("keypress", (key) => {
switch (key.name) {
case "up":
playerY = Math.max(1, playerY - 1)
break
case "down":
playerY = Math.min(18, playerY + 1)
break
case "left":
playerX = Math.max(1, playerX - 1)
break
case "right":
playerX = Math.min(38, playerX + 1)
break
}
render()
})
render()
renderer.root.add(gameCanvas)
示例:进度条
const EMPTY_BG = RGBA.fromHex("#222222")
function drawProgressBar(fb, x, y, width, progress, color) {
const filled = Math.floor(width * progress)
// Draw filled portion
for (let i = 0; i < filled; i++) {
fb.setCell(x + i, y, "█", color, EMPTY_BG)
}
// Draw empty portion
for (let i = filled; i < width; i++) {
fb.setCell(x + i, y, "░", RGBA.fromHex("#333333"), EMPTY_BG)
}
}
// Usage
drawProgressBar(canvas.frameBuffer, 5, 10, 30, 0.75, RGBA.fromHex("#00FF00"))
性能优化建议
- 批量更新:在下一次渲染周期之前对帧缓冲区进行多次修改
- 减少 fillRect 调用:对复杂形状使用单独的 setCell 调用
- 复用 RGBA 对象:创建颜色常量,而不是重复调用
fromHex
// Good: Create once, reuse
const RED = RGBA.fromHex("#FF0000")
const GREEN = RGBA.fromHex("#00FF00")
const BG = RGBA.fromHex("#000000")
for (let i = 0; i < 100; i++) {
fb.setCell(i, 5, "*", RED, BG)
}
// Avoid: Creating new RGBA objects in loops
for (let i = 0; i < 100; i++) {
fb.setCell(i, 5, "*", RGBA.fromHex("#FF0000"), RGBA.fromHex("#000000")) // Creates 200 objects
}