This is a good use-case for a Model-View-Controller (MVC) architecture as often used in software engineering. Keep your model (board contents) and view (how does it look) as separate parts of your program.
Motivating example
Say you have a board that looks like this:

You then delete the red pieces and want to animate the replacement pieces falling from above, down into their places:

Now your board is like this:

The MVC pattern suggests the model should only store board states (
) and not care how they are shown.
That's the view's job. Based on the model and the current point in time, it creates images for a user to see and handles everything else about animations. It does this without changing the model at all, although it might store additional data.
Implementation hints
In your model, you might store a type
and unique id
for each piece on a grid. Here's a 3x3 grid:
model = [
[ { type : "blue", id : 1 }, { type : "red", id : 2 }, { type : "blue", id : 3 }, ]
[ { type : "red", id : 4 }, { type : "red", id : 5 }, { type : "blue", id : 6 }, ]
[ { type : "green", id : 7 }, { type : "blue", id : 8 }, { type : "green", id : 9 }, ]
]
The notation is pseudocode; {...}
are objects containing key : value
pairs and [...]
are lists.

It's the same initial board state colours; we've just added numerical id
s. To my understanding, you've got this bit working, though maybe without IDs.
The reason for the IDs becomes clear if we consider what happens when the model changes.
Here's a new board state:
model = [
[ { type : "red", id : 10 }, { type : "green", id : 11 }, { type : "blue", id : 3 }, ]
[ { type : "blue", id : 1 }, { type : "green", id : 12 }, { type : "blue", id : 6 }, ]
[ { type : "green", id : 7 }, { type : "blue", id : 8 }, { type : "green", id : 9 }, ]
]
This transition happened:

Now, a "dumb" view could ignore that anything happened at all and just continue happily drawing the new board state, without any animation.
However, a "clever" view which has been paying attention by storing a copy of the state, has enough information here to animate everything:

- The pieces with
id
s 3
, 6
, 7
, 8
and 9
have stayed where they were. There's no need to animate them.
- The piece
1
is there, but it's in a different position! We could animate it moving from its old position to the new one.
- The pieces
2
, 4
and 5
aren't there anymore. They could be animated by "shrinking out" or just disappearing.
- The pieces
10
, 11
and 12
are new. We can animate them falling in from off-screen above.
The "clever view" would then track which animations are happening and how far into each animation it is. Every tick, the view would advance all animations until they've all finished. (The details of this are all about interpolation. There are various interpolations you could use, but that's a different question's territory!)
I hope this is helpful. Ask about unclear parts!