4

For example you have simple model like this:

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>

Each time you press button value would be incremented and value2 would be animatied with numeric animation (like in Qt QML for example). In this sample this done throw new variable and self made timer.

Question

Is here is any way in knockout to do such behaviour with custom binding or custom extender?

Nick Bondarenko
  • 6,211
  • 4
  • 35
  • 56

1 Answers1

2

I've sketched custom "animatedValue" binding, hope it 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>

Update 1

I've added "animatedText" custom binding handler.

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; });
    }
};
TSV
  • 7,538
  • 1
  • 29
  • 37
  • Hm. This works with `value` bindingHandler but do not work with `text`. May be you know why? – Nick Bondarenko Jun 17 '16 at 10:04
  • Sure, I've overloaded "value" binding (by using "ko.bindingHandlers.value.init" and "ko.bindingHandlers.value.update") because of original sample was using it. And I've called a new binding "animatedValue". I guess you can override the "text" binding the same way. – TSV Jun 17 '16 at 11:45
  • 1
    It wasn't so easy as I thought. I've updated the answer with new "animatedText" binding. – TSV Jun 17 '16 at 12:57