Skip to content

0x02b-鸿蒙开发笔记8-如何动态绘制一个气泡

如何绘制图形

根据鸿蒙官方的文档,可以使用Path等绘制图形。 例如:

js
Circle({ width: 150, height: 150 })
Circle({ width: 150, height: 150 })

但是绘制图形里面需明确知道绘制图形的像素点的位置,因此需要动态获取组件的尺寸。

如何获取计算组件的尺寸

使用.onAreaChange来获取。

绘制气泡图形

js
@Component
@Preview
export struct BubbleView {
  // 圆角弧度,默认24。单位像素
  @State roundRadiusPx: number = 16
  @State sizeValue: Area = {
    width: 0,
    height: 0,
    position: {},
    globalPosition: {}
  }
  // 支持外部传入组件
  @BuilderParam innerView: () => void = this.titleWidget;

  // 外部没有传入的时候,默认展示这个
  @Builder
  titleWidget() {
    Row() {
      Text("")
    }
  }
  
  build() {
    Stack({ alignContent: Alignment.TopStart }) {
      Path()
        .commands(`M${this.roundRadiusPx * 2} 0
          L${vp2px(this.sizeValue.width as number) - this.roundRadiusPx} 0
          Q${vp2px(this.sizeValue.width as number)} 0 ${vp2px(this.sizeValue.width as number)} ${this.roundRadiusPx}
          L${vp2px(this.sizeValue.width as number)} ${vp2px(this.sizeValue.height as number) - this.roundRadiusPx}
          Q${vp2px(this.sizeValue.width as number)} ${vp2px(this.sizeValue.height as number)} ${vp2px(this.sizeValue.width as number) -
        this.roundRadiusPx} ${vp2px(this.sizeValue.height as number)}
          L${this.roundRadiusPx * 2} ${vp2px(this.sizeValue.height as number)}
          Q${this.roundRadiusPx} ${vp2px(this.sizeValue.height as number)} ${this.roundRadiusPx} ${vp2px(this.sizeValue.height as number) -
          this.roundRadiusPx * 2}
          L${this.roundRadiusPx} ${this.roundRadiusPx * 4}
          L0 ${this.roundRadiusPx * 3}
          L${this.roundRadiusPx} ${this.roundRadiusPx * 2}
          Q${this.roundRadiusPx} 0 ${this.roundRadiusPx * 2} 0
          Z
          `)
        .stroke(Color.Transparent)
        .fill(Color.White)
        .fillOpacity(0.86)
        .shadow({
          type: ShadowType.COLOR,
          fill: true,
          color: 'rgba(0, 0, 0, 0.1)',
          radius: 44,
          offsetX: 16,
          offsetY: 12
        })
        .width(this.sizeValue.width)
        .height(this.sizeValue.height)

      Row() {
        this.innerView()
      }
      .padding({
        left: 16,
        right: 8,
        top: 6,
        bottom: 6
      })
      .onAreaChange((_: Area, newValue: Area) => {
        this.sizeValue = {
          width: Math.floor(Math.max(newValue.width as number, this.roundRadiusPx * 2)),
          height: Math.floor(Math.max(newValue.height as number, this.roundRadiusPx * 2)),
          position: newValue.position,
          globalPosition: newValue.globalPosition
        }
      })
    }
  }
}
@Component
@Preview
export struct BubbleView {
  // 圆角弧度,默认24。单位像素
  @State roundRadiusPx: number = 16
  @State sizeValue: Area = {
    width: 0,
    height: 0,
    position: {},
    globalPosition: {}
  }
  // 支持外部传入组件
  @BuilderParam innerView: () => void = this.titleWidget;

  // 外部没有传入的时候,默认展示这个
  @Builder
  titleWidget() {
    Row() {
      Text("")
    }
  }
  
  build() {
    Stack({ alignContent: Alignment.TopStart }) {
      Path()
        .commands(`M${this.roundRadiusPx * 2} 0
          L${vp2px(this.sizeValue.width as number) - this.roundRadiusPx} 0
          Q${vp2px(this.sizeValue.width as number)} 0 ${vp2px(this.sizeValue.width as number)} ${this.roundRadiusPx}
          L${vp2px(this.sizeValue.width as number)} ${vp2px(this.sizeValue.height as number) - this.roundRadiusPx}
          Q${vp2px(this.sizeValue.width as number)} ${vp2px(this.sizeValue.height as number)} ${vp2px(this.sizeValue.width as number) -
        this.roundRadiusPx} ${vp2px(this.sizeValue.height as number)}
          L${this.roundRadiusPx * 2} ${vp2px(this.sizeValue.height as number)}
          Q${this.roundRadiusPx} ${vp2px(this.sizeValue.height as number)} ${this.roundRadiusPx} ${vp2px(this.sizeValue.height as number) -
          this.roundRadiusPx * 2}
          L${this.roundRadiusPx} ${this.roundRadiusPx * 4}
          L0 ${this.roundRadiusPx * 3}
          L${this.roundRadiusPx} ${this.roundRadiusPx * 2}
          Q${this.roundRadiusPx} 0 ${this.roundRadiusPx * 2} 0
          Z
          `)
        .stroke(Color.Transparent)
        .fill(Color.White)
        .fillOpacity(0.86)
        .shadow({
          type: ShadowType.COLOR,
          fill: true,
          color: 'rgba(0, 0, 0, 0.1)',
          radius: 44,
          offsetX: 16,
          offsetY: 12
        })
        .width(this.sizeValue.width)
        .height(this.sizeValue.height)

      Row() {
        this.innerView()
      }
      .padding({
        left: 16,
        right: 8,
        top: 6,
        bottom: 6
      })
      .onAreaChange((_: Area, newValue: Area) => {
        this.sizeValue = {
          width: Math.floor(Math.max(newValue.width as number, this.roundRadiusPx * 2)),
          height: Math.floor(Math.max(newValue.height as number, this.roundRadiusPx * 2)),
          position: newValue.position,
          globalPosition: newValue.globalPosition
        }
      })
    }
  }
}

额外的,这里使用了@BuilderParam,支持外部传入组件。使用方法:

js
@Component
@Preview
struct BubbleShapView__Preview {
  build() {
    Column() {
      BubbleView() {
        Text("Good Job !")
      }
    }
    .padding(20)
    .backgroundColor(Color.Gray)
  }
}
@Component
@Preview
struct BubbleShapView__Preview {
  build() {
    Column() {
      BubbleView() {
        Text("Good Job !")
      }
    }
    .padding(20)
    .backgroundColor(Color.Gray)
  }
}

kdjsjfsd %%