big ui refractor

This commit is contained in:
Rene Kievits
2025-12-23 02:23:44 +01:00
parent 4428a2b7be
commit eaed23a678
62 changed files with 2662 additions and 815 deletions

View File

@@ -38,6 +38,7 @@ export class KanjiController {
this.isAnimating = false;
this.ctx = { bg: null, hint: null, draw: null };
this.hintAnimationFrame = null;
}
static createPathElement(d) {
@@ -120,7 +121,16 @@ export class KanjiController {
}
}
setAutoHint(val) {
this.autoHint = val;
if (!val) {
this.clearCanvas(this.ctx.hint);
}
}
async loadChar(char, autoHint = false) {
this.autoHint = autoHint;
this.paths = [];
this.reset();
const hex = char.charCodeAt(0).toString(16).padStart(5, '0');
@@ -139,6 +149,11 @@ export class KanjiController {
}
reset() {
if (this.hintAnimationFrame) {
cancelAnimationFrame(this.hintAnimationFrame);
this.hintAnimationFrame = null;
}
this.currentStrokeIdx = 0;
this.mistakes = 0;
this.isAnimating = false;
@@ -148,6 +163,10 @@ export class KanjiController {
this.clearCanvas(this.ctx.hint);
this.drawGrid();
this.resetDrawStyle();
if (this.autoHint && this.paths.length > 0) {
this.showHint();
}
}
startStroke(point) {
@@ -191,6 +210,11 @@ export class KanjiController {
}
showHint() {
if (this.hintAnimationFrame) {
cancelAnimationFrame(this.hintAnimationFrame);
this.hintAnimationFrame = null;
}
this.clearCanvas(this.ctx.hint);
if (this.currentStrokeIdx >= this.paths.length) return;
@@ -199,20 +223,49 @@ export class KanjiController {
const len = pathEl.getTotalLength();
const ctx = this.ctx.hint;
ctx.beginPath();
ctx.strokeStyle = KANJI_CONSTANTS.COLORS.HINT;
ctx.setLineDash(KANJI_CONSTANTS.DASH_ARRAY_HINT);
const step = 5;
const count = Math.floor(len / step) + 1;
const startTime = performance.now();
const DURATION = 600;
Array.from({ length: count }).forEach((_, i) => {
const dist = Math.min(i * step, len);
const pt = pathEl.getPointAtLength(dist);
if (i === 0) ctx.moveTo(pt.x * this.scale, pt.y * this.scale);
else ctx.lineTo(pt.x * this.scale, pt.y * this.scale);
});
ctx.stroke();
const animate = (now) => {
const elapsed = now - startTime;
const progress = Math.min(elapsed / DURATION, 1);
this.clearCanvas(ctx);
ctx.beginPath();
const drawLen = len * progress;
const step = 5;
let first = true;
for (let dist = 0; dist <= drawLen; dist += step) {
const pt = pathEl.getPointAtLength(dist);
if (first) {
ctx.moveTo(pt.x * this.scale, pt.y * this.scale);
first = false;
} else {
ctx.lineTo(pt.x * this.scale, pt.y * this.scale);
}
}
if (drawLen > 0) {
const pt = pathEl.getPointAtLength(drawLen);
if (first) ctx.moveTo(pt.x * this.scale, pt.y * this.scale);
else ctx.lineTo(pt.x * this.scale, pt.y * this.scale);
}
ctx.stroke();
if (progress < 1) {
this.hintAnimationFrame = requestAnimationFrame(animate);
} else {
this.hintAnimationFrame = null;
}
};
this.hintAnimationFrame = requestAnimationFrame(animate);
}
redrawAllPerfectStrokes(includeCurrent = false) {
@@ -257,13 +310,22 @@ export class KanjiController {
}));
if (this.checkMatch(userNormalized, targetD)) {
if (this.hintAnimationFrame) {
cancelAnimationFrame(this.hintAnimationFrame);
this.hintAnimationFrame = null;
}
this.animateMorph(this.userPath, targetD, () => {
this.currentStrokeIdx += 1;
this.mistakes = 0;
this.redrawAllPerfectStrokes();
this.clearCanvas(this.ctx.hint);
if (this.currentStrokeIdx >= this.paths.length) {
this.onComplete();
} else if (this.autoHint) {
this.showHint();
}
});
} else {