首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >鸿蒙Next使用Canvas绘制一个汽车仪表盘

鸿蒙Next使用Canvas绘制一个汽车仪表盘

原创
作者头像
用户4773577
修改2025-06-28 20:24:52
修改2025-06-28 20:24:52
14500
代码可运行
举报
运行总次数:0
代码可运行

本文通过实现一个汽车仪表盘,介绍使用CanvasRenderingContext2D在Canvas组件上进行绘制图形。 看一下最终演示:

先了解一下CanvasRenderingContext2D都有哪些属性方法

属性名

说明

fillStyle

指定绘制的填充色。

lineWidth

设置绘制线条的宽度。

strokeStyle

设置线条的颜色。

lineCap

指定线端点的样式。

lineJoin

指定线段间相交的交点样式。

miterLimit

设置斜接面限制值。

font

设置文本绘制中的字体样式。

textAlign

设置文本绘制中的文本对齐方式。

textBaseline

设置文本绘制中的水平对齐方式。

globalAlpha

设置透明度。

lineDashOffset

设置画布的虚线偏移量。

globalCompositeOperation

设置合成操作的方式。

shadowBlur

设置绘制阴影时的模糊级别。

shadowColor

设置绘制阴影时的阴影颜色。

shadowOffsetX

设置绘制阴影时和原有对象的水平偏移值。

shadowOffsetY

设置绘制阴影时和原有对象的垂直偏移值。

imageSmoothingEnabled

用于设置绘制图片时是否进行图像平滑度调整。

height

组件高度。

width

组件宽度。

imageSmoothingQuality

imageSmoothingEnabled 为 true 时,用于设置图像平滑度。

direction

用于设置绘制文字时使用的文字方向。

filter

用于设置图像的滤镜。

canvas

获取和 CanvasRenderingContext2D 关联的 Canvas 组件的 FrameNode 实例。

本文使用的几个方法以放到前面,可以参考说明。

方法名

说明

beginPath

创建一个新的绘制路径

arc

绘制弧线路径

stroke

根据当前的路径,进行边框绘制操作

save

将当前状态放入栈中,保存canvas的全部状态,通常在需要保存绘制状态时调用

translate

移动当前坐标系的原点

rotate

针对当前坐标轴进行顺时针旋转

restore

对保存的绘图上下文进行恢复

fillText

绘制填充类文本

fillRect

填充一个矩形

strokeRect

绘制具有边框的矩形,矩形内部不填充

clearRect

删除指定区域内的绘制内容

strokeText

绘制描边类文本

measureText

返回一个文本测算的对象

moveTo

路径从当前点移动到指定点

lineTo

从当前点到指定点进行路径连接

closePath

结束当前路径形成一个封闭路径

quadraticCurveTo

创建二次贝赛尔曲线的路径

arcTo

依据给定的控制点和圆弧半径创建圆弧路径

ellipse

在规定的矩形区域绘制一个椭圆

rect

创建矩形路径

fill

对当前路径进行填充

clip

设置当前路径为剪切路径

reset

重置为其默认状态,清除后台缓冲区、绘制状态栈、绘制路径和样式

saveLayer

创建一个图层

scale

设置canvas画布的缩放变换属性,后续的绘制操作将按照缩放比例进行缩放

drawImage

进行图像绘制

createLinearGradient

创建一个线性渐变色

源码:

代码语言:javascript
代码运行次数:0
运行
复制
import { getScreenWidth } from '../utils/DisplayUtil';

