DEV Community

HarmonyOS
HarmonyOS

Posted on

Rotating the verification code effect

Read the original article:Rotating the verification code effect

Requirement Description

The goal is to implement a rotating verification code effect, where the user rotates an image component to a specific orientation for successful verification. (Demonstration figures are implied but not provided).

Background Knowledge

  • rotate Component Universal Attribute: Used to set the rotation of a component. It takes a RotationOptions class object. Attributes include:
    • centerX: The X-coordinate of the transformation center point (in vp).
    • centerY: The Y-coordinate of the transformation center point (in vp).
    • angle: The rotation angle (can be a number or a string type, e.g., '90deg').
  • onTouch Component Universal Event: This event is triggered when a finger presses down, slides, or lifts up on the component.

Implementation Steps

  1. Add onTouch Event to the Circle Component:
    • When the TouchType is Down, record the initial touch position (startPosition).
  2. Calculate Rotation on Move:
    • When the TouchType is Move, continuously get the updated touch position (updatePosition).
    • Calculate the image's rotation angle by using the calculateAngleBetweenPoints method, which determines the angle change between the starting touch point and the current touch point, relative to the circle's center.
  3. Apply Rotation:
    • Define a state variable (rotateAngle) to store the cumulative rotation value obtained from the calculation.
    • Assign this rotateAngle to the rotate attribute of the Image component.
  4. Verification Check:
    • Implement a check to prompt successful verification (e.g., using promptAction.openToast) when the calculated rotateAngle falls within a predefined range (e.g., between -2 and 2 degrees).

Code Snippet / Configuration

import display from '@ohos.display';
import { promptAction } from '@kit.ArkUI';

// Define Position type for coordinates
interface Position {
  x?: number | string;
  y?: number | string;
}

@Entry
@Component
struct RotatingCodePage {
  @State startRotates: number = 0;
  @State updateRotates: number = 0;
  @State rotateAngle: number = -90;
  @State startPosition: Position = {};
  @State updatePosition: Position = {};
  // Image center point coordinates
  @State circleCenterPoint: Position = {};
  private screenH: number = 0;
  private screenW: number = 0;

  aboutToAppear(): void {
    let displayClass: display.Display | null = null;
    displayClass = display.getDefaultDisplaySync();
    this.screenH = this.getUIContext().px2vp(displayClass.height);
    this.screenW = this.getUIContext().px2vp(displayClass.width);
    this.circleCenterPoint = { x: this.screenW / 2, y: this.screenH / 2 };
  }

  build() {
    Column({ space: 20 }) {
      Stack() {
        // Users can customize the image here
        Image($r('app.media.hand'))
          .width(100)
          .height(100)
          .objectFit(ImageFit.Contain)
          .rotate({
            // Set the rotation center point to the center of the component
            centerX: '50%',
            centerY: '50%',
            angle: this.rotateAngle
          });

        Circle()
          .width(220)
          .height(220)
          .fillOpacity(0)
          .strokeWidth(2)
          .stroke(Color.Red)
          .onTouch((event: TouchEvent) => {
            if (event.type === TouchType.Down) {
              this.updateRotates = 0;
              this.startRotates = this.rotateAngle;
              this.startPosition = { x: event.touches[0].windowX, y: event.touches[0].windowY };
            } else if (event.type === TouchType.Move) {
              this.updatePosition = { x: event.touches[0].windowX, y: event.touches[0].windowY };
              // Angle when the finger moves
              this.updateRotates =
                calculateAngleBetweenPoints(this.circleCenterPoint, this.startPosition, this.updatePosition);
            }
            this.rotateAngle = this.updateRotates + this.startRotates;

            if (this.rotateAngle > -2 && this.rotateAngle < 2) {
              promptAction.openToast({
                message: 'Verification successful' // Translated text
              });
            }
          });
      }
      .hitTestBehavior(HitTestMode.Transparent);
    }
    .alignItems(HorizontalAlign.Center)
    .justifyContent(FlexAlign.Center)
    .width('100%')
    .height('100%');
  }
}

function atanToDegrees(atanResult: number) {
  const degrees = atanResult * (180 / Math.PI);
  return degrees;
}

function calculateAngleBetweenPoints(p0: Position = { x: 0, y: 0 }, p1: Position = { x: 0, y: 0 },
  p2: Position = { x: 0, y: 0 }): number {
  const startY = Number(p1.y) - Number(p0.y);
  const startX = Number(p1.x) - Number(p0.x);
  // The radian value obtained by the arctangent function
  const startRadians: number = Math.atan2(startY, startX);
  // Relative to the positive x-axis, the angle range is 0 to 360 degrees
  const startDeg = atanToDegrees(startRadians);

  const endY = Number(p2.y) - Number(p0.y);
  const endX = Number(p2.x) - Number(p0.x);
  const endRadians: number = Math.atan2(endY, endX);
  const endDeg = atanToDegrees(endRadians);

  return endDeg - startDeg;
}
Enter fullscreen mode Exit fullscreen mode

Test Results

bb.gif

Limitations or Considerations

This implementation relies on calculating angle changes based on screen coordinates and requires the center point (circleCenterPoint) to be accurately determined (e.g., based on screen dimensions). The initial rotation (rotateAngle = -90) must be adjusted to match the initial display state of the image.

Written by Muhammet Ali Ilgaz

Top comments (0)