Is numerical animation behavior with observable knockout possible?

For example, you have a simple model:

var ViewModel = function() {
    var timer,
        self = this;
        
    this.value = ko.observable(10);
    this.value2 = ko.observable(10);

    this.value.subscribe(function(newValue){
        clearInterval(timer)
        timer = setInterval(function(){
           if (self.value2() == newValue) {
              clearInterval(timer);
              return;
           }
           
           self.value2(self.value2() + 1);
        }, 100);
    });    

    
    this.update = function(){
        this.value(this.value() + 10);
    };
};
 
ko.applyBindings(new ViewModel());
body { font-family: arial; font-size: 14px; }
.liveExample { padding: 1em; background-color: #EEEEDD; border: 1px solid #CCC; max-width: 655px; }
.liveExample input { font-family: Arial; }
.liveExample b { font-weight: bold; }
.liveExample p { margin-top: 0.9em; margin-bottom: 0.9em; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<div class='liveExample'>   
    <p>Value: <input data-bind='value: value' /></p> 
    <p>Animated value: <input data-bind='value: value2' /></p> 
    <button data-bind="click: update">
    +10
    </button>
</div>
Run codeHide result

Each time you press the button value, it grows and value2will be animated using numerical animation (for example, in Qt QML). In this example, this is done to make a new variable and its own timer.

Question

Is there any way to knock out this behavior using custom binding or a custom extender?

+4
source share
1 answer

I sketched the user binding "animatedValue", hope this helps:

function createValueAccessor(val) {
  var displayValue = ko.observable(val());
  var timer;
  val.subscribe(function(newValue) {
    clearInterval(timer)
    timer = setInterval(function(){
      if(displayValue() == newValue) {
        clearInterval(timer);
      } else {
        displayValue(displayValue() + 1);
      }
    }, 100);
  });    
  return function() {
    return displayValue;
  }
}

// Binding handler

ko.bindingHandlers.animatedValue = {
    init: function (element, valueAccessor, allBindingsAccessor, viewModel) {
        var value = valueAccessor();
        ko.bindingHandlers.value.init(element, createValueAccessor(value), allBindingsAccessor, viewModel);
    }
};

var ViewModel = function() {
    var timer,
        self = this;
        
    this.value = ko.observable(10);
    this.value2 = ko.observable(10);

    this.value.subscribe(function(newValue){
        clearInterval(timer)
        timer = setInterval(function(){
           if (self.value2() == newValue) {
              clearInterval(timer);
              return;
           }
           
           self.value2(self.value2() + 1);
        }, 100);
    });    

    
    this.update = function(){
        this.value(this.value() + 10);
    };
};
 
function createValueAccessor(val) {
  var displayValue = ko.observable(val());
  var timer;
  val.subscribe(function(newValue) {
    clearInterval(timer)
    timer = setInterval(function(){
      if(displayValue() == newValue) {
        clearInterval(timer);
      } else {
        displayValue(displayValue() + 1);
      }
    }, 100);
  });    
  return function() {
    return displayValue;
  }
}

// Binding handler

ko.bindingHandlers.animatedValue = {
    init: function (element, valueAccessor, allBindingsAccessor, viewModel) {
        var value = valueAccessor();
        ko.bindingHandlers.value.init(element, createValueAccessor(value), allBindingsAccessor, viewModel);
    }
};

ko.bindingHandlers.animatedText = {
    init: function (element, valueAccessor, allBindingsAccessor, viewModel) {
        var value = valueAccessor();
        var displayValue = ko.observable(value());
        var timer;
        value.subscribe(function(newValue) {
          clearInterval(timer)
            timer = setInterval(function() {
              if(displayValue() == newValue) {
                clearInterval(timer);
              } else {
                displayValue(displayValue() + 1);
                ko.bindingHandlers.text.update(element, function() { return displayValue; });
              }
            }, 100);
        });    
      ko.bindingHandlers.text.update(element, function() { return displayValue; });
    }
};


ko.applyBindings(new ViewModel());
body { font-family: arial; font-size: 14px; }
.liveExample { padding: 1em; background-color: #EEEEDD; border: 1px solid #CCC; max-width: 655px; }
.liveExample input { font-family: Arial; }
.liveExample b { font-weight: bold; }
.liveExample p { margin-top: 0.9em; margin-bottom: 0.9em; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<div class='liveExample'>   
    <p>Value: <input data-bind='value: value' /></p> 
    <p>Animated value: <input data-bind='value: value2' /></p> 
    <p>Animated value via animatedText binding: <span data-bind='animatedText: value'></span></p> 
    <p>Animated value via binding: <input data-bind='animatedValue: value' /></p>
    <button data-bind="click: update">
    +10
    </button>
</div>
Run codeHide result

Update 1

"animatedText".

ko.bindingHandlers.animatedText = {
    init: function (element, valueAccessor, allBindingsAccessor, viewModel) {
        var value = valueAccessor();
        var displayValue = ko.observable(value());
        var timer;
        value.subscribe(function(newValue) {
          clearInterval(timer)
            timer = setInterval(function() {
              if(displayValue() == newValue) {
                clearInterval(timer);
              } else {
                displayValue(displayValue() + 1);
                ko.bindingHandlers.text.update(element, function() { return displayValue; });
              }
            }, 100);
        });    
      ko.bindingHandlers.text.update(element, function() { return displayValue; });
    }
};
+2

All Articles