当前位置: 首页 > article >正文

HarmonyOS 5.0应用开发——Canvas制作个人签名

【高心星出品】

文章目录

      • Canvas制作个人签名
        • 案例介绍
          • 运行结果
        • 开发步骤
        • 完整代码

Canvas制作个人签名

Canvas提供画布组件,用于自定义绘制图形,开发者使用CanvasRenderingContext2D对象和OffscreenCanvasRenderingContext2D对象在Canvas组件上进行绘制,绘制对象可以是基础形状、文本、图片等。

案例介绍

使用鸿蒙Canvas制作个人签名是一种在HarmonyOS系统环境下,利用Canvas组件实现手写签名功能的技术。以下是对该技术的详细介绍:

  1. 基础准备
    • 环境搭建:确保开发环境已正确配置,包括安装HarmonyOS SDK及相关开发工具,以便进行应用程序的开发和调试。
    • 创建项目:在开发环境中创建一个新的鸿蒙应用项目,并选择合适的应用模板,如空应用模板等。
  2. 页面布局与Canvas初始化
    • 页面布局:在项目的页面文件中,使用ArkTS语言或其他相关语言设计页面布局,为Canvas签名区域预留合适的空间,并可添加其他相关的按钮或控件,如生成签名按钮、清除按钮等。
    • Canvas初始化:通过创建Canvas组件实例,并设置其宽度、高度、背景颜色等属性来初始化Canvas。可以使用ArkTS中的相关类和方法,如CanvasRenderingContext2D等,来获取Canvas的绘图上下文,以便后续进行绘图操作。
  3. 手势监听与绘制逻辑
    • 手势监听:利用鸿蒙系统的手势监听机制,为Canvas组件添加触摸事件监听器,如PanGesture等,以捕捉用户的手指触摸动作,包括按下、移动和抬起等操作。
    • 绘制逻辑:在触摸事件回调函数中,根据用户的操作实时更新画笔的位置和状态,并通过Canvas的绘图方法将用户的签名轨迹绘制到画布上。例如,在手指按下时记录起始位置,在手指移动时绘制线条,在手指抬起时结束绘制。
  4. 功能扩展与优化
    • 撤销与重做功能:为了提供更好的用户体验,可以实现撤销和重做功能。可以通过记录用户每次绘制的操作步骤,当用户点击撤销按钮时,回退到上一步的操作状态;点击重做按钮时,重新执行被撤销的操作。
    • 签名生成与保存:用户可以点击生成签名按钮,将Canvas上的签名内容转换为图片格式,如PNG或JPEG等,并保存到本地存储或上传到服务器,以便后续使用。
    • 界面美化与交互优化:对签名界面进行美化,如添加边框、阴影等效果,提升视觉效果。同时,优化交互细节,使签名过程更加流畅和自然。

综上所述,使用鸿蒙Canvas制作个人签名技术涉及多个方面。这种技术为用户在鸿蒙设备上提供了一种便捷、高效的手写签名方式,可应用于电子合同签署、表单填写等多种场景。

运行结果

在这里插入图片描述

开发步骤

整体布局

整体布局是两个层叠的Canvas画板,居于顶部绘制了绿色的十字网格,居于底部接受用户的手势动作,来绘制线段。

这里为了不被上层Canvas阻止事件传递,使用.hitTestBehavior(HitTestMode.Transparent)方法。

Stack() {
  // 负责绘制
  Canvas(this.canvascontext)
    .width(300)
    .height(300)
    .backgroundColor(Color.Grey)
    .onReady(() => {
    })
    .onTouch((event) => {
      let x1 = 0
      let y1 = 0
      if (event.type == TouchType.Down) {
        this.x = event.touches[0].x
        this.y = event.touches[0].y
      } else if (event.type == TouchType.Move) {
        x1 = event.touches[0].x
        y1 = event.touches[0].y
        // 手指滑动的时候 跟着手指绘制
        this.drawtings((width: number, height: number) => {
          this.drawline(x1, y1)
        })
        this.x = x1
        this.y = y1
      }
    })
  // 只绘制绿色十字
  Canvas(this.canvascontext1)
    .width(300)
    .height(300)
    .backgroundColor(Color.Transparent)
    .onReady(() => {
     this.drawshi1()
    })
    // 不阻止事件传递
    .hitTestBehavior(HitTestMode.Transparent)
}

