Skip to content

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.

3rd2nd1st

Draw a single horizontal stroke across the full width.

Full stroke = Yang (━)

Draw two separate strokes, one on each side.

Two strokes = Yin (─ ─)

Lines are drawn from bottom to top, following traditional I Ching order:

3Top line (drawn last)
2Middle line
1Bottom line (drawn first)

The canvas validates strokes based on:

CheckRequirementInvalid Response
LengthMinimum 10% of line width (~28px)Stroke fades out
StraightnessMax 20% vertical spreadStroke fades out
Direction changesMax 3 direction changesStroke fades out
Already drawnLine slot must be emptyWarning haptic

Invalid strokes fade out with a 500ms animation and trigger a warning haptic.


The canvas exposes methods via ref for external control:

const canvasRef = useRef<TrigramDrawingCanvasRef>(null);
// Clear all strokes
canvasRef.current?.clear();
// Undo last line
canvasRef.current?.undo();
// Check if undo is available
canvasRef.current?.canUndo();
// Validate and get binary result
const binary = canvasRef.current?.check(); // e.g., '111' or '010'
// Check if validation is possible
canvasRef.current?.canCheck();

PropTypeDefaultDescription
onComplete(binary: string) => void-Called when 3 lines are drawn
expectedBinarystring-For validation mode
onClear() => void-Called when canvas is cleared
onLineComplete(count: number) => void-Called after each line
onInvalidStroke() => void-Called on invalid stroke
MethodReturnDescription
clear()voidClear all strokes and reset
undo()voidUndo last completed line
canUndo()booleanCheck if undo is available
check()string | nullGet binary if 3 lines complete
canCheck()booleanCheck if all 3 lines are drawn

PropertyValue
Canvas Width280px
Canvas Height376px
Line Height80px
Line Spacing32px
Line Width240px (with 20px padding)
Broken Gap40px
Stroke Width4px (drawing), 6px (completed)
Guide Lines1px dashed, gold muted

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>
);
}