开天辟地 HarmonyOS(鸿蒙) - canvas: 通过 CanvasRenderingContext2D 绘制图形(简单图形,路径图形,贝塞尔曲线,Path2D,画笔样式,渐变色,图像色)

源码 https://github.com/webabcd/HarmonyDemo
作者 webabcd

开天辟地 HarmonyOS(鸿蒙) - canvas: 通过 CanvasRenderingContext2D 绘制图形(简单图形,路径图形,贝塞尔曲线,Path2D,画笔样式,渐变色,图像色)

示例如下:

pages\canvas\CanvasRenderingContext2DDemo.ets

/*
 * CanvasRenderingContext2D - 按照 W3C 标准封装了本地绘图接口(效率比 DrawingRenderingContext 低)
 *
 * 本例用于演示如何通过 CanvasRenderingContext2D 绘制简单图形,路径图形,贝塞尔曲线,Path2D,画笔样式,渐变色,图像色
 */

import { TitleBar } from '../TitleBar';

@Entry
@Component
struct CanvasRenderingContext2DDemo {

  build() {
    Column() {
      TitleBar()
      Tabs() {
        TabContent() { MySample1() }.tabBar('简单图形').align(Alignment.Top)
        TabContent() { MySample2() }.tabBar('路径图形').align(Alignment.Top)
        TabContent() { MySample3() }.tabBar('贝塞尔曲线').align(Alignment.Top)
        TabContent() { MySample4() }.tabBar('Path2D').align(Alignment.Top)
        TabContent() { MySample5() }.tabBar('画笔样式').align(Alignment.Top)
        TabContent() { MySample6() }.tabBar('渐变色').align(Alignment.Top)
        TabContent() { MySample7() }.tabBar('图像色').align(Alignment.Top)
      }
      .scrollable(true)
      .barMode(BarMode.Scrollable)
      .layoutWeight(1)
    }
  }
}

// 演示简单图形
@Component
struct MySample1 {

  private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(new RenderingContextSettings(true))

  @State message: string = ""

  build() {
    Column({ space: 10 }) {

      Text(this.message)

      /*
       * CanvasRenderingContext2D - 按照 W3C 标准封装了本地绘图接口
       *   width, height - 画布的宽和高
       *   fillStyle - 填充颜色
       *   strokeStyle - 画笔颜色
       *   lineWidth - 画笔宽度
       *   fillRect() - 用填充颜色填充一个矩形区域
       *     通过参数指定矩形的 x, y, width, height
       *   strokeRect() - 用画笔画一个矩形框
       *     通过参数指定矩形的 x, y, width, height
       */
      Canvas(this.context).width('100%').height('100%').backgroundColor(Color.Yellow)
        .onReady(() => {
          this.message = `canvas width:${this.context.width}\n`
          this.message += `canvas height:${this.context.height}`

          this.context.fillStyle = Color.Red
          this.context.fillRect(0, 0, 200, 100)

          this.context.strokeStyle = Color.Green
          this.context.lineWidth = 20
          this.context.strokeRect(0, 120, 200, 100)
        })
    }
  }
}

// 路径图形
@Component
struct MySample2 {

  private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(new RenderingContextSettings(true))

