鸿蒙canvans的使用-仿照QQ聊天气泡效果

开胃小菜

@Entry
@Component
struct Index {
  //2D渲染
  context: CanvasRenderingContext2D = new CanvasRenderingContext2D();

  build() {
    Row() {
      Column({ space: 20 }) {
        Canvas(this.context)//绘制东西需要上下文,类似画笔
          .width('100%')//宽度
          .aspectRatio(1.5)//宽高比
          .backgroundColor(Color.Gray)//背景色
          .onReady(() => { //canvas准备好了的时候回调,如果画布没出来,是不会回调该方法的。
            //静态的可以再这里绘制
            this.context.beginPath();
            this.context.strokeStyle ="#123"//颜色
            this.context.lineWidth = 4//线宽
            this.context.rect(1,2,3,4)//绘制矩形
            this.context.fill()//填充画笔
            this.context.stroke()//类似于Android的Invalidate()
            this.context.closePath();
            
            //绘制path路径
            this.context.beginPath();
            this.context.strokeStyle = '#0f0';
            this.context.moveTo(20,20);//起点移动
            this.context.lineTo(100,100);//与起点确定一条直线
            this.context.stroke();
            this.context.closePath();

            this.context.clearRect(0,0,360,240);//清空上面所绘制的内容
          })
          .onAreaChange((old: Area, newArea: Area) => {//大小位置变化的时候调用。
            //old 和 newArea两个参数只能是具体的vp值,而不是百分比,可以打印输出值,以方便看结果
            console.log("jett", "old:" + JSON.stringify(old));
            console.log("jett", "newArea:" + JSON.stringify(newArea));
          })
        Row({space:20}){
          Button("Canvas上给制")
            .onClick(()=>{
              //可以在这里直接画  基本操作
              this.context.beginPath();
              this.context.strokeStyle = '#F00';
              this.context.lineWidth = 4;
              // this.context.rect(50, 50, 100, 100);
              this.context.arc(180, 120, 100, 60, 270, true);
              // this.context.fill();
              this.context.stroke(); //完成绘制的渲染
              this.context.closePath();

              //画path路径
              this.context.beginPath();
              this.context.strokeStyle = '#0F0';
              this.context.moveTo(20,20);
              this.context.lineTo(100,100);
              this.context.lineTo(150,200);
              this.context.stroke(); //完成绘制的渲染
              this.context.closePath();
            })
          Button("清除Canvas")
            .onClick(()=>{
              this.context.clearRect(0,0,360,240); //数值前面打印了
            })
        }
      }
      .width('100%')
    }
    .height('100%')
  }
}

仿照QQ气泡的绘制

在这里插入图片描述

import { ComponentRect } from '../common/ComponentRect';
import { Point } from '../common/Point';
import { Utils } from '../utils/Utils';

@Entry
@Component
struct Index {
  //气泡中显示的字
  @State message: string = '99+';
  //水滴的位置
  @State positionX: number = 0;
  @State positionY: number = 0;
  //Text控件的宽高:水滴组件区域
  textComponentRect: ComponentRect = new ComponentRect();
  //定义一个画笔
  context: CanvasRenderingContext2D = new CanvasRenderingContext2D();
  //两个圆心
  startAounRadius: number = 20;
  endAoundRadius: number = 20;
  //连接桥坐标
  startPoint: Point = new Point();
  endPoint: Point = new Point();
  //aPoint,bPoint,cPoint,dPoint为图中ABCD四个点
  aPoint: Point = new Point();
  bPoint: Point = new Point();
  cPoint: Point = new Point();
  dPoint: Point = new Point();
 //绘制圆的图标
  drawAround(x: number, y: number, r: number) {
    this.context.beginPath();
    this.context.lineWidth = 3;
    this.context.strokeStyle = "#f00"
    //360表示圆
    this.context.arc(x, y, r, 0, 360);
    this.context.stroke();
    //每调一次就关闭路径
    this.context.closePath();
  }
   //绘制路径
  drawPath() {
    //两个圆的圆心之间连线的中点
    let widthX: number = this.endPoint.x - this.startPoint.x;
    let widthY: number = this.endPoint.y - this.startPoint.y;
    //获取角度
    let angle: number = Math.atan(widthY / widthX);
    //获取圆心到四个点的偏移值,使用三角函数
    let offsetX: number = Math.sin(angle) * this.startAounRadius;
    let offsetY: number = Math.cos(angle) * this.startAounRadius;
    //abcd四个点的位置
    this.aPoint.x = this.startPoint.x - offsetX;
    this.aPoint.y = this.startPoint.y + offsetY;
    this.bPoint.x = this.endPoint.x - offsetX;
    this.bPoint.y = this.endPoint.y + offsetY;
    this.cPoint.x = this.endPoint.x + offsetX;
    this.cPoint.y = this.endPoint.y - offsetY;
    this.dPoint.x = this.startPoint.x + offsetX;
    this.dPoint.y = this.startPoint.y - offsetY;
    //控制点,
    let ctrlX: number = (this.startPoint.x + this.endPoint.x) / 2;
    let ctrlY: number = (this.startPoint.y + this.endPoint.y) / 2;

    //画出路径
    this.context.beginPath();
    this.context.moveTo(this.aPoint.x, this.aPoint.y);
    this.context.lineTo(this.dPoint.x, this.dPoint.y);
    //2阶贝塞尔曲线
    this.context.quadraticCurveTo(ctrlX, ctrlY, this.cPoint.x, this.cPoint.y);
    this.context.lineTo(this.bPoint.x, this.bPoint.y);
    this.context.quadraticCurveTo(ctrlX, ctrlY, this.aPoint.x, this.aPoint.y);
    this.context.stroke();
    this.context.closePath();

  }

