chanind / hanzi-writer

Chinese character stroke order animations and practice quizzes
https://hanziwriter.org
MIT License
3.52k stars 551 forks source link

How to quiz only for the last stroke of a character? #280

Closed rodrigomorales1 closed 1 year ago

rodrigomorales1 commented 1 year ago

The context

I'm currently learning the Wubi input method (a shape-based input method for Chinese characters). In this input method, some characters requires identifying the last stroke of a character to determine one of the key pressings. You can read more about such characters in this section of a guide written in English (search for "last stroke" for jumping to the relevant part)

Because of what I mentioned, I want the hanzi-writer quiz to only test me on the last stroke of characters.

What I've tried

I inspected the source code and noticed that _handleSuccess is called when a stroke is correctly drawn, so I thought that I could get the desired behavior by calling _handleSuccess repeatedly until the last stroke is to be drawn and change the color of the correctly drawn strokes to the same color of the outline (so that the last stroke is not the only one with a different color). See source code below.

<html>
  <head>
    <script src="https://cdn.jsdelivr.net/npm/hanzi-writer@3.2/dist/hanzi-writer.min.js"></script>
  </head>
  <body>
    <div id="character-target-div"></div>

    <script>

      async function drawAllStrokesExceptLast() {
        const character = await writer.getCharacterData();
        const numStrokes = character.strokes.length;
        for(var i=0; i<numStrokes-1; i++)
          writer._quiz._handleSuccess()
      }

      var writer = HanziWriter.create('character-target-div', '鬼', {
        width: 150,
        height: 150,
        showCharacter: false,
        showOutline: true,
        showHintAfterMisses: false,
        strokeColor: '#ddd',
        padding: 5
      });

      // We use .then() because we need to make sure that the _quiz
      // object is defined in writer so that we can call
      // _handleSuccess (method of _quiz) in drawAllStrokesExceptLast

      writer.quiz({
        onMistake: function(strokeData) {
          console.log("That's not the last stroke");
        },
        onComplete: function(summaryData) {
          console.log('You chose the last stroke correctly');
        }
      }).then(drawAllStrokesExceptLast)

    </script>
  </body>
</html>

Notes

I'd appreciate someone could provide a better alternative for getting this behavior, since my solution implies calling _quiz and _handleSuccess, these both start with an underscore and might be deleted in the future (as stated in this reply by @Chaning).

chanind commented 1 year ago

That's a good point that's hard to do with the library in its current state. Maybe we can add an option to quiz() like quiz({startStroke: 3}) or allow negative numbers to start at the end, like quiz({startStroke: -1 })

chanind commented 1 year ago

:tada: This issue has been resolved in version 3.4.0 :tada:

The release is available on:

Your semantic-release bot :package::rocket:

rodrigomorales1 commented 1 year ago

@chanind Thanks for the quick response and new release!

For the record, when using v3.4.0, the behavior I was looking for can be obtained as shown in the minimal working example below.

<head>
  <script src="https://cdn.jsdelivr.net/npm/hanzi-writer@3.4/dist/hanzi-writer.min.js"></script>
</head>
<body>
  <div id="character-target-div"></div>
  <script>
    var writer = HanziWriter.create('character-target-div', '愛', {
      width: 100,
      height: 100,
      padding: 5,
      // The "strokeColor" option needs to have the same value as
      // "outlineColor". Otherwise, the last stroke will have a different
      // color than the other strokes, so the last stroke could be easily
      // deduced just by looking at the stroke with different color.
      strokeColor: '#ddd'
    });

    writer.quiz({
      quizStartStrokeNum: -1,
      onMistake: () => {
        console.log('onMistake');
      },
      onCorrectStroke: () => {
        console.log('onCorrectStroke');
      },
      onComplete: () => {
        console.log('onComplete');
      }
    });

  </script>
</body>