DEV Community

HarmonyOS
HarmonyOS

Posted on

Progress sliding rating dynamic effect implementation

Read the original article:Progress sliding rating dynamic effect implementation

Problem Description

How to customize the pattern to achieve the dynamic effect of sliding rating?

Background Knowledge

  • ContentModifier supports customizing the content area of a specific component through the style builder.
  • The Progress bar component is used to display the progress of content loading or operation processing.
  • PanGesture sliding gesture event, which is triggered when the minimum sliding distance reaches the set minimum value.

Solution

1.Customize the scoring style.

   class MyProgressModifier implements ContentModifier<ProgressConfiguration> {
    // todo: Detailed attribute definition

     applyContent(): WrappedBuilder<[ProgressConfiguration]> {
       return wrapBuilder(myProgress);
     }
   }
Enter fullscreen mode Exit fullscreen mode

2.Draw the left and right scoring paths respectively as the filling standard for the 0.5 score.

   // todo:Path component for drawing pentagram tracks
   @Builder 
   function leftStar(config: ProgressConfiguration, value: number) {

   }

   @Builder
   function rightStar(config: ProgressConfiguration, value: number) {

   }
Enter fullscreen mode Exit fullscreen mode

3.Handle related attribute changes when the gesture slides and render the page.

   Progress({ value: this.currentValue, total: 10 })
     .contentModifier(this.modifier)
     .gesture(
     PanGesture()
       // todo:Progress processing when gesture sliding
       .onActionStart((event: GestureEvent) => {
       })
       .onActionUpdate((event: GestureEvent) => {
       })
     )
     .margin(30)
Enter fullscreen mode Exit fullscreen mode

The effect diagram is as follows:

cke_5729.gif

Complete code:

class MyProgressModifier implements ContentModifier<ProgressConfiguration> {
  color: Color = Color.White;
  outerRadius: number = 300;
  innerRadius: number;
  idList: Array<string> = ['1', '3', '5', '7', '9'];

  constructor(color: Color, outerRadius: number) {
    this.color = color;
    this.outerRadius = outerRadius;
    this.innerRadius = outerRadius * sin(18) / cos(36);
  }

  paintingPath(startX: number, startY: number, isHalf: boolean = false, isLeft: boolean = true) {
    let point1: string = `${startX} ${startY}`;
    let point3: string = `${startX - this.outerRadius * cos(18)} ${startY - (sin(18) - 1) * this.outerRadius}`;
    let point5: string = `${startX - this.outerRadius * cos(54)} ${startY - (-sin(54) - 1) * this.outerRadius}`;
    let point7: string = `${startX + this.outerRadius * cos(54)} ${startY - (-sin(54) - 1) * this.outerRadius}`;
    let point9: string = `${startX + this.outerRadius * cos(18)} ${startY - (sin(18) - 1) * this.outerRadius}`;

    let point2: string =
      `${startX - this.innerRadius * cos(54)} ${startY - this.innerRadius * sin(54) + this.outerRadius}`;
    let point4: string =
      `${startX - this.innerRadius * cos(18)} ${startY + this.innerRadius * sin(18) + this.outerRadius}`;
    let point6: string = `${startX} ${startY + this.innerRadius + this.outerRadius}`;
    let point8: string =
      `${startX + this.innerRadius * cos(18)} ${startY + this.innerRadius * sin(18) + this.outerRadius}`;
    let point10: string =
      `${startX + this.innerRadius * cos(54)} ${startY - this.innerRadius * sin(54) + this.outerRadius}`;
    if (!isHalf) {
      return `M${point1} L${point2} L${point3} L${point4} L${point5} L${point6} L${point7} L${point8} L${point9} L${point10} L${point1} Z`
    }
    if (isLeft) {
      return `M${point1} L${point2} L${point3} L${point4} L${point5} L${point6} `
    }
    return `M${point6} L${point7} L${point8} L${point9} L${point10} L${point1}  `
  }

  applyContent(): WrappedBuilder<[ProgressConfiguration]> {
    return wrapBuilder(myProgress);
  }
}

@Builder
function leftStar(config: ProgressConfiguration, value: number) {
  Path()
    .width(20)
    .height(20)
    .commands((config.contentModifier as MyProgressModifier).paintingPath(35, 0, true, true))
    .fill(config.enabled && config.value >= value ? (config.contentModifier as MyProgressModifier).color :
    Color.White)
    .stroke(Color.Black)
    .strokeLineCap(LineCapStyle.Round)
    .strokeLineJoin(LineJoinStyle.Round)
    .strokeWidth(2)
}

@Builder
function rightStar(config: ProgressConfiguration, value: number) {
  Path()
    .width(20)
    .height(20)
    .commands((config.contentModifier as MyProgressModifier).paintingPath(0, 0, true, false))
    .fill(config.enabled && config.value >= value ? (config.contentModifier as MyProgressModifier).color :
    Color.White)
    .stroke(Color.Black)
    .strokeLineCap(LineCapStyle.Round)
    .strokeLineJoin(LineJoinStyle.Round)
    .strokeWidth(2)
}

@Builder
function myProgress(config: ProgressConfiguration) {
  Column({ space: 30 }) {
    Row() {
      Flex({ justifyContent: FlexAlign.Start }) {
        ForEach((config.contentModifier as MyProgressModifier).idList, (item: string) => {
          leftStar(config, Number(item))
          rightStar(config, Number(item) + 1)
        }, (item: string) => item)
      }
      .width('100%')
      .height(20)
    }
  }.margin({ bottom: 100 })


  Text('Current rating:').fontSize(20)
  Text(`${config.value / 2}`).fontSize(20)
}

@Entry
@Component
struct Index4 {
  @State currentValue: number = 0;
  modifier = new MyProgressModifier(Color.Yellow, 40);
  @State myModifier: (MyProgressModifier | undefined) = this.modifier;
  progressX: number = 0;
  context: UIContext = this.getUIContext()

  build() {
    Column() {
      Progress({ value: this.currentValue, total: 10 })
        .contentModifier(this.modifier)
        .gesture(
          PanGesture()
            .onActionStart((event: GestureEvent) => {
              this.progressX = event.fingerList[0].localX;
              this.currentValue = this.context.vp2px(this.progressX) / 100
            })
            .onActionUpdate((event: GestureEvent) => {
              this.progressX = event.fingerList[0].localX;
              this.currentValue = this.context.vp2px(this.progressX) / 100
            })
        )
        .margin(30)
    }.width('100%').height('100%')
  }
}

function cos(d: number) {
  return Math.cos(d * 3.14 / 180)
}

function sin(d: number) {
  return Math.sin(d * 3.14 / 180)
}
Enter fullscreen mode Exit fullscreen mode

Written by Mucahid Kincir

Top comments (0)