【HarmonyOS 5】鸿蒙实现手写板

【HarmonyOS 5】鸿蒙实现手写板

一、前言

实现一个手写板功能,基本思路如下:

创建一个可交互的组件,用户在屏幕上触摸并移动手指时,会根据触摸的位置动态生成路径,并使用黑色描边绘制在屏幕上。当用户按下屏幕时,记录按下点的坐标作为路径的起点。当用户移动手指时,不断记录移动点的坐标,通过线段连接起来形成路径。

功能效果图如下所示:

二、方案思路

系统提供了非常便捷的画线组件Path。该组件将画布和画线功能合二为一。提供了简洁的画线组件。

详情参见,官方组件API文档链接Path-图形绘制-ArkTS组件-ArkUI(方舟UI框架)-应用框架 - 华为HarmonyOS开发者developer.huawei.com/consumer/cn...

Path该组件的使用思路很简单,如下代码所示:

javascript 复制代码
      Path()
        .commands(this.pathCommands) // 设置SVG路径描述字符串
        .strokeWidth(5) // 设置路径的描边宽度为 5
        .fill("none") // 设置路径的填充颜色为无
        .stroke(Color.Black) // 设置路径的描边颜色为黑色
        .height('100%')
        .width('100%')

1. 将组件的宽高设置需要展示的画布区域大小。

height和width为百分之百。

2. 设置线的样式颜色和宽度。

stroke为Color.Black,strokeWidth为5

3. 设置绘制闭合的填充区域的颜色。

fill为none

若你修改了fill有填充色,就会得到如下的效果:

因为该效果并非手写板的需求,所以需要设置fill为none,不要填充色。

4. 设置SVG路径描述字符串。

该组件最核心的属性。它的值是字符串大概是这样的:

javascript 复制代码
M225.99999999999997 409.99999999999994 L225.99999999999997 409.99999999999994

首先我们先了解SVG 路径描述符是干嘛的?

SVG 路径描述符是用于定义矢量图形路径的一组命令,在使用Path组件绘制图形时发挥关键作用,可创建各种复杂的自定义形状。主要作用如下:

(1)构建基础图形:

通过M(moveto)命令确定起始点,配合L(lineto)、H(horizontal lineto)、V(vertical lineto)等命令绘制直线段,能构建出三角形、矩形等基础图形。例如commands('M0 20 L50 50 L50 100 Z')定义了一个起始于(0,20),通过绘制两条直线段,最后用Z(closepath)命令关闭路径形成的三角形 。

(2)绘制曲线图形:

借助C(curveto)、S(smooth curveto)、Q(quadratic Belzier curve)、T(smooth quadratic Belzier curveto)等命令,能绘制不同类型的贝塞尔曲线,实现复杂的曲线图形绘制。像C100 100 250 100 250 200可绘制一条从当前点到(250, 200)点的三次贝塞尔曲线,用于创建诸如花瓣、波浪线等曲线形状。

(3)绘制椭圆弧:

A(elliptical Arc)命令用于从当前点到指定点绘制椭圆弧,通过设置椭圆的半径、旋转角度以及其他标志位,能绘制各种椭圆形状的部分弧线,比如绘制饼图的扇区。

而在手写板场景中,SVG 路径描述符仅使用M(moveto)和L(lineto)命令就能实现主要功能。 原因在于手写板的核心需求是实时记录并呈现用户绘制的轨迹。M命令用于确定绘制轨迹的起始点,每次用户触摸屏幕开始书写时,就相当于启动一个新的路径,M命令记录下这个起始坐标,如同在纸上确定第一笔的落点。L命令从当前点到指定坐标画一条线,在手写板中,随着用户手指的移动,不断获取移动过程中的坐标点,通过L命令依次连接这些点,就能形成连续的线条,精准地呈现出手写的笔迹。

