Animation can still perfectly be split between logic and rendering. The abstract data state of animation would be the information that is necessary for your graphics API to render the animation.
In 2D games for example,that could be a rectangle area which marks the area that displays the current part of your sprite sheet that needs to be drawn (when you have a sheet consisting of lets say 30 80x80 drawings containing the various steps of your character jumping, sitting down, moving etc.). It can also be any kind of data that you don't need for rendering, but maybe for managing the animation states themselves, like the time left until the current animation step expires or the name of the animation ("walking","standing" etc.) All of that can be represented any way you want. That's the logics part.
In the rendering part, you just do it as usual, get that rectangle from your model and use your renderer to actually do the calls to the graphics API.
In code (using C++ syntax here):
class Sprite //Model
class AnimatedSprite : public Sprite, public Updatable //arbitrary interface for classes that need to change their state on a regular basis
animation_controller.Update(); //Good OOP design ;) It will take control of changing animations in time etc. for you
That's the data. Your renderer will take that data and draw it. Since both normal Sprites and animated ones are drawn the same way, you can use polymorphy here!
void Draw(const Sprite &spr)
I came up with another example. Say
you have an RPG. Your model that
represents the world map, for example,
would probably need to store the
character's position in the world as
tile coordinates on the map. However,
when you move the character, they walk
a few pixels at a time to the next
square. Do you store this "between
tiles" position in an animation
object? How do you update the model
when the character has finally
"arrived" at the next tile coordinate
on the map?
The world map doesn't know about the players position directly (it doesn't have a Vector2f or something like that which directly stores the players position=, instead it has a direct reference to the player object itself, which in turn derives from AnimatedSprite so you can pass it to the renderer easily, and gets all necessary data from it.
In general though, your tilemap shouldn't be able to do just everything - I'd have a class "TileMap" which takes care of managing all the tiles, and maybe it also does collision detection between objects that I hand over to it and the tiles on the map. Then, I'd have another "RPGMap" class, or however you'd like to call it, which has both a reference to your tilemap and the reference to the player and makes the actual Update() calls to your player and to your tilemap.
How you want to update the model when the player moves depends on what you want to do.
Is your player allowed to move in between tiles independently (Zelda style)? Simply handle the input and move the player accordingly every frame. Or do you want the player to press "right" and your character automatically moves one tile to the right? Let your RPGMap class interpolate the players position until he arrives at his destination and meanwhile lock all the movement-key input handling.
Either way, if you want to make it easier on yourself, all of your models will have Update() methods if they actually need some logic to update themselves (instead of just changing values of variables) - You don't give away the controller in the MVC pattern that way, you just move the code from "one step above" (the controller) down to the model, and all the controller does is call this Update() method of the model (The controller in our case would be the RPGMap). You can still easily swap out the logics code - you can just directly change the code of the class or if you need completely different behaviour, you can just derive from your model class and only override the Update() method.
That approach reduces method calls and things like that a lot - which used to be one of the main disadvantages of the pure MVC pattern (you end up calling GetThis() GetThat() very very often) - it makes the code both longer and a tiny bit harder to read and also slower - even though that might be taken care of by your compiler who optimizes a lot of stuff like that.