TrigramDrawingCanvas
TrigramDrawingCanvas provides an interactive drawing surface where users practice drawing trigrams. It recognizes broken (yin) and unbroken (yang) lines and validates the completed trigram.
Preview
Section titled “Preview”3rd2nd1st
Drawing Lines
Section titled “Drawing Lines”Unbroken Line (Yang)
Section titled “Unbroken Line (Yang)”Draw a single horizontal stroke across the full width.
→
Full stroke = Yang (━)Broken Line (Yin)
Section titled “Broken Line (Yin)”Draw two separate strokes, one on each side.
→
Two strokes = Yin (─ ─)Drawing Order
Section titled “Drawing Order”Lines are drawn from bottom to top, following traditional I Ching order:
3Top line (drawn last)
2Middle line
1Bottom line (drawn first)
Stroke Validation
Section titled “Stroke Validation”The canvas validates strokes based on:
| Check | Requirement | Invalid Response |
|---|---|---|
| Length | Minimum 10% of line width (~28px) | Stroke fades out |
| Straightness | Max 20% vertical spread | Stroke fades out |
| Direction changes | Max 3 direction changes | Stroke fades out |
| Already drawn | Line slot must be empty | Warning haptic |
Invalid strokes fade out with a 500ms animation and trigger a warning haptic.
Imperative API
Section titled “Imperative API”The canvas exposes methods via ref for external control:
const canvasRef = useRef<TrigramDrawingCanvasRef>(null);
// Clear all strokescanvasRef.current?.clear();
// Undo last linecanvasRef.current?.undo();
// Check if undo is availablecanvasRef.current?.canUndo();
// Validate and get binary resultconst binary = canvasRef.current?.check(); // e.g., '111' or '010'
// Check if validation is possiblecanvasRef.current?.canCheck();API Reference
Section titled “API Reference”| Prop | Type | Default | Description |
|---|---|---|---|
onComplete | (binary: string) => void | - | Called when 3 lines are drawn |
expectedBinary | string | - | For validation mode |
onClear | () => void | - | Called when canvas is cleared |
onLineComplete | (count: number) => void | - | Called after each line |
onInvalidStroke | () => void | - | Called on invalid stroke |
Ref Methods
Section titled “Ref Methods”| Method | Return | Description |
|---|---|---|
clear() | void | Clear all strokes and reset |
undo() | void | Undo last completed line |
canUndo() | boolean | Check if undo is available |
check() | string | null | Get binary if 3 lines complete |
canCheck() | boolean | Check if all 3 lines are drawn |
Visual Specifications
Section titled “Visual Specifications”| Property | Value |
|---|---|
| Canvas Width | 280px |
| Canvas Height | 376px |
| Line Height | 80px |
| Line Spacing | 32px |
| Line Width | 240px (with 20px padding) |
| Broken Gap | 40px |
| Stroke Width | 4px (drawing), 6px (completed) |
| Guide Lines | 1px dashed, gold muted |
Complete Example
Section titled “Complete Example”import TrigramDrawingCanvas, { TrigramDrawingCanvasRef} from '@synchronicity/react-native';import { useRef, useState } from 'react';
function TrigramPractice({ expectedTrigram }) { const canvasRef = useRef<TrigramDrawingCanvasRef>(null); const [linesDrawn, setLinesDrawn] = useState(0); const [result, setResult] = useState<'correct' | 'incorrect' | null>(null);
const handleLineComplete = (count: number) => { setLinesDrawn(count); };
const handleCheck = () => { const binary = canvasRef.current?.check(); if (binary) { setResult(binary === expectedTrigram.binary ? 'correct' : 'incorrect'); } };
const handleClear = () => { canvasRef.current?.clear(); setLinesDrawn(0); setResult(null); };
return ( <View style={styles.container}> <Text style={styles.instruction}> Draw: {expectedTrigram.name} ({expectedTrigram.chinese}) </Text>
<TrigramDrawingCanvas ref={canvasRef} expectedBinary={expectedTrigram.binary} onLineComplete={handleLineComplete} onInvalidStroke={() => { // Optional: Show hint }} />
<View style={styles.progress}> <Text>{linesDrawn}/3 lines</Text> </View>
<View style={styles.actions}> <Button variant="ghost" onPress={() => canvasRef.current?.undo()} disabled={!canvasRef.current?.canUndo()} > Undo </Button> <Button variant="ghost" onPress={handleClear} > Clear </Button> <Button variant="primary" onPress={handleCheck} disabled={linesDrawn < 3} > Check </Button> </View>
{result && ( <Text style={result === 'correct' ? styles.correct : styles.incorrect}> {result === 'correct' ? 'Correct!' : 'Try again'} </Text> )} </View> );}