最终我们只需要在Path组件外添加onTouch事件 记录用户的手指触摸事件,得到其x,y坐标,即可实现手写板的svg路径描述符:

javascript 复制代码
 // 处理触摸事件的方法
    onTouchEvent(event: TouchEvent) {
        // 根据触摸事件的类型进行不同的处理
        switch (event.type) {
            // 当触摸类型为按下时
            case TouchType.Down:
                // 在 pathCommands 中添加一个移动命令(M),并记录按下点的坐标
                this.pathCommands += 'M' + event.touches[0].x + ' ' + event.touches[0].y;
                break;
            // 当触摸类型为移动时
            case TouchType.Move:
                // 在 pathCommands 中添加一个线段命令(L),并记录移动点的坐标
                this.pathCommands += 'L' + event.touches[0].x + ' ' + event.touches[0].y;
                break;
            // 其他触摸类型,不做处理
            default:
                break;
        }
    }

5. 当需要清空手写板。

只需要对Path组件的commands属性设置为空字符串即可。

三、源码示例:

javascript 复制代码
@Entry
@Component
struct PathTestPage {

  @State pathCommands: string = "";

  private setPathCommands(str: string, event: TouchEvent){
    let x = event.touches[0].x;
    let y = event.touches[0].y;
    this.pathCommands += str + vp2px(x) + ' ' + vp2px(y);
    console.log("georgeDebug", " this.pathCommands: " + this.pathCommands);
  }

  onTouchEvent(event: TouchEvent){
    // event xy 单位:vp
    switch (event.type){
      case TouchType.Down:
        this.setPathCommands('M', event);
        break;

      case TouchType.Move:
        this.setPathCommands('L', event);
        break;
        default:
          break;
    }
  }

  build() {
    Stack({
      alignContent: Alignment.TopStart
    }){
      Path()
        .commands(this.pathCommands) // 设置SVG路径描述字符串
        .strokeWidth(5) // 设置路径的描边宽度为 5
        .fill("none") // 设置路径的填充颜色为无
        .stroke(Color.Black) // 设置路径的描边颜色为黑色
        .height('100%')
        .width('100%')
        .onTouch((event: TouchEvent)=>{
          this.onTouchEvent(event);
        })

      Button("清空绘制")
        .onClick(()=>{
          this.pathCommands = "";
        })
    }
    .height('100%')
    .width('100%')

  }
}

注意:

  1. 因为path不能作为build的根节点,所以用容器组件row进行了包裹。

  2. onTouch得到的坐标xy单位为vp,而svg描述符的单位为vp,所以数值要做单位转化。否则会造成能画出东西, 但是坐标跟下笔的位置都缩小了。

  3. 需要给path组件设置宽高,否则会不显示。

相关推荐
一只栖枝1 小时前
华为 HCIE 大数据认证中 Linux 命令行的运用及价值
大数据·linux·运维·华为·华为认证·hcie·it
加班是不可能的,除非双倍日工资3 小时前
css预编译器实现星空背景图
前端·css·vue3
wyiyiyi4 小时前
【Web后端】Django、flask及其场景——以构建系统原型为例
前端·数据库·后端·python·django·flask
gnip4 小时前
vite和webpack打包结构控制
前端·javascript
excel4 小时前
在二维 Canvas 中模拟三角形绕 X、Y 轴旋转
前端
阿华的代码王国5 小时前
【Android】RecyclerView复用CheckBox的异常状态
android·xml·java·前端·后端
一条上岸小咸鱼5 小时前
Kotlin 基本数据类型(三):Booleans、Characters
android·前端·kotlin
Jimmy5 小时前
AI 代理是什么,其有助于我们实现更智能编程
前端·后端·ai编程
zhanshuo5 小时前
在鸿蒙里优雅地处理网络错误:从 Demo 到实战案例
harmonyos
zhanshuo5 小时前
在鸿蒙中实现深色/浅色模式切换:从原理到可运行 Demo
harmonyos