How to animate changing numbers by counting (showing intermediate values)


#1

I am building a darts calculator, naturally there is a scoreboard showing the score for each player. When a new dart is input the score decreases. Using bindings a change from 20 to 10 looks like this: 20 -> 10. I want this change to be animated like this: 20 -> 18 -> 16 -> 14 -> 13 -> 12 -> 11 -> 10.

I would describe it as “count pretty fast from the old value to the new one”. I also have an actual GIF from my app:

My current solution is a custom component[1] that uses Velocity.js and directly manipulates the DOM. I’m interested in ideas for different, better, solutions. One using a custom Liquid Fire transition would be very welcome. I tried to make one but failed miserably.

[1] The count-to component:

import Ember from 'ember';

export default Ember.Component.extend({
  onValueChange: function() {
    var from = this.get('displayValue');
    var to = this.get('value');

    this.set('displayValue', this.get('value'));

    if (!this.$()) {
      return;
    }

    if (this.isAnimationRunning) {
      from = parseInt(this.$().html(), 10);
      this.$().velocity('stop');
    }

    this.$().velocity({
      tween: [to, from]
    }, {
      progress: function(elements, complete, remaining, start, tweenValue) {
        this.$().html(parseInt(tweenValue, 10));
      }.bind(this),
      complete: function() {
        this.isAnimationRunning = false;
      }.bind(this),
      begin: function() {
        this.isAnimationRunning = true;
      }.bind(this),
      duration: 800,
      easing: 'ease-out'
    });
  }.observes('value').on('init'),

  onDidInsertElement: function() {
    this.$().html(this.get('displayValue'));
  }.on('didInsertElement'),

  onWillDestroyElement: function() {
    this.$().velocity('stop');
  }.on('willDestroyElement')
});

#2

Mabye http://emberjs.com/guides/cookbook/working_with_objects/continuous_redrawing_of_views/ will help?


#3

Unfortunately I don’t think this is a viable solution as a time base for animations.


#4

Why don’t you use a “shadow” display number? Something along the lines of:

var value: null; //destination value

updateDisplayed: function (){
	if(this.get('value')!=this.get('displayValue')){
		if(this.get('value')>this.get('displayValue')){
			this.set('displayValue', this.get('displayValue')+1);
		} else {
			this.set('displayValue', this.get('displayValue')-1);
		}
		Ember.run.later('updateDisplayed')
	}
}.observes('value')

And in your component templete simply use: {{displayedValue}}


#5

This might be entirely possible with streams. In any case, I couldn’t figure complete stream solution out. But this solutions subscribes to the stream and tweens the value without directly accessing the DOM.

In any case, generally when dealing with animations - I actually think direct DOM manipulation is the best for performance. But since the animations here don’t involve hundreds of “steps”, the perf cost is negligible for this example.

Edit: here is the same solution as a helper and a stream http://jsbin.com/qiseyedefe/1/edit?html,js,output

Edit 2: you could take it a step further and use them as subexpressions to animate declaratively http://jsbin.com/tohukumeta/1/edit?html,js,output


Declarative syntax for animation