DEV Community

HarmonyOS
HarmonyOS

Posted on

Creating a Lucky Spin on HarmonyOS

Read the original article:Creating a Lucky Spin on HarmonyOS

📘 Introduction

Creating interactive and animated components in HarmonyOS can significantly improve user engagement — especially in casual games or reward-based apps. In this article, I’ll walk you through how I built a simple but smooth “Lucky Spin” wheel using HarmonyOS and ArkTS.

We’ll use PieChartModel from @ohos/mpchart, basic animation with @ohos.animator, and some layout tricks to make the UI feel fun and responsive.

📊 What Is the Lucky Spin?

The Lucky Spin is a circular prize wheel divided into segments, each representing a reward like “Smartphone”, “Laptop”, or “VR Headset”. Users tap the Spin button, and the wheel rotates — eventually stopping at a random reward.

It’s often used in games, loyalty apps, or promotional events to increase excitement and give randomized feedback.

🛠️ Step 1: Import Required PieChart Components

Before building the chart, we need to import the necessary classes from the @ohos/mpchart package.

import {
  ColorTemplate,
  JArrayList,
  MPPointF,
  PieChart,
  PieChartModel,
  PieData,
  PieDataSet,
  PieEntry
} from '@ohos/mpchart';
Enter fullscreen mode Exit fullscreen mode
  • PieChartModel: Main logic and data handler for the chart
  • PieChart: The UI component that renders the wheel
  • PieEntry: Represents a single segment of the wheel
  • PieDataSet: A collection of entries with styling
  • ColorTemplate: A set of predefined color palettes
  • JArrayList: Used to build chart entry and color lists
  • MPPointF: Used to define icon/text offsets

🛠️ Step 2: Initialize the Pie Chart

I used PieChartModel to build the circular wheel. I disabled hole, legend, and touch interaction, so it acts like a static visual chart — only spinning on command.

🎨 Step 3: Set Up Prize Data and Colors

To represent each reward segment, I used PieEntry objects with equal values, and then styled the chart using colorful slices from ColorTemplate.

private async setData(range: number): Promise<void> {
  let entries: JArrayList<PieEntry> = new JArrayList<PieEntry>();

  for (let i = 0; i < this.count; i++) {
    entries.add(new PieEntry(range, this.parties[i % this.parties.length]));
  }

  let dataSet: PieDataSet = new PieDataSet(entries, "Prizes");
  dataSet.setSliceSpace(1);                        // Small gap between slices
  dataSet.setIconsOffset(new MPPointF(0, 40));     // Offset for potential icons
  dataSet.setSelectionShift(5);                    // Visual shift on selection
  dataSet.setValueTextColor(Color.White);          // Value text color
  dataSet.setDrawValues(false);                    // Hide values inside slices

  let colors: JArrayList<number> = new JArrayList();
  for (let index = 0; index < ColorTemplate.COLORFUL_COLORS.length; index++) {
    colors.add(ColorTemplate.COLORFUL_COLORS[index]);
  }
  colors.add(ColorTemplate.getHoloBlue());         // Add extra color
  dataSet.setColorsByList(colors);

  let data: PieData = new PieData(dataSet);
  this.onePartAngle = 360 / this.count;
  this.model.setRotationAngle(-90 - this.onePartAngle / 2);  // Align pointer
  this.model.setData(data);
}
Enter fullscreen mode Exit fullscreen mode

📌 range here defines the value given to each slice. Since all rewards are equal, we use the same value for all.

🌀 Step 3: Animate the Wheel Spin

To spin the wheel, I created a startGun() function that:

  • Calculates a randomized angle.
  • Adds a large base value (7200 deg) to create a satisfying long spin.
  • Adjusts the final angle to align with the top (pointer).
  • Determines which prize segment the pointer stops at.

Here’s the full function:

startGun() {
  let currentAngle = this.model.getRotationAngle();
  let randomExtraRotation = Math.random() * 360;
  let end = 7200 + currentAngle + randomExtraRotation;

  let options: AnimatorOptions = {
    duration: 8000,
    easing: "ease",
    delay: 0,
    fill: "forwards",
    direction: "normal",
    begin: currentAngle,
    end: end,
    iterations: 1
  };

  this.animatorResult = animator.create(options);
  this.animatorResult.onframe = (value) => {
    this.model.setRotationAngle(value);
    this.model.invalidate();
  };

  this.animatorResult.onfinish = () => {
    const finalRotation = end % 360;
    let adjustedAngle = finalRotation - 90;
    if (adjustedAngle < 0) adjustedAngle += 360;

    let index = Math.floor(adjustedAngle / this.onePartAngle);
    this.currentIndex = index;
  };

  this.animatorResult.play();
}
Enter fullscreen mode Exit fullscreen mode

📌 Note: The -90 correction accounts for the chart’s initial rotation offset, ensuring the pointer correctly lands on the intended segment.

✅ Conclusion

Building a Lucky Spin UI in HarmonyOS is easier than it seems — with just a combination of PieChartModel and animator, we can simulate a complete spin wheel experience. From customizing rewards to calculating exact stop angles, the entire flow feels responsive and engaging.

This component can easily be extended with sound effects, haptics, or even backend integration for real prizes.

If you’re building a gamified app or want to boost user engagement, give it a spin! 🎯

📚 References

Top comments (0)