  build() {
    Column({ space: 10 }) {

      /*
       * CanvasRenderingContext2D - 按照 W3C 标准封装了本地绘图接口
       *   fillStyle - 填充颜色
       *   strokeStyle - 画笔颜色
       *   lineWidth - 画笔宽度
       *
       *   beginPath() - 绘制新的路径
       *   closePath() - 通过直线路径连接路径的首尾两端
       *   stroke() - 用画笔连接路径
       *   fill() - 填充路径组成的图形的内部
       *
       *   moveTo() - 路径移动到指定的点
       *   lineTo() - 之前 moveTo() 点通过直线路径连接 lineTo() 点
       *   rect() - 矩形路径
       *   ellipse() - 椭圆路径
       *   arc() - 圆弧路径
       *   arcTo() - 圆弧路径
       */
      Canvas(this.context).width('100%').height('100%').backgroundColor(Color.Yellow)
        .onReady(() => {

          this.context.fillStyle = Color.Red
          this.context.strokeStyle = Color.Green
          this.context.lineWidth = 2

          this.context.beginPath()
          this.context.moveTo(0, 0)
          this.context.lineTo(100, 40)
          this.context.stroke()

          this.context.beginPath()
          this.context.moveTo(200, 0)
          this.context.lineTo(300, 0)
          this.context.lineTo(250, 40)
          this.context.closePath()
          this.context.stroke()
          this.context.fill()

          this.context.beginPath()
          // 矩形路劲,参数按顺序分别是 x, y, width, height
          this.context.rect(0, 50, 200, 40)
          this.context.stroke()
          this.context.fill()

          this.context.beginPath()
          // 椭圆路劲,参数按顺序分别是
          //   圆心的 x, 圆心的 y, 椭圆的 x 轴半径, 椭圆的 y 轴半径
          //   椭圆的旋转弧度, 绘制的起始点的弧度, 绘制的结束点的弧度, 是否逆时针方向绘制
          this.context.ellipse(100, 150, 100, 40, 0, 0, Math.PI * 1.5, false)
          this.context.stroke()

          this.context.beginPath()
          // 圆弧路劲,参数按顺序分别是
          //   圆心的 x, 圆心的 y, 圆弧的半径
          //   绘制的起始点的弧度, 绘制的结束点的弧度, 是否逆时针方向绘制
          this.context.arc(100, 250, 40, 0, Math.PI * 1.5, false)
          this.context.stroke()

          this.context.beginPath()
          this.context.rect(50, 300, 100, 100)
          this.context.fill()
          this.context.beginPath();
          this.context.moveTo(50, 300)
          // 圆弧路劲,参数按顺序分别是
          //   控制点 1 的 x, y, 控制点 2 的 x, y, 圆弧的半径
          // 关于 arcTo() 的说明:以 moveTo() 点为起点, 控制点 2 为终点,圆弧偏向控制点 1 的方向,然后通过指定的半径画圆弧
          this.context.arcTo(50, 400, 150, 400, 100);
          this.context.stroke()
        })
    }
  }
}

// 贝塞尔曲线
@Component
struct MySample3 {

  private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(new RenderingContextSettings(true))

  build() {
    Column({ space: 10 }) {

      Row() {
        Image('/pages/canvas/quadratic.gif').layoutWeight(1)
        Image('/pages/canvas/bezier.gif').layoutWeight(1)
      }

      /*
       * CanvasRenderingContext2D - 按照 W3C 标准封装了本地绘图接口
       *   quadraticCurveTo() - 二次贝赛尔曲线
       *   bezierCurveTo() - 三次贝赛尔曲线
       */
      Canvas(this.context).width('100%').height('100%').backgroundColor(Color.Yellow)
        .onReady(() => {

          this.context.fillStyle = Color.Red
          this.context.strokeStyle = Color.Green
          this.context.lineWidth = 2

          this.context.beginPath()
          this.context.moveTo(10, 100)
          // 二次贝赛尔曲线,参数按顺序分别是
          //   控制点的 x, y, 曲线终点的 x, y
          // 注:曲线起点为 moveTo() 点
          this.context.quadraticCurveTo(30, 10, 300, 100)
          this.context.stroke()

          this.context.beginPath()
          this.context.moveTo(10, 300)
          // 三次贝赛尔曲线,参数按顺序分别是
          //   控制点 1 的 x, y, 控制点 2 的 x, y, 曲线终点的 x, y
          // 注:曲线起点为 moveTo() 点
          this.context.bezierCurveTo(20, 120, 70, 150, 300, 300)
          this.context.stroke()
        })
    }
  }
}

// Path2D
@Component
struct MySample4 {

  private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(new RenderingContextSettings(true))

