Interactive Mobile Experiences: Implementing Scratch Cards in React Native

by Bogdan Sikora, Founder & CTO

In the digital age, engagement and interactivity have become cornerstones of user experience. One unique method to elevate engagement in mobile applications is through the use of scratch card features. Scratch cards, familiar to many from the realm of lottery tickets and reward games, involve scratching off a surface to reveal hidden information or prizes beneath. In mobile apps, this concept is replicated digitally, providing a fun and interactive element for users. Examples of where digital scratch cards can be effectively utilized include:

  • Marketing Campaigns: Apps can use scratch cards as a part of promotional activities, allowing users to scratch off panels to reveal discounts, coupons, or special offers.
  • Educational Apps: To make learning more engaging, scratch cards can be used to reveal answers to quizzes or puzzles.
  • E-commerce Platforms: Scratch cards can serve as an intriguing way for customers to receive random rewards or discover special deals.
  • Loyalty Programs: Businesses can enhance their loyalty programs by offering digital scratch cards that provide a chance to win bonus points, exclusive content, or prizes.

However, when it comes to implementing such interactive scratch card features within React Native Expo projects, developers face a significant challenge. React Native Expo, renowned for its extensive library and ease of use in developing cross-platform mobile applications, surprisingly lacks actively maintained solutions for incorporating scratch card functionality. While there exist a few attempts and scattered solutions across the development community, these are often outdated, not optimized for current Expo SDK versions, or lack comprehensive documentation and support. This gap signifies a missed opportunity for developers looking to integrate such engaging features seamlessly into their apps without ejecting from Expo or resorting to complex native module integrations.

This gap in the React Native Expo ecosystem necessitates a creative and technically robust solution to bring the engaging experience of scratch cards to mobile applications. Recognizing the potential to fill this void, our solution revolves around leveraging the capabilities of a custom canvas solution tailored for the React Native environment. The challenge of not being able to use the traditional HTML <canvas> element, due to its incompatibility with native mobile platforms, leads us to an innovative alternative: React Native Skia.

Introducing React Native Skia

React Native Skia is a cutting-edge 2D graphics library that brings the power of the Skia Graphics Library to React Native applications. As the driving force behind the visuals of Chrome and Android, Skia enables developers to create smooth, high-performance animations and graphics. React Native Skia extends this capability to mobile app development, offering a rich API for crafting interactive and engaging user interfaces. It stands as a pivotal tool for overcoming the challenges of implementing dynamic features like scratch cards in React Native Expo apps, providing a seamless, cross-platform solution that enhances user experience through visually compelling interactions.

Key Components of Our Scratch Card Solution with React Native Skia

Our digital scratch card feature leverages React Native Skia's robust 2D graphics capabilities, adopting a structure reminiscent of SVG for intuitive, high-performance mobile interactions. Central to our approach are several pivotal elements:

  • Image Component: This component is crucial as it represents the scratchable surface. Users interact with this layer by "scratching" it away to unveil hidden elements underneath. The image thus serves not just as a background, but as an active layer that responds to touch interactions.
  • Masking with Rect and Path: To achieve the dynamic scratch-off effect, we employ a sophisticated masking technique involving two elements:
    • Rect: Initially covers the user elements beneath, acting as the canvas for the scratchable image. It ensures that the content meant to be revealed remains hidden until interacted with.
    • Path: Evolves in real-time based on user touch, effectively "erasing" parts of the Rect to reveal the content below. This component is key to simulating the scratch effect.

Understanding Masking

Masking is a graphic design technique used to control the visibility of graphical elements. It's similar to placing a stencil over a surface, where only exposed areas can be altered or interacted with. In the context of our scratch card:

  • The Rect component starts fully opaque, concealing the elements underneath.
  • The Path grows as the user scratches the screen, changing its shape based on the movement of the user's touch. This action gradually uncovers the elements beneath the image, creating an engaging and interactive experience.

This method allows for dynamic interaction, with the scratched path revealing underlying content in a manner that's both visually appealing and engaging for the user.

export const ScratchCard: React.FC<Props> = ({ style, children, image }) => {
  // State to hold the dimensions of the Canvas, initialized to 0.
  const [[width, height], setSize] = useState([0, 0]);
  // Ref to hold the dynamically changing path created by user interaction.
  const path = useRef(Skia.Path.Make());

  return (
    <View
      // Event handler to update the Canvas size based on its layout dimensions.
      onLayout={(e) => {
        setSize([e.nativeEvent.layout.width, e.nativeEvent.layout.height]);
      }}
      style={[styles.container, style]}>
      {Boolean(image && width && height) && (
        <>
          <View style={styles.content}>{children}</View>
          <Canvas
            style={styles.canvas}
            // Start a new path when the user begins a touch gesture.
            onTouchStart={({ nativeEvent }) => {
              path.current.moveTo(nativeEvent.locationX, nativeEvent.locationY);
            }}
            // Extend the path as the user moves their finger.
            onTouchMove={({ nativeEvent }) => {
              path.current.lineTo(nativeEvent.locationX, nativeEvent.locationY);
            }}>
            {/* Mask to create the scratch effect by revealing parts of the image based on the path */}
            <Mask
              mode="luminance"
              // Define the mask with a Rect (the entire area) and the dynamic Path for scratched areas.
              mask={
                <Group>
                  {/* Rect covering the whole canvas; initially, everything is "covered". */}
                  <Rect x={0} y={0} width={1000} height={1000} color="white" />
                  {/* Path representing the scratched area. Black color subtracts from the mask, revealing the image. */}
                  <Path
                    path={path.current}
                    color="black"
                    style="stroke"
                    strokeJoin="round"
                    strokeCap="round"
                    strokeWidth={50}
                  />
                </Group>
              }>
              {/* The image that acts as scratching surface */}
              <Image
                image={image}
                fit="cover"
                x={0}
                y={0}
                width={width}
                height={height}
              />
            </Mask>
          </Canvas>
        </>
      )}
    </View>
  );
};

Try It Out Yourself

For those eager to dive into the code and see our scratch card solution in action, we've made the complete implementation available on Expo Snack. This platform allows you to easily run and experiment with the code in a web-based simulator or on your own device, offering a hands-on experience with the solution we've developed.

Example Gif

View the full code on Expo Snack

By exploring the code, you can gain deeper insights into the implementation details and potentially adapt or extend the functionality to suit your specific app development needs. We encourage you to experiment with the component, tweak the parameters, and see firsthand how the scratch card feature can be integrated into your React Native Expo projects.

More articles

Bridging Worlds: Integrating Phaser with React for Rich Interactive Experiences

Learn to seamlessly integrate React with Phaser or any other canvas library, unlocking the potential for creating highly interactive and visually rich web applications. This guide provides instructions to bridge the gap between React's UI capabilities and the dynamic graphics of canvas-based libraries.

Read more

Zones in Action: Structuring Next.js Monorepos for Seamless Integration

This guide explores the advanced integration of autonomous Next.js applications using zones within a monorepo setup, highlighting best practices for project structure, seamless application transitions

Read more

Tell us more about your vision