跳到主要内容

画布 Canvas

有时候,内置控件再丰富,也满足不了你的脑洞。也许你不想画按钮,而是想画一个折线图、粒子效果,或者一个自定义的游戏小地图。这时候就该 Canvas 登场了——它给你一个裸的 HXBufferPainter*,你可以在上面为所欲为。

一句话理解

Canvas 就是 HiEasyX 留给你的“逃生舱门”。你在 Window 里画控件,在 Canvas 里画自由图形。

函数原型

void Canvas(HXPoint Size, const std::function<void(HXBufferPainter*, HXPoint)> &Callback);

参数说明

参数类型默认值说明
SizeHXPoint画布的尺寸 {宽, 高}。和控件一样,-1 表示自动填充。
Callbackstd::function<void(HXBufferPainter*, HXPoint)>绘制回调函数。第一个参数是画布的 HXBufferPainter* 指针,第二个参数是画布在当前窗口中的实际位置偏移。

返回值

void

Canvas 与 Window 内控件的区别

特性Window 内控件Canvas
绘制方式由 HiEasyX 自动渲染你自己写绘制代码
输入处理自动(点击、悬停等)需手动判断鼠标/键盘位置
适用场景按钮、输入框、滑块等标准 UI图表、游戏画面、自定义可视化
坐标系相对窗口,自动布局相对 Canvas 左上角 (0, 0)
滚动条部分控件自带需自行实现(如有需要)
什么时候用 Canvas?
  • 你需要画折线图、饼图、柱状图
  • 你想做一个迷你地图
  • 你要实现粒子特效预览窗口
  • 你需要一个自定义渲染的游戏区域

如果你只是想让背景好看一点,优先考虑修改主题或贴图;Canvas 更适合“程序化绘制”。

回调函数详解

回调接收两个参数:

  1. HXBufferPainter* painter — 指向画布缓冲区的绘制器。你可以调用所有 painter->DrawXxx 系列函数,例如 DrawRectDrawLineDrawText 等。
  2. HXPoint where — 画布在当前窗口中的实际像素位置。如果你需要把鼠标坐标转换到画布本地坐标系,可以用:
int localX = Context.MouseX - where.x;
int localY = Context.MouseY - where.y;

完整示例

下面这个例子在画布上画了一个会跟随鼠标移动的小球,外加一个网格背景——这用标准控件是绝对做不出来的。

#include <include/hex.h>
#include <include/impl/EasyX/hex_impl_easyx.h>
#include <cmath>

int main() {
static HX::WindowProfile wp;
while (true) {
HX::HXBegin();
ExMessage msg;
while (peekmessage(&msg)) {
HX::PushMessage(HX::GetHXMessage(&msg));
}

HX::Window(HXStr("Canvas 示例"), wp);

HX::Text(HXStr("下面是一块自由画布:"));

HX::Canvas({400, 300}, [](HXBufferPainter* painter, HXPoint where) {
// 1. 画背景网格
painter->SetLineColor(HX::HXColor{60, 60, 60, 255});
for (int x = 0; x <= 400; x += 40) {
painter->DrawLine(x, 0, x, 300);
}
for (int y = 0; y <= 300; y += 40) {
painter->DrawLine(0, y, 400, y);
}

// 2. 获取鼠标在画布上的本地坐标
int mx = HX::Context.MouseX - where.x;
int my = HX::Context.MouseY - where.y;

// 3. 画一个跟随鼠标的高亮圆
int radius = 20;
painter->SetFillColor(HX::HXColor{0, 200, 255, 180});
painter->SetLineColor(HX::HXColor{0, 255, 255, 255});
painter->DrawCircle(mx, my, radius, true, true);

// 4. 在圆心写坐标
HXString coordText = std::to_wstring(mx) + HXStr(", ") + std::to_wstring(my);
painter->SetTextColor(HX::HXColor{255, 255, 255, 255});
painter->DrawText(coordText.c_str(), mx + radius + 5, my - 8);
});

HX::End();
HX::Render();
}

return 0;
}

进阶:在 Canvas 里做局部交互

Canvas 不帮你处理输入,但你可以用 HX::Context 里的鼠标状态自己做:

HX::Canvas({200, 200}, [](HXBufferPainter* p, HXPoint pos) {
int localX = HX::Context.MouseX - pos.x;
int localY = HX::Context.MouseY - pos.y;

bool inside = localX >= 0 && localX < 200 && localY >= 0 && localY < 200;
bool clicked = inside && HX::Context.MouseLeftPressed;

if (clicked) {
// 用户点击了画布内部!
}
});
不要持有 painter 指针

回调结束后,painter 指向的缓冲区可能会被回收或重用。不要在回调外面保存这个指针,否则下一帧它可能指向无效内存。

常见疑问

Q:Canvas 支持子控件吗? 不支持。Canvas 是一个纯绘制区域,里面不能放 ButtonTextInput。如果你需要可滚动的复杂区域,考虑组合 Scroller 和多个 Canvas

Q:Canvas 占用的内存大吗? HiEasyX 会为 Canvas 分配一块和 Size 匹配的离屏缓冲区。如果你创建了一个 1920x1080 的画布,那它就会占用对应的图像内存。建议按需分配,不用时及时结束所在窗口。

运行效果

截图占位符:请补充 $name 控件的运行效果截图。

截图占位符:请补充 Canvas 运行效果 的运行效果截图,保存为 ./assets/Canvas_view.png。`n