清空按钮和提交按钮

清空按钮负责清空画板,提交按钮负责将底层画板绘制的内容转化成图片,在下面的image中展示。

Row(){
  // 底层画板清屏
  Button('清空').onClick(()=>{
    let width=this.canvascontext.width
    let height=this.canvascontext.height
    this.canvascontext.clearRect(0, 0, width, height)
  })
  // 将绘制内容转化成图片展示
  Button('提交').onClick(()=>{
    let width=this.canvascontext.width
    let height=this.canvascontext.height
    this.pm= this.canvascontext.getPixelMap(0,0,width,height)
  })
}
.width('100%')
.justifyContent(FlexAlign.SpaceAround)
Image(this.pm)
  .width(200)
  .height(200)

标准绘制过程

// 标准绘制模板 clear---save---设置绘制属性 开始绘制 绘制结束--restore呈现
// draw 是具体的绘制过程 isclear 是在绘制前是否清屏
drawtings(draw:(width:number,height:number)=>void,isclear:boolean=false){
  let width=this.canvascontext.width
  let height=this.canvascontext.height
  if(isclear) {
    this.canvascontext.clearRect(0, 0, width, height)
  }
  this.canvascontext.save()
  draw(width,height)
  this.canvascontext.restore()
}

绘制上层十字画板

上层十字画板要绘制两个线段,一个水平,一个垂直

//绘制十字方法
drawshi1() {
  let width = this.canvascontext1.width
  let height = this.canvascontext1.height
  this.canvascontext1.clearRect(0, 0, width, height)
  this.canvascontext1.save()
  // 线条粗细
  this.canvascontext1.lineWidth = 2
  // 线条颜色
  this.canvascontext1.strokeStyle = '#00ff00'
  // 线条虚线间隔
  this.canvascontext1.setLineDash([10, 20])
  // 开始绘制
  this.canvascontext1.beginPath()
  this.canvascontext1.moveTo(width as number / 2, 0)
  this.canvascontext1.lineTo(width as number / 2, height)
  this.canvascontext1.closePath()
  // 绘制结束
  this.canvascontext1.stroke()

  this.canvascontext1.beginPath()
  this.canvascontext1.moveTo(0, height as number / 2)
  this.canvascontext1.lineTo(width, height as number / 2)
  this.canvascontext1.closePath()
  this.canvascontext1.stroke()
  this.canvascontext1.restore()
}

绘制手势线段

根据手势事件获取坐标点,然后将这些点链接在一起形成线段。