  build() {
    Row() {
      Flex() {
        Canvas(this.context)
          .width('100%').height('100%')
          .onAreaChange((old: Area, newArea: Area) => {
            //通过日志,拿到画布的大小。
            console.log("old:" + JSON.stringify(old));
            console.log("newArea:" + JSON.stringify(newArea));
          })
        //水滴
        Text(this.message)
          //修改组件id getInspectorByKey('water'),使用该方法,根据id获取组件信息
          //得到的单位是像素点
          .id("water")
          .fontSize(30)
          .fontWeight(FontWeight.Bold)
          .backgroundColor(Color.Red)
          .padding(10)
          //改成圆角或圆形
          .border({ radius: 90 })
          //定位,跟着positionX和positionY的变化而变化,positionX和positionY的位置在控件的左上点
          .position({ x: this.positionX, y: this.positionY })
          //手指按上去拖动可以使用onTouch方法,在移动过程中绘制
          .onTouch((event: TouchEvent) => {
            if (event.type === TouchType.Down) {
              //获取红点的矩形区域
              this.textComponentRect = Utils.getComponentRect("water");

              //px与vp需要转换,获取组件的长和宽的一半,点击组件的时候该组件的中心点移动到点击位置
              //this.textComponentRect.right / 2 拿到的是vp值
              //event.touches[0].screenX - width;获取到的是像素值
              //px2vp() 是官方API
              let width: number = px2vp(this.textComponentRect.right / 2);
              let height: number = px2vp(this.textComponentRect.bottom / 2);
               //0表示down,1表示move,2表示up
              this.positionX = event.touches[0].screenX - width;
              this.positionY = event.touches[0].screenY - height;

              this.startPoint.x = width;
              this.startPoint.y = height;
              //按下的时候绘制圆
              this.drawAround(this.startPoint.x, this.startPoint.y, this.startAounRadius);
            }
            if (event.type === TouchType.Up) {
              //px与vp需要转换
              let width: number =  (this.textComponentRect.right / 2);
              let height: number = px2vp(this.textComponentRect.bottom / 2);

              this.positionX = 0;
              this.positionY = 0;

              this.endPoint.x = event.touches[0].screenX;
              this.endPoint.y = event.touches[0].screenY;
              this.context.clearRect(0, 0, 360, 780);
            }
            if (event.type === TouchType.Move) {
              //px与vp需要转换,
              let width: number = px2vp(this.textComponentRect.right / 2);
              let height: number = px2vp(this.textComponentRect.bottom / 2);
              this.positionX = event.touches[0].screenX - width;
              this.positionY = event.touches[0].screenY - height;

              this.endPoint.x = event.touches[0].screenX;
              this.endPoint.y = event.touches[0].screenY;
              //清除上一次绘制的圆,不然会有无数个园,360和780是onAreaChange()中计算出来的长和宽,这里是整个屏幕
              this.context.clearRect(0, 0, 360, 780);
              //画出两个圆和连接桥
              this.drawAround(this.startPoint.x, this.startPoint.y, this.startAounRadius);
              this.drawAround(this.endPoint.x, this.endPoint.y, this.endAoundRadius);
              this.drawPath();
            }
          })

      }
      .width('100%')
      .backgroundColor(Color.Blue)
    }
    .height('100%')
  }
}














评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值