Achieve smooth 60fps animation with view-side interpolation
mainIf your model updates at a lower frequency (e.g., 20fps via this.future(50)), you can achieve smooth 60fps rendering by decoupling the rendering position from the model position using interpolation in the view.
Implementation Pattern
- Store Render State: Use a
WeakMapto map model objects to their current smoothed render state{x, y, a}. This avoids manual tracking of object creation/destruction. - Interpolate: In each frame, calculate the difference between the model's position and the smoothed render position.
- Handle Jumps: If the distance between the model and the smoothed position is too large (e.g.,
Math.abs(dx) < 50is false), snap the smoothed position directly to the model position to prevent 'sliding' during resets or teleports. - Apply Smoothing: Otherwise, increment the smoothed position by a fraction of the difference (e.g.,
smoothed.x += dx * 0.3).
// In the View class
this.smoothing = new WeakMap();
smoothPos(obj) {
if (!this.smoothing.has(obj)) {
this.smoothing.set(obj, { x: obj.x, y: obj.y, a: obj.a });
}
const smoothed = this.smoothing.get(obj);
const dx = obj.x - smoothed.x;
const dy = obj.y - smoothed.y;
// if distance is large, don't smooth but jump to new position
if (Math.abs(dx) < 50) smoothed.x += dx * 0.3; else smoothed.x = obj.x;
if (Math.abs(dy) < 50) smoothed.y += dy * 0.3; else smoothed.y = obj.y;
return smoothed;
}
// In the View's update method
for (const asteroid of this.model.asteroids) {
const { x, y, a } = this.smoothPos(asteroid);
// use x, y, a for drawing...
}