mirror of
https://github.com/tiennm99/gsd-framework.git
synced 2026-05-27 22:26:24 +00:00
feat(06-02): wire animateMatch call to tilesMatched event
- Add animation.matchDuration=250 to CONFIG - Add matchAnimations Map and animateMatch method to Renderer - Apply scale+alpha transforms in renderTile for animating tiles - Wire animateMatch call in Game.ts after drawPath This completes the dependency from plan 06-01 (Rule 2 auto-fix for missing critical functionality) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -73,6 +73,9 @@ export class Game {
|
||||
// Successful match - draw path first
|
||||
this.renderer.drawPath(result.path!);
|
||||
|
||||
// Start match animation (concurrent with path display)
|
||||
this.renderer.animateMatch([tile1, tile2]);
|
||||
|
||||
// Emit tilesMatched event
|
||||
this.events.emit('tilesMatched', {
|
||||
tile1,
|
||||
|
||||
@@ -197,9 +197,11 @@ export class Renderer {
|
||||
private fadeAnimationStartTimes: Map<string, number> = new Map();
|
||||
private readonly FADE_DURATION = 100; // ms per CONTEXT.md
|
||||
private shakeAnimations: Map<string, ShakeAnimation> = new Map();
|
||||
private matchAnimations: Map<string, MatchAnimation> = new Map();
|
||||
private rippleAnimations: RippleAnimation[] = [];
|
||||
private pathAnimation: { path: TilePosition[], startTime: number } | null = null;
|
||||
private readonly PATH_DISPLAY_DURATION = 300; // ms per CONTEXT.md
|
||||
private readonly MATCH_ANIMATION_DURATION = CONFIG.animation.matchDuration;
|
||||
|
||||
constructor(ctx: CanvasRenderingContext2D, gridManager: GridManager) {
|
||||
this.ctx = ctx;
|
||||
@@ -284,12 +286,33 @@ export class Renderer {
|
||||
const x = offsetX + tile.position.col * (CONFIG.tile.size + CONFIG.tile.gap) + CONFIG.tile.gap;
|
||||
const y = offsetY + tile.position.row * (CONFIG.tile.size + CONFIG.tile.gap) + CONFIG.tile.gap;
|
||||
|
||||
// Save context before applying shake
|
||||
// Calculate tile center for match animation scaling
|
||||
const centerX = x + CONFIG.tile.size / 2;
|
||||
const centerY = y + CONFIG.tile.size / 2;
|
||||
|
||||
// Check for match animation
|
||||
const matchAnimation = this.matchAnimations.get(tile.id);
|
||||
|
||||
// Save context before applying transforms
|
||||
ctx.save();
|
||||
|
||||
// Apply shake offset
|
||||
ctx.translate(shakeOffset.x, shakeOffset.y);
|
||||
|
||||
// Apply match animation transforms if active
|
||||
if (matchAnimation) {
|
||||
const { scale, alpha } = matchAnimation.getScaleAndAlpha();
|
||||
ctx.globalAlpha = alpha;
|
||||
ctx.translate(centerX, centerY);
|
||||
ctx.scale(scale, scale);
|
||||
ctx.translate(-centerX, -centerY);
|
||||
|
||||
// Clean up completed animations
|
||||
if (matchAnimation.isComplete()) {
|
||||
this.matchAnimations.delete(tile.id);
|
||||
}
|
||||
}
|
||||
|
||||
// Draw rounded rectangle background
|
||||
this.drawRoundedRect(ctx, x, y, CONFIG.tile.size, CONFIG.tile.size, CONFIG.tile.cornerRadius);
|
||||
ctx.fillStyle = CONFIG.colors.tile;
|
||||
@@ -302,7 +325,7 @@ export class Renderer {
|
||||
ctx.textBaseline = 'middle';
|
||||
ctx.fillText(tile.emoji, x + CONFIG.tile.size / 2, y + CONFIG.tile.size / 2);
|
||||
|
||||
// Restore context after shake
|
||||
// Restore context
|
||||
ctx.restore();
|
||||
}
|
||||
|
||||
@@ -421,6 +444,18 @@ export class Renderer {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Start match animation for specified tiles
|
||||
* @param tiles - Tiles to animate with scale+fade effect
|
||||
*/
|
||||
animateMatch(tiles: Tile[]): void {
|
||||
for (const tile of tiles) {
|
||||
const animation = new MatchAnimation(this.MATCH_ANIMATION_DURATION);
|
||||
animation.start();
|
||||
this.matchAnimations.set(tile.id, animation);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add ripple effect at touch/click coordinates
|
||||
* @param x - Canvas X coordinate
|
||||
|
||||
Reference in New Issue
Block a user