[1] Introduction (full code at the end)
The Drag and Drop Tangram Game presented in this article is a simple puzzle game that allows users to complete puzzle tasks by dragging and rotating tangram pieces of different shapes. The whole game is developed using the Hongmeng Next framework, which utilizes its powerful UI building capabilities and data response mechanism to achieve a smooth user experience.
[2] Environmental Preparation
Computer system: windows 10
Development Tools: DevEco Studio NEXT Beta1 Build Version: 5.0.3.806
Engineering version: API 12
Real camera: Mate 60 Pro
Languages: ArkTS, ArkUI
[3] Key Technical Points
1. TangramBlock Class Definition The core of the game lies in the definition of the TangramBlock class, which encapsulates the properties and behaviors of each tangram block. The class contains properties such as width, height, color, initial and current offset, rotation angle, etc. It also provides methods to reset the data. This lays the foundation for subsequent data binding and UI rendering.
2. Data Binding and Responsive Updates Data observation and responsive updates can be easily implemented in Hongmeng Next using the @ObservedV2 and @Trace decorators. Whenever a property in a TangramBlock instance changes, the UI is automatically updated to reflect the latest state. This mechanism greatly simplifies data synchronization, allowing developers to focus on logic implementation without worrying about UI updates.
3. UI Building and Layout Management Hongmeng Next provides a rich set of UI components and layout tools, which makes building complex user interfaces easy. In this project, we use Column, Stack, Polygon and other components to build the layout of Tangram boards. By nesting these components, we can flexibly control the position and size of each board.
4. Gesture Handling and Interaction In order to realize the dragging and rotating functions, we use the PanGesture and rotate methods to handle the user's touch and gesture operations. When the user drags the panel, the position change of the panel can be reflected in real time by updating the initialOffsetX and initialOffsetY properties. Similarly, the rotation effect of the board can be realized by increasing or decreasing the rotationAngle property.
5. Animation and Transition Hongmeng Next has built-in rich animation and transition effects to make user interaction more natural. In this project, we use the animateTo method to smoothly update the state of the board, which enhances the user experience.
5.1 Rotation animation properties
.rotate({ angle: , })
5.2 Flip animation properties
.rotate({ x: 0, y: 1, z: 0, angle: , centerX: / 2, // Center x-coordinate centerY: / 2, // Y-coordinate of center point })
5.3 Translation Animation Properties
.translate({ x: , y: , z: 0 })
[complete code]
@ObservedV2 // Decorator that listens for data changes class TangramBlock { // Define the tangram class width: number; // Width height: number; // Height points: Array<[number, number]>; // Array of point coordinates color: string; // Color @Trace initialOffsetX: number; // Initial X offset @Trace initialOffsetY: number; // Initial Y offset @Trace currentOffsetX: number; // Current X offset @Trace currentOffsetY: number; // Current Y offset @Trace rotationAngle: number; // Rotation angle @Trace flipAngle: number = 0; // Flip angle, default 0 @Trace rotateValue: number; // Rotation value defaultInitialOffsetX: number; // Default initial X offset defaultInitialOffsetY: number; // Default initial Y offset defaultRotationAngle: number; // Default Rotation Angle constructor(color: string, width: number, height: number, initialOffsetX: number, initialOffsetY: number, rotationAngle: number, points: Array<[number, number]>) { = = = initialOffsetX; // Initialize X offset = = = initialOffsetY; // Initialize the Y offset = = = rotationAngle; // Initialize rotation angle = color; // Setting the color = width; // Set the width = height; // Set the height = points; // Set the array of point coordinates } resetData() { // Reset data methods = 0; // Reset flip angle = = ; // Reset the initial X offset = = ; // Reset the initial Y offset = = ; // Reset rotation angle } } const baseUnitLength: number = 80; // Basic unit length @Entry // Entry components @Component // Define the component export struct Index { // Main Component @State selectedBlockIndex: number = -1; // Current selected position @State blocks: TangramBlock[] = [// Tangram Array // Small right-angled isosceles triangle new TangramBlock("#fed8e5", baseUnitLength, baseUnitLength, -33.58, -58.02, 135, [[0, 0], [baseUnitLength, 0], [0, baseUnitLength]]), new TangramBlock("#0a0bef", baseUnitLength, baseUnitLength, 78.76, 54.15, 45, [[0, 0], [baseUnitLength, 0], [0, baseUnitLength]]), // Right-angled isosceles triangle new TangramBlock("#ff0d0c", baseUnitLength * (2), baseUnitLength * (2), -33.16, -1.43, -90, [[0, 0], [baseUnitLength * (2), 0], [0, baseUnitLength * (2)]]), // Large right-angled isosceles triangle new TangramBlock("#ffa60a", baseUnitLength * 2, baseUnitLength * 2, 22.46, -172, -135, [[0, 0], [baseUnitLength * 2, 0], [0, baseUnitLength * 2]]), new TangramBlock("#3da56a", baseUnitLength * 2, baseUnitLength * 2, 135.65, -59.34, -45, [[0, 0], [baseUnitLength * 2, 0], [0, baseUnitLength * 2]]), // Square new TangramBlock("#ffff0b", baseUnitLength, baseUnitLength, 23.07, -1.84, -45, [[0, 0], [baseUnitLength, 0], [baseUnitLength, baseUnitLength], [0, baseUnitLength]]), // Parallelograms new TangramBlock("#5e0b9b", baseUnitLength * 2, baseUnitLength, -61.53, -85.97, 45, [[0, 0], [baseUnitLength, 0], [baseUnitLength * 2, baseUnitLength], [baseUnitLength, baseUnitLength]]) ]; build() { // Build methodology Column({ space: 30 }) { // Create a vertical layout Stack() { // Create a stacked layout ForEach(, (block: TangramBlock, index: number) => { // Traverse the jigsaw puzzle array Stack() { // Create a stacked layout Polygon({ width: , height: })// Drawing polygons .points()// Set the polygon vertex coordinates .fill()// Fill color .draggable(false)// Press and hold for non-drag .rotate({ angle: }) // Rotation angle } .rotate({ // Rotate x: 0, y: 1, z: 0, angle: , centerX: / 2, // Center x-coordinate centerY: / 2, // Y-coordinate of center point }) .width() // Set the width .height() // Set the height .onTouch(() => { // Touch events = index; // Set the selected index }) .draggable(false) // Press and hold for non-drag .translate({ x: , y: , z: 0 }) // Panning .gesture( // Gestures PanGesture()// Drag gestures .onActionUpdate((event: GestureEvent | undefined) => { // Update events if (event) { = + ; // Update the X offset = + ; // Update the Y offset } }) .onActionEnd((event: GestureEvent | undefined) => { // End event if (event) { = ; // Update the current X offset = ; // Update the current Y offset } }) ) .zIndex( == index ? 1 : 0) // Setting the hierarchy .borderWidth(2) // Border width .borderStyle() // Border Styles .borderColor( == index ? "#80a8a8a8" : ) // border color }) }.width('100%').height('750lpx') // Set the width and height .backgroundColor("#e4f2f5") // background color // Rotation angle counter Column({ space: 5 }) { // Create a vertical layout and set the spacing Text(`angle of rotation(intervals5)`).fontColor() // Display the rotation angle text and set the font color Counter() { // Create the counter component Text(`${ != -1 ? [].rotationAngle : '-'}`)// Displays the rotation angle or placeholder for the currently selected tangram. .fontColor() // Set the font color } .width(300) // Set the counter width .onInc(() => { // Add a button click event if ( != -1) { animateTo({}, () => { [].rotationAngle += 5; // Increase the angle of rotation }) } }).onDec(() => { // Reduce button click events if ( != -1) { animateTo({}, () => { [].rotationAngle -= 5; // Reduced rotation angle }) } }); } // Rotation angle counter Column({ space: 5 }) { // Create a vertical layout and set the spacing Text(`angle of rotation(intervals45)`).fontColor() // Display the rotation angle text and set the font color Counter() { // Create the counter component Text(`${ != -1 ? [].rotationAngle : '-'}`)// Displays the rotation angle or placeholder for the currently selected tangram. .fontColor() // Set the font color } .width(300) // Set the counter width .onInc(() => { // Add a button click event if ( != -1) { animateTo({}, () => { [].rotationAngle += 45; // Increase the angle of rotation }) } }).onDec(() => { // Reduce button click events if ( != -1) { animateTo({}, () => { [].rotationAngle -= 45; // Reduced rotation angle }) } }); } // Flip button Row() { // Create a horizontal layout Button('Flip to the left').onClick(() => { // Left flip button click event animateTo({}, () => { if ( != -1) { [].flipAngle -= 180; // Reduced flip angle } }); }); Button('Flip to the right').onClick(() => { // Right flip button click event animateTo({}, () => { if ( != -1) { [].flipAngle += 180; // Increase flip angle } }); }); }.width('100%').justifyContent() // Set the width and content alignment // Reset and hide the border buttons Row() { // Create a horizontal layout Button('Reset').onClick(() => { // Reset the button click event animateTo({}, () => { for (let i = 0; i < ; i++) { [i].resetData(); // Reset jigsaw data } = -1; // Reset selected indexes }); }); Button('Hide border').onClick(() => { // Hide the border button click event = -1; // Reset selected indexes }); }.width('100%').justifyContent() // Set width and content alignment }.width('100%').height('100%') } }