  build() {
    Column({ space: 10 }) {

      /*
       * CanvasRenderingContext2D - 按照 W3C 标准封装了本地绘图接口
       *   stroke() - 用画笔绘制指定的 Path2D 对象
       *
       * Path2D - 路径
       *   实例化时可以传入一个 svg(Scalable Vector Graphics) 的 path 路径
       *   addPath() - 当前路径再叠加一个指定的路径
       *   closePath() - 通过直线路径连接路径的首尾两端
       *   moveTo(), lineTo(), arc(), arcTo(), ellipse(), rect(), bezierCurveTo(), quadraticCurveTo() - 这几个的用法参见本例中 MySample2 和 MySample3 的说明
       */
      Canvas(this.context).width('100%').height('100%').backgroundColor(Color.Yellow)
        .onReady(() => {

          this.context.strokeStyle = Color.Green
          this.context.lineWidth = 5

          // 指定一个 svg 的 path 路径,其是一个五角星图形
          let path2D_1: Path2D = new Path2D('M108 0 L141 70 L218 78.3 L162 131 L175 205 L108 170 L41.2 205 L55 131 L1 78 L75 68 L108 0 Z')

          let path2D_2: Path2D = new Path2D()
          path2D_2.moveTo(200, 100)
          path2D_2.lineTo(300, 100)
          path2D_2.lineTo(200, 200)
          path2D_2.closePath()

          let path2D: Path2D = new Path2D()
          path2D.addPath(path2D_1)
          path2D.addPath(path2D_2)

          this.context.stroke(path2D)
        })
    }
  }
}

// 画笔样式
@Component
struct MySample5 {

  private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(new RenderingContextSettings(true))

  build() {
    Column({ space: 10 }) {

      /*
       * CanvasRenderingContext2D - 按照 W3C 标准封装了本地绘图接口
       *   strokeStyle - 画笔颜色
       *   lineWidth - 画笔宽度
       *   lineCap - 线段两端的样式(默认值为 butt)
       *     butt - 无特殊处理
       *     round - 有一个半圆形的延伸(这个半圆形的半径为: lineWidth / 2)
       *     square - 有一个矩形的延伸(这个矩形的长度为: lineWidth / 2)
       *   lineJoin - 线段交点的处理方式(默认值为 miter),比如三角形的三个顶点
       *     round - 圆角处理
       *     bevel - 斜切处理
       *     miter - 尖角处理
       *   miterLimit - 当 lineJoin 为 miter 时,线段连接处的那个“尖”的极限长度(默认值为 10px)
       *   setLineDash() - 一个数组,第 1 个元素是虚线的实线部分的长度,第 2 个元素是虚线的空白部分的长度
       *   lineDashOffset - 虚线的偏移距离
       *     虚线的起始部分是先实线再空白,如果需要调整虚线的起始部分就可以通过 lineDashOffset 实现
       */
      Canvas(this.context).width('100%').height('100%').backgroundColor(Color.Yellow)
        .onReady(() => {

          this.context.strokeStyle = Color.Green
          this.context.lineWidth = 20

          this.context.beginPath()
          this.context.lineCap = 'butt'
          this.context.moveTo(20, 20)
          this.context.lineTo(300, 20)
          this.context.stroke()
          this.context.beginPath()
          this.context.lineCap = 'round'
          this.context.moveTo(20, 50)
          this.context.lineTo(300, 50)
          this.context.stroke()
          this.context.beginPath()
          this.context.lineCap = 'square'
          this.context.moveTo(20, 80)
          this.context.lineTo(300, 80)
          this.context.stroke()

          this.context.beginPath()
          this.context.lineJoin = 'round'
          this.context.moveTo(20, 120)
          this.context.lineTo(100, 150)
          this.context.lineTo(20, 180)
          this.context.stroke()
          this.context.beginPath()
          this.context.lineJoin = 'bevel'
          this.context.moveTo(120, 120)
          this.context.lineTo(200, 150)
          this.context.lineTo(120, 180)
          this.context.stroke()
          this.context.beginPath()
          this.context.lineJoin = 'miter'
          this.context.moveTo(220, 120)
          this.context.lineTo(300, 150)
          this.context.lineTo(220, 180)
          this.context.stroke()

          this.context.lineJoin = 'miter'
          this.context.miterLimit = 0
          this.context.beginPath()
          this.context.moveTo(20, 220)
          this.context.lineTo(100, 250)
          this.context.lineTo(20, 280)
          this.context.stroke()
          this.context.beginPath()
          this.context.miterLimit = 10
          this.context.moveTo(120, 220)
          this.context.lineTo(200, 250)
          this.context.lineTo(120, 280)
          this.context.stroke()

          this.context.lineCap = 'butt'
          this.context.lineWidth = 2
          this.context.beginPath()
          this.context.setLineDash([10, 30])
          this.context.lineDashOffset = 0
          this.context.moveTo(0, 320)
          this.context.lineTo(300, 320)
          this.context.stroke()
          this.context.beginPath()
          this.context.setLineDash([10, 30])
          this.context.lineDashOffset = -5
          this.context.moveTo(0, 350)
          this.context.lineTo(300, 350)
          this.context.stroke()
        })
    }
  }
}