//画线段
drawline(x1:number,y1:number){
  this.canvascontext.lineWidth=3
  this.canvascontext.beginPath()
  this.canvascontext.moveTo(this.x, this.y)
  this.canvascontext.lineTo(x1,y1)
  this.canvascontext.closePath()
  this.canvascontext.stroke()
}
完整代码
@Entry
@Component
struct Canvaspage1 {
  @State message: string = 'Hello World';
  // 定义了两个canvas画板 两个设置项
  private settings:RenderingContextSettings=new RenderingContextSettings(true)
  private canvascontext:CanvasRenderingContext2D=new CanvasRenderingContext2D(this.settings)
  private settings1:RenderingContextSettings=new RenderingContextSettings(true)
  private canvascontext1:CanvasRenderingContext2D=new CanvasRenderingContext2D(this.settings1)
  // 记录手指滑动坐标
  private x:number=0
  private y:number=0
  // 保存图片
  @State pm:PixelMap|null=null
  build() {
    Column({space:20}){
      Text('签名').fontSize(20).fontWeight(FontWeight.Bolder)
      // 层叠了两个画板 一个负责显示绿十字  一个负责接受绘制
      Stack() {
        // 负责绘制
        Canvas(this.canvascontext)
          .width(300)
          .height(300)
          .backgroundColor(Color.Grey)
          .onReady(() => {
          })
          .onTouch((event) => {
            let x1 = 0
            let y1 = 0
            if (event.type == TouchType.Down) {
              this.x = event.touches[0].x
              this.y = event.touches[0].y
            } else if (event.type == TouchType.Move) {
              x1 = event.touches[0].x
              y1 = event.touches[0].y
              // 手指滑动的时候 跟着手指绘制
              this.drawtings((width: number, height: number) => {
                this.drawline(x1, y1)
              })
              this.x = x1
              this.y = y1
            }
          })
        // 只绘制绿色十字
        Canvas(this.canvascontext1)
          .width(300)
          .height(300)
          .backgroundColor(Color.Transparent)
          .onReady(() => {
           this.drawshi1()
          })
          // 不阻止事件传递
          .hitTestBehavior(HitTestMode.Transparent)
      }
      Row(){
        // 底层画板清屏
        Button('清空').onClick(()=>{
          let width=this.canvascontext.width
          let height=this.canvascontext.height
          this.canvascontext.clearRect(0, 0, width, height)
        })
        // 将绘制内容转化成图片展示
        Button('提交').onClick(()=>{
          let width=this.canvascontext.width
          let height=this.canvascontext.height
          this.pm= this.canvascontext.getPixelMap(0,0,width,height)
        })
      }
      .width('100%')
      .justifyContent(FlexAlign.SpaceAround)
      Image(this.pm)
        .width(200)
        .height(200)
    }
    .height('100%')
    .width('100%')
    .justifyContent(FlexAlign.Center)
  }
  //画线段
  drawline(x1:number,y1:number){
    this.canvascontext.lineWidth=3
    this.canvascontext.beginPath()
    this.canvascontext.moveTo(this.x, this.y)
    this.canvascontext.lineTo(x1,y1)
    this.canvascontext.closePath()
    this.canvascontext.stroke()
  }
  // 标准绘制模板 clear---save---设置绘制属性 开始绘制 绘制结束--restore呈现
  // draw 是具体的绘制过程 isclear 是在绘制前是否清屏
  drawtings(draw:(width:number,height:number)=>void,isclear:boolean=false){
    let width=this.canvascontext.width
    let height=this.canvascontext.height
    if(isclear) {
      this.canvascontext.clearRect(0, 0, width, height)
    }
    this.canvascontext.save()
    draw(width,height)
    this.canvascontext.restore()
  }
  //绘制十字方法
  drawshi1() {
    let width = this.canvascontext1.width
    let height = this.canvascontext1.height
    this.canvascontext1.clearRect(0, 0, width, height)
    this.canvascontext1.save()
    // 线条粗细
    this.canvascontext1.lineWidth = 2
    // 线条颜色
    this.canvascontext1.strokeStyle = '#00ff00'
    // 线条虚线间隔
    this.canvascontext1.setLineDash([10, 20])
    // 开始绘制
    this.canvascontext1.beginPath()
    this.canvascontext1.moveTo(width as number / 2, 0)
    this.canvascontext1.lineTo(width as number / 2, height)
    this.canvascontext1.closePath()
    // 绘制结束
    this.canvascontext1.stroke()

    this.canvascontext1.beginPath()
    this.canvascontext1.moveTo(0, height as number / 2)
    this.canvascontext1.lineTo(width, height as number / 2)
    this.canvascontext1.closePath()
    this.canvascontext1.stroke()
    this.canvascontext1.restore()
  }
}


http://www.kler.cn/a/549690.html

相关文章:

  • 微软官方出品GPT大模型编排工具:7个开源项目
  • mysql快照读当前读
  • LlamaFactory可视化模型微调-Deepseek模型微调+CUDA Toolkit+cuDNN安装
  • 缓存的介绍
  • STM32的DMA解释
  • hivemetastore 连接过多导致sql查询慢
  • Fiori APP配置中的Semantic object 小bug
  • 如何避免redis长期运行持久化AOF文件过大的问题:AOF重写
  • 深度学习:基于Qwen复现DeepSeek R1的推理能力
  • 基于mediapipe深度学习的手势数字识别系统python源码
  • 力扣 乘积最大子数组
  • 【动态路由】系统Web URL资源整合系列(后端技术实现)【apisix实现】
  • MySQL8.x版本的新的功能特性总结
  • 提升顾客转化率:融合2+1链动模式AI智能名片与S2B2C商城小程序的创新策略
  • 一文讲明白RAG 与 KAG 的区别:自然语言处理中的知识增强方法对比
  • 文件上传功能(四)——项目集成
  • Hive之分区表
  • 使用sublime_text中,TAB键无效怎么解决???
  • 【第4章:循环神经网络(RNN)与长短时记忆网络(LSTM)——4.2 LSTM的引入与解决长期依赖问题的方法】
  • Qt QOpenGLShaderProgram详解