查看: 100|回复: 0

鸿蒙 ASCF Canvas 绘制导出图片与 draw 回调避坑

[复制链接]
发表于 1 小时前 | 显示全部楼层 |阅读模式
在鸿蒙元服务里,Canvas 常被用于海报生成、签名板、数据可视化和图片编辑。ASCF 提供的画布 API 通过 CanvasContext 使用,方法基本覆盖 Canvas 2D 常用能力。下面按创建、绘制、样式、变换、像素、导出和排障几个环节,把实际开发中会用到的调用方式串起来。

创建画布与上下文

HXML 中先声明 canvas,canvas-id 要和 createCanvasContext 的参数一致:
  1. <canvas canvas-id='myCanvas' style='width: 300px; height: 200px;' />
复制代码

在页面里创建上下文:
  1. const ctx = has.createCanvasContext('myCanvas');
复制代码

如果在自定义组件中使用,需要把组件实例作为第二个参数传入:
  1. const ctx = has.createCanvasContext('myCanvas', this);
复制代码

基础绘制

矩形使用 fillRect 和 strokeRect,绘制完成后调用 draw:
  1. ctx.setFillStyle('#0A59F7');
  2. ctx.fillRect(30, 30, 200, 100);
  3. ctx.setStrokeStyle('#ff0000');
  4. ctx.setLineWidth(2);
  5. ctx.strokeRect(30, 30, 200, 100);
  6. ctx.draw();
复制代码

线段通过 moveTo、lineTo、stroke 组合:
  1. ctx.beginPath();
  2. ctx.moveTo(10, 10);
  3. ctx.lineTo(200, 100);
  4. ctx.setStrokeStyle('#333333');
  5. ctx.setLineWidth(3);
  6. ctx.stroke();
  7. ctx.draw();
复制代码

圆弧用 arc(x, y, r, sAngle, eAngle, counterclockwise)。sAngle 为起始弧度,0 是 3 点钟方向,2 * Math.PI 是一整圈,counterclockwise 默认 false 表示顺时针。arcTo 则根据控制点和半径画弧线。

文字用 fillText 或 strokeText,setTextAlign 支持 left/center/right;图片用 drawImage。drawImage 只接收本地路径或下载后的临时路径,网络图片要先通过 has.downloadFile 下载,再把 res.tempFilePath 传给 drawImage。

路径、曲线与样式

beginPath 开始新路径,closePath 连接当前点和起点形成封闭图形。多段路径可以分别 beginPath、设置样式、再 stroke,互不影响。曲线常用 quadraticCurveTo 和 bezierCurveTo,前者一个控制点,后者两个控制点。

样式包括 setFillStyle、setStrokeStyle、setLineWidth、setLineCap、setLineJoin、setLineDash、setMiterLimit、setShadow、setGlobalAlpha。填充和描边也支持渐变对象,线性渐变用 createLinearGradient,径向渐变用 createCircularGradient,再通过 addColorStop 添加色标。

坐标变换与剪切

translate、rotate、scale 都是坐标变换。rotate 参数是弧度,不是角度。变换会累积,推荐用 save/restore 成对包住,避免影响后续绘制。剪切用 clip,剪切后所有绘制都限制在剪切区域内,圆形头像裁剪就是典型用法:save、beginPath、arc、clip、drawImage、restore。

像素操作

像素读写分别用 has.canvasGetImageData 和 has.canvasPutImageData。读取结果里的 data 是 Uint8ClampedArray,每 4 个值表示一个像素的 RGBA。写入时需要按 RGBA 填充一维数组,再传给 canvasPutImageData。

导出图片

导出用 has.canvasToTempFilePath,但要特别注意:它必须放在 draw 的回调里执行,否则可能导出空白或旧内容。draw 本身是异步的,绘制 API 是同步的,所以在 draw 完成后再执行导出才可靠。
  1. ctx.draw(false, () => {
  2.   has.canvasToTempFilePath({
  3.     canvasId: 'myCanvas',
  4.     x: 0,
  5.     y: 0,
  6.     width: 300,
  7.     height: 200,
  8.     destWidth: 600,
  9.     destHeight: 400,
  10.     fileType: 'png',
  11.     quality: 1,
  12.     success: (res) => {
  13.       console.info('图片路径:', res.tempFilePath);
  14.       has.saveImageToPhotosAlbum({
  15.         filePath: res.tempFilePath
  16.       });
  17.     }
  18.   });
  19. });
复制代码

canvasToTempFilePath 支持 x、y、width、height,以及输出尺寸 destWidth、destHeight,fileType 可选 png 或 jpg,quality 用于 jpg 质量。导出成功后可以继续保存到相册。

三个常见场景

海报生成:创建上下文后画背景、标题、价格、分割线,再下载商品图,drawImage 后通过 draw 回调导出并保存。

签名板:onTouchStart 记录起点,onTouchMove 里不断 beginPath、moveTo、lineTo、stroke,并且必须用 draw(true) 保留之前笔迹,否则每次移动都会清空画布。清空画布时调用 draw() 或 draw(false)。

圆形头像:用 arc 建立圆形路径并 clip,再 drawImage,最后 restore,可再描一圈白色边框。

常见坑与排查

第一,canvasToTempFilePath 不在 draw 回调里,导出可能空白。正确做法是 ctx.draw(false, () => { ...导出... })。

第二,draw(true) 和 draw(false) 行为不同。draw() 或 draw(false) 会清空画布再绘制,draw(true) 会保留之前内容并叠加。签名板、连续轨迹类场景要用 true。

第三,drawImage 不支持网络 URL。网络图片先 has.downloadFile,成功后再用 res.tempFilePath。

第四,translate、rotate、scale 会累积。没有 save/restore 时,后续绘制可能被意外变换。建议成对使用。

第五,canvas 组件的 CSS 尺寸和绘图坐标是独立体系。如果 HXML 尺寸和绘制坐标不一致,内容可能被拉伸或压缩。最好设置固定尺寸,绘图时使用相同尺寸。

第六,渐变坐标是相对 canvas 左上角,不是相对当前绘制区域。createLinearGradient 和 createCircularGradient 的坐标要按画布整体来算。

最后

Canvas API 方法很多,常用组合其实集中在几组:fillRect/strokeRect 画矩形,beginPath + lineTo + stroke 画线,arc 画圆,drawImage 画图片,draw + canvasToTempFilePath 导出图片。记住导出必须放在 draw 回调、签名板用 draw(true)、网络图片先下载、变换用 save/restore,基本能避开主要问题。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

指导单位

江苏省公安厅

江苏省通信管理局

浙江省台州刑侦支队

DEFCON GROUP 86025

Hacking Group 021A

旗下站点

态势感知中心

应急响应中心

红盟安全

联系我们

官方QQ群:112851260

官方邮箱:security#ihonker.org(#改成@)

官方核心成员

关注微信公众号

Archiver|手机版|小黑屋| ( 沪ICP备2021026908号 )

GMT+8, 2026-9-17 13:16 , Processed in 0.021355 second(s), 18 queries , Gzip On, Redis On.

Powered by ihonker.com

Copyright © 2015-现在.

  • 返回顶部