// 渐变色
@Component
struct MySample6 {

  private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(new RenderingContextSettings(true))

  build() {
    Column({ space: 10 }) {

      /*
       * CanvasRenderingContext2D - 按照 W3C 标准封装了本地绘图接口
       *   createLinearGradient() - 线性渐变
       *   createRadialGradient() - 放射渐变
       *   createConicGradient() - 扫描式渐变
       *     addColorStop() - 设置渐变点的位置和颜色
       *       offset - 渐变点的位置(0 - 1 之间)
       *       color - 渐变点的颜色
       *   fillStyle - 使用指定的渐变作为填充色
       *   strokeStyle - 使用指定的渐变作为画笔色
       */
      Canvas(this.context).width('100%').height('100%').backgroundColor(Color.Yellow)
        .onReady(() => {
          // 线性渐变,参数按顺序分别是
          //   渐变起始点的 x, y, 渐变结束点的 x, y
          let linearGradient = this.context.createLinearGradient(0, 0, 300, 100)
          linearGradient.addColorStop(0.0, '#ff0000')
          linearGradient.addColorStop(0.5, '#00ff00')
          linearGradient.addColorStop(1.0, '#0000ff')
          this.context.fillStyle = linearGradient
          this.context.fillRect(0, 0, 300, 100)

          // 放射渐变,参数按顺序分别是
          //   渐变起始圆的 x, y, 半径, 渐变结束圆的 x, y, 半径
          let radialGradient = this.context.createRadialGradient(100, 250, 10, 100, 250, 100)
          radialGradient.addColorStop(0.0, '#ff0000')
          radialGradient.addColorStop(0.5, '#00ff00')
          radialGradient.addColorStop(1.0, '#0000ff')
          this.context.fillStyle = radialGradient
          this.context.fillRect(0, 150, 200, 200)

          // 扫描式渐变,参数按顺序分别是
          //   渐变的起始弧度, 渐变中心点的 x, y
          let conicGradient = this.context.createConicGradient(0, 100, 500)
          conicGradient.addColorStop(0.0, '#ff0000')
          conicGradient.addColorStop(0.5, '#00ff00')
          conicGradient.addColorStop(1.0, '#0000ff')
          this.context.fillStyle = conicGradient
          this.context.fillRect(0, 400, 200, 200)
        })
    }
  }
}

// 图像色
@Component
struct MySample7 {

  private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(new RenderingContextSettings(true))
  private imageBitmap = new ImageBitmap("resources/base/media/son.jpg")

  build() {
    Column({ space: 10 }) {

      /*
       * CanvasRenderingContext2D - 按照 W3C 标准封装了本地绘图接口
       *   createPattern() - 重复图像
       *     参数按顺序分别是: 源图像, 重复方式
       *   fillStyle - 使用指定的重复图像作为填充色
       *   strokeStyle - 使用指定的重复图像作为画笔色
       */
      Canvas(this.context).width('100%').height('100%').backgroundColor(Color.Yellow)
        .onReady(() => {

          let pattern = this.context.createPattern(this.imageBitmap, 'repeat')! // 重复
          this.context.fillStyle = pattern
          this.context.fillRect(0, 0, 300, 180)

          pattern = this.context.createPattern(this.imageBitmap, 'mirror')! // 翻转重复
          this.context.fillStyle = pattern
          this.context.fillRect(0, 200, 300, 180)
        })
    }
  }
}

源码 https://github.com/webabcd/HarmonyDemo
作者 webabcd

posted @ 2025-03-21 16:24  webabcd  阅读(141)  评论(0)    收藏  举报