canvas — 实现与 components.md 对照

对照基准:

  • 规格:docs/refs/components.md#canvas
  • AI 限制:仅 2d;Context 经 this.createSelectorQuery 获取;type=webgl ⏭️;canvas-id 可选(同页唯一)
  • 实现:
    • packages/flexui-frontend/src/components/canvas/canvas.ts
    • flexui-engine/driver/js/packages/flexui-backend/src/adapters/canvas-adapter.ts
    • JS 2d 代理:.../native-backend/canvas-context.ts(createSelectorQuery → fields({ context }))
    • Native:framework/ohos/.../canvas/FRCanvasView.ets(drawCommands / getSize)
    • Demo:demos/ascf-ai-demo/.../canvas-demo/(属性 + JS 自绘图表)

模板使用 kebab-case(如 disable-scroll),组件 properties 为 camelCase(如 disableScroll);adapter / host attribute 仅 kebab-case,不双写 camelCase 别名。

包名说明:内置组件源码在 @openflexui/frontend(packages/flexui-frontend);原生 adapter 在 flexui-backend。


结论摘要

维度 结论
规格纳入(✅) type(仅 2d)、canvas-id(可选、同页唯一)、disable-scroll、bindtouch* / bindlongtap、binderror → 已声明并贯通
规格「⏭️」 type=webgl → 不作为合法值(传入则 error + 仍 2d)
绘制 JS Canvas2D:createSelectorQuery().fields({ context: true, size: true }) → ctx.fillRect / stroke … → callUIFunction(drawCommands)
默认值 type='2d'、disable-scroll=false;标签默认尺寸 300×150(微信 tip)
尺寸 tip 标签默认 300×150;绘制面硬上限 1365×1365(仅布局超限时 FRCanvasView clamp + scale + setTransform;未超限仍 width/height:100%,与无 clamp 路径一致)

属性对照

通用(可交付)

属性(模板) 类型 规格默认 components.md 当前实现 说明
type string — ✅ 仅 2d ✅ type → canvasType(传原值) 非法 / webgl → native binderror + 仍按 2d 绘制;不 setData 回写
canvas-id string — ✅ 纳入(可选) ✅ → canvasId 同页不可重复;重复 → 隐藏 + binderror + 拒绘 / 无 context
disable-scroll boolean false ✅ 纳入 ✅ → disableScroll true 时 native 空 Pan 抢占,阻止外层页滚
bindtouchstart 等 event — ✅ ✅ adapter 映射;FRCanvas overlay onTouch
bindlongtap event — ✅ ✅ longtap → longclick
binderror event — ✅ ✅ 等 listener 注册后再发(deferred);含 type / 重复 canvas-id

通用(AI 冲突 — 不声明)

属性 components.md 当前实现
type=webgl ⏭️ AI 冲突 ❌ 不作为合法值;传入则 error + 仍 2d 绘制

事件对照

事件 components.md 当前实现 detail / 备注
bindtouchstart / move / end / cancel ✅ ✅ adapter processEventData 填 touches
bindlongtap ✅ ✅ native longclick
binderror ✅ ✅ { errMsg };type 非法 / 重复 canvas-id
bindtap (基础) ✅ tap → click

实现要点

  1. TS 声明:交付 type、canvasId、disableScroll。
  2. type:host 写原值 → adapter 原样 → native canvasType;非 2d 时 deferred error;禁止 setData({ type: '2d' })。
  3. canvas-id:可选。NBE 按 rootViewId 注册;冲突置 canvas-id-conflict → visibility:hidden + FRCanvasView 拒 drawCommands + binderror。Domlike 组件层注册表同语义。冲突节点永久隐藏可接受,不因原 owner release 自动复活(微信 tip「不再正常工作」);级联 untrackDescendants release 后,新挂载同 id 可 claim。
  4. disable-scroll:kebab host attr;true 时 PanGesture 抢占。
  5. 触摸 / 长按:FRCanvas 对齐 View 的 overlay + LongPressGesture。
  6. JS 绘图:NativeBackendElement.getContext(cb) 返回 CanvasRenderingContext2DProxy;冲突时 cb(null)。命令经 drawCommands 落到 ArkUI。布局超过 1365 时才 clamp。flush(onDone?) 须等 native ack 后再标记已绘制;canvasReady=false 时 native 不假成功,onReady 超时(3s)binderror + rejected: canvasNotReady。
  7. Demo 图表:canvas-demo/charts.ts 用 JS API 画 line/area/column/pie/radar;禁止 native demo-chart 属性自绘。
  8. 验证:改 adapter / canvas-context / NBE → build-jsfwk;改 FRCanvasView.ets → HAP(含 native);改 demo → 重编 pack。

Demo 验收清单

项 预期
type=2d 画布可见;触摸可测
canvas-id 唯一 可见
重复 canvas-id 第二块隐藏 + binderror
disable-scroll true 时拖动画布不带动页滚
bindtouch* / longtap / error 状态文案更新
JS 五图 query 取 context 后绘制,非空图

关键文件

角色 路径
组件定义 packages/flexui-frontend/src/components/canvas/canvas.ts
Domlike 注册表 .../canvas/canvas-id-registry.ts
Adapter .../flexui-backend/src/adapters/canvas-adapter.ts
Native 注册表 .../native-backend/canvas-id-registry.ts
JS 2d 代理 .../native-backend/canvas-context.ts
Native View .../canvas/FRCanvasView.ets
Demo demos/.../canvas-demo/(含 charts.ts)