@jamsch/react-native-hanzi-writer
TypeScript icon, indicating that this package has built-in type declarations

0.4.0 • Public • Published

@jamsch/react-native-hanzi-writer

npm version bundle size

Hanzi/Kanji writer and stroke order quizzer library for React Native, based on the vanilla JS hanzi-writer library.

preview

Installation

# Peer dependencies required for this library to function
npx expo install react-native-gesture-handler
npx expo install react-native-reanimated
npx expo install react-native-svg

# Install this library
npm install @jamsch/react-native-hanzi-writer

Basic Usage

[!IMPORTANT] Make sure your entire application is wrapped in a <GestureHandlerRootView> element for gestures to work in quiz mode. Also make sure that the order of the elements inside <HanziWriter.Svg> match the above example as it affects the display layering. That being said, you still can conditionally render these components.

import { Button, Text, View } from 'react-native';
import { GestureHandlerRootView } from 'react-native-gesture-handler';
import { HanziWriter, useHanziWriter } from '@jamsch/react-native-hanzi-writer';

function App() {
  const writer = useHanziWriter({
    character: '验',
    // (Optional) This is where you would load the character data from a CDN
    loader(char) {
      return fetch(
        `https://cdn.jsdelivr.net/npm/hanzi-writer-data@2.0/${char}.json`
      ).then((res) => res.json());
    },
  });

  return (
    <GestureHandlerRootView style={{ flex: 1 }}>
      <HanziWriter
        writer={writer}
        // Optional, render out your loading UI
        loading={<Text>Loading...</Text>}
        // Optional, render out an error UI in case the fetch call fails
        error={
          <View>
            <Text>Error loading character. </Text>
            <Button title="Refetch" onPress={writer.refetch} />
          </View>
        }
        style={{ alignSelf: 'center' }}
      >
        {/** Optional, grid lines to help draw the character */}
        <HanziWriter.GridLines color="#ddd" />
        <HanziWriter.Svg>
          {/** The outline is laid under the character */}
          <HanziWriter.Outline color="#ccc" />
          {/** The character is displayed on top. Animations run here. Quizzing will hide it */}
          <HanziWriter.Character color="#555" radicalColor="green" />
          {/** Quiz strokes display after every correct stroke in quiz mode */}
          <HanziWriter.QuizStrokes />
          {/** The mistake highligher will animate and fade out a stroke in quiz mode */}
          <HanziWriter.QuizMistakeHighlighter
            color="#539bf5"
            strokeDuration={400}
          />
        </HanziWriter.Svg>
      </HanziWriter>
    </GestureHandlerRootView>
  );
}

Starting the quiz

You can start the quiz by calling writer.quiz.start().

  • To listen for state changes (such as the current stroke index, or whether the quiz is active), use writer.quiz.useStore(selector)
  • To listen for specific quiz events (such as completed, correct stroke, mistakes), attach them as arguments to writer.quiz.start().
import { HanziWriter, useHanziWriter } from '@jamsch/react-native-hanzi-writer';

function App() {
  const writer = useHanziWriter({ character: '验' });

  const quizActive = writer.quiz.useStore((s) => s.active);

  const startQuiz = () => {
    writer.quiz.start({
      /** Optional. Default: 1. This can be set to make stroke grading more or less lenient. Closer to 0 the more strictly strokes are graded. */
      leniency: 1,
      /** Optional. Default: 0. */
      quizStartStrokeNum: 0,
      /** Highlights correct stroke (uses <QuizMistakeHighlighter />) after incorrect attempts. Set to `false` to disable. */
      showHintAfterMisses: 2,
      onComplete({ totalMistakes }) {
        console.log(
          `Quiz complete! You made a total of ${totalMistakes} mistakes`
        );
      },
      onCorrectStroke() {
        console.log('onCorrectStroke');
      },
      onMistake(strokeData) {
        console.log('onMistake', strokeData);
      },
    });
  };

  return (
    <GestureHandlerRootView style={{ flex: 1 }}>
      <HanziWriter writer={writer}>
        {/** Include all the HanziWriter.XXX components here */}
      </HanziWriter>
      <Button
        onPress={quizActive ? writer.quiz.stop : startQuiz}
        title={quizActive ? 'Stop Quiz' : 'Start Quiz'}
      />
    </GestureHandlerRootView>
  );
}

Animating strokes

Running stroke order animations is simple.

  • Call writer.animator.animateCharacter() with optional arguments to control stroke duration, delays between strokes.
  • Call writer.animator.cancelAnimation() to cancel the running animation.
  • Use writer.animator.useStore(selector) to listen for changes to the animation state.
import { HanziWriter, useHanziWriter } from '@jamsch/react-native-hanzi-writer';

function App() {
  const writer = useHanziWriter({ character: '验' });

  const animatorState = writer.animator.useStore((s) => s.state);

  return (
    <GestureHandlerRootView style={{ flex: 1 }}>
      <HanziWriter writer={writer}>
        {/** Include all the HanziWriter.XXX components here */}
      </HanziWriter>
      <Button
        onPress={() => {
          if (animatorState === 'playing') {
            writer.animator.cancelAnimation();
          } else {
            writer.animator.animateCharacter({
              /** Optional. Default: 1000ms */
              delayBetweenStrokes: 800,
              /** Optional. Default: 400ms */
              strokeDuration: 800,
              /** Optional. */
              onComplete() {
                console.log('Animation complete!');
              },
            });
          }
        }}
        title={
          animatorState === 'playing' ? 'Stop animating' : 'Animate Strokes'
        }
      />
    </GestureHandlerRootView>
  );
}

Contributing

See the contributing guide to learn how to contribute to the repository and the development workflow.

License

MIT

Dependents (0)

Package Sidebar

Install

npm i @jamsch/react-native-hanzi-writer

Weekly Downloads

43

Version

0.4.0

License

MIT

Unpacked Size

275 kB

Total Files

89

Last publish

Collaborators

  • jamesplay