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
-
rotateComponent Universal Attribute: Used to set the rotation of a component. It takes aRotationOptionsclass object. Attributes include:-
centerX: The X-coordinate of the transformation center point (invp). -
centerY: The Y-coordinate of the transformation center point (invp). -
angle: The rotation angle (can be anumberor astringtype, e.g.,'90deg').
-
-
onTouchComponent Universal Event: This event is triggered when a finger presses down, slides, or lifts up on the component.
Implementation Steps
-
Add
onTouchEvent to the Circle Component:- When the
TouchTypeisDown, record the initial touch position (startPosition).
- When the
-
Calculate Rotation on Move:
- When the
TouchTypeisMove, continuously get the updated touch position (updatePosition). - Calculate the image's rotation angle by using the
calculateAngleBetweenPointsmethod, which determines the angle change between the starting touch point and the current touch point, relative to the circle's center.
- When the
-
Apply Rotation:
- Define a state variable (
rotateAngle) to store the cumulative rotation value obtained from the calculation. - Assign this
rotateAngleto therotateattribute of theImagecomponent.
- Define a state variable (
-
Verification Check:
- Implement a check to prompt successful verification (e.g., using
promptAction.openToast) when the calculatedrotateAnglefalls within a predefined range (e.g., between -2 and 2 degrees).
- Implement a check to prompt successful verification (e.g., using
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;
}
Test Results
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.

Top comments (0)