@Entry
@ComponentV2
struct CanvasTest {
  private settings: RenderingContextSettings = new RenderingContextSettings(true);
  private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings);
  private context2: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings);
  private context3: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings);
  @Local centerX: number = 0
  @Local centerY: number = 0
  @Local currentSpeed: number = 0
  @Local angle: number = 0
  @Local startAngle:number= Math.PI * 140 / 180   //弧线的起始弧度
  @Local endAngle:number= Math.PI * 40 / 180   //弧线的起始弧度

  //绘制渐变外环
  draw(){
    this.context3.clearRect(0,0,2*this.centerX,2*this.centerX)
    this.context3.beginPath()
    let grad = this.context3.createLinearGradient(this.getStartX(), this.getStartY(), this.getEndX(), this.getEndY())
    grad.addColorStop(0, '#0000ff00')
    grad.addColorStop(0.9, '#00ff00')
    if (this.currentSpeed>120) {
      grad.addColorStop(1, '#ff0000')
    }else {
      grad.addColorStop(1, '#00ff00')
    }
    this.context3.strokeStyle = grad;
    this.context3.lineWidth = 4;
    this.context3.arc(this.centerX, this.centerY, this.centerX - 4, this.startAngle, Math.PI * (140+(this.currentSpeed * 260 / 180) ) / 180)
    this.context3.stroke()
  }

  build() {
    Column() {
      Stack() {
        Canvas(this.context)
          .width('100%')
          .height('100%')
          .backgroundColor("#fceb99")
          .onReady(() => {
            //绘制外环
            this.context.beginPath()
            this.context.strokeStyle = '#0000ff';
            this.context.lineWidth = 2;
            this.context.arc(this.centerX, this.centerY, this.centerX - 10, this.startAngle, this.endAngle)
            this.context.stroke()
            //绘制内环
            this.context.beginPath()
            this.context.strokeStyle = '#07A6EC';
            this.context.lineWidth = 8;
            this.context.arc(this.centerX, this.centerY, this.centerX - 16, this.startAngle,  this.endAngle)
            this.context.stroke()
            //绘制刻度
            this.context.save()
            this.context.translate(this.centerX, this.centerY);
            this.context.rotate(Math.PI * 50 / 180)
            for (let i = 0; i <= 36; i++) {
              if (i % 4 == 0) {
                this.context.beginPath()
                this.context.lineWidth = 4;
                this.context.strokeStyle = '#07A6EC';
                this.context.moveTo(0, this.centerY - 34);
                this.context.lineTo(0, this.centerY - 12);
                this.context.stroke();
              } else {
                this.context.beginPath()
                this.context.lineWidth = 2;
                this.context.strokeStyle = '#07A6EC';
                this.context.moveTo(0, this.centerY - 26);
                this.context.lineTo(0, this.centerY - 12);
                this.context.stroke();
              }
              this.context.rotate(Math.PI * (260 / 36) / 180)
            }
            this.context.restore()
            //绘制数字
            this.context.save()
            this.context.translate(this.centerX, this.centerY);
            for (let i = 0; i < 10; i++) {
              // 转换为弧度
              const radians = (140 + i * (260 / 9)) * Math.PI / 180;
              // 计算坐标
              const x = (this.centerY - 60) * Math.cos(radians);
              const y = (this.centerY - 60) * Math.sin(radians);
              this.context.textAlign = 'center'
              this.context.textBaseline = 'middle'
              this.context.font = '30vp sans-serif'
              this.context.fillText(i * 20 + '', x, y)
            }
            //绘制中间单位
            this.context.textAlign = 'center'
            this.context.textBaseline = 'middle'
            this.context.font = '30vp sans-serif'
            this.context.fillText('km/h', 0, -40)
            this.context.restore()
            //绘制红色圆环
            this.context.translate(0, 0);
            this.context.beginPath()
            this.context.strokeStyle = Color.Red;
            this.context.lineWidth = 3;
            this.context.arc(this.centerX, this.centerY, this.centerX - 100, this.startAngle,  this.endAngle)
            this.context.stroke()
          })
        Canvas(this.context3)
          .width('100%')
          .height('100%')

        Canvas(this.context2)
          .width('100%')
          .height('100%')
          .onReady(() => {
            this.context2.beginPath()
            this.context2.lineWidth = 3;
            this.context2.strokeStyle = "#95ff00";
            this.context2.moveTo(this.centerX, this.centerY)
            this.context2.lineTo(this.centerX, 14)
            this.context2.stroke()
          })
          .rotate({ centerX: this.centerX, centerY: this.centerY, angle: this.cacleAngle() })

        Text(this.currentSpeed + '').fontColor(Color.Black).fontSize(30).offset({ y: 20 })

      }.height('50%').width('100%')
      .onSizeChange((oldValue: SizeOptions, newValue: SizeOptions) => {
        this.centerX = (newValue.width as number) / 2
        this.centerY = (newValue.height as number) / 2
      })

      Slider({
        value: this.currentSpeed,
        min: 0,
        max: 180,
        step: 1,
      })
        .width('80%')
        .blockColor("#0099ff")//设置滑块的颜色
        .onChange((value: number, mode: SliderChangeMode) => {
          this.currentSpeed = value
        })
    }
  }

  cacleAngle(): number {
    this.draw();
    // (当前速度/最大速度180)*圆环角度260- 默认其实位置从中心位置向左旋转130
    return (this.currentSpeed * 260 / 180) - 130
  }

  getStartX() {
    const angleRadians = 140 * Math.PI / 180;
    return this.centerX + this.centerX * Math.cos(angleRadians);
  }

  getStartY(): number {
    const angleRadians = 140 * Math.PI / 180;
    return this.centerX + this.centerX * Math.sin(angleRadians);
  }

  getEndX() {
    const angleRadians = (140+(this.currentSpeed * 260 / 180) )* Math.PI / 180;
    return this.centerX + this.centerX * Math.cos(angleRadians);
  }
  getEndY(): number {
    const angleRadians = (140+(this.currentSpeed * 260 / 180))* Math.PI / 180;
    return this.centerX + this.centerX * Math.sin(angleRadians);
  }
}

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档