Transitions Between Different Knockout Components
I'm trying to apply CSS transitions as I switch between knockout components but I'm not having much joy in achieving this. Essentially I want to have a div with a fixed width, but
Solution 1:
You're actually switching the component meaning the DOM is being re-constructed, so I don't see a way to animate using CSS.
What you can do is build your own binding handler which does the animating for you using Javascript:
ko.bindingHandlers.animatingComponent = {
init: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
var value = valueAccessor();
var componentName = value.name;
// create a new observable so we can delay the moment ko's component // binding builds the new componentvar actualComponentName = ko.observable(componentName());
componentName.subscribe(function(newComponent) {
$(element).hide(500, function() {
actualComponentName(newComponent);
$(element).show(500);
});
});
ko.bindingHandlers.component.init(element, function() {
return { name: actualComponentName, params: value.params};
}, allBindings, viewModel, bindingContext);
}
};
ko.components.register("big", {
viewModel: function (vm) {
this.items = vm.value.items;
},
template: '<div class="big box" data-bind="foreach: items"><p class="item" data-bind="text: name"></p></div>'
});
ko.components.register("small", {
viewModel: function (vm) {
this.items = vm.value.items;
},
template: '<div class="small box" data-bind="foreach: items"><span class="item" data-bind="text: name"></span></div>'
});
var vm = {};
vm.componentName = ko.observable("small");
vm.items = ko.observableArray([{ name: "A" }, { name: "B" }, { name: "C" }]);
ko.applyBindings(vm);
setInterval(function() {
if(vm.componentName() === "small") { vm.componentName("big"); }
else { vm.componentName("small"); }
}, 3000);
.box {
width: 200px;
}
.big {
border: thin solid black;
}
.small {
border: thin solid black;
padding: 10px10px10px10px;
}
.item {
padding-left: 10px;
}
<scriptsrc="https://code.jquery.com/jquery-1.11.3.min.js"></script><scriptsrc="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script><divdata-bind="animatingComponent: { name: componentName, params: { value: $data } }"></div>
Solution 2:
It only took most of the day, but I got the transition to work. I have a variable that remembers the last display size (which is where it will start when the component changes). When the component changes, I set visibility to hidden and sizes to default so I can get the intended size of the div. Then I size it back to the last display size and make it visible, then size it to the new size, and the transition happens.
Note that I also had to change the CSS a bit.
ko.components.register("big", {
viewModel: function (vm) {
this.items = vm.value.items;
},
template: '<div class="big box" data-bind="style:$root.boxSize, foreach: items, sizeGet:$root.boxSize"><p class="item" data-bind="text: name"></p></div>'
});
ko.components.register("small", {
viewModel: function (vm) {
this.items = vm.value.items;
},
template: '<div class="small box" data-bind="style:$root.boxSize, foreach: items, sizeGet:$root.boxSize"><span class="item" data-bind="text: name"></span></div>'
});
var unclipped;
ko.bindingHandlers.sizeGet = {
init: function (element, valueAccessor) {
var sizer = valueAccessor();
sizer({
height: '',
width: '',
visibility: 'hidden'
});
var nextUnclipped = {
height: element.scrollHeight + 'px',
width: element.scrollWidth + 'px',
visibility: 'visible'
};
if (unclipped) sizer(unclipped);
unclipped = nextUnclipped;
setTimeout(function () {
sizer(unclipped);
}, 0);
}
};
var vm = (function () {
var activeComponent = ko.observable('small'),
defaultSize = {
width: '',
height: ''
},
boxSize = ko.observable(defaultSize);
return {
componentName: activeComponent,
boxSize: boxSize,
items: ko.observableArray([{
name: "A"
}, {
name: "Big"
}, {
name: "Cat"
}, {
name: "Dropping"
}])
};
}());
ko.applyBindings(vm);
var i = setInterval(function () {
if (vm.componentName() === "small") {
vm.componentName("big");
} else {
vm.componentName("small");
}
}, 3000);
setTimeout(clearInterval.bind(null, i), 45000);
.box {
width:200px;
-webkit-transition: height 2s, width 2s;
transition: height 2s, width 2s;
overflow:hidden;
}
.big {
border: thin solid black;
}
.small {
border: thin solid black;
padding: 10px10px10px10px;
}
.item {
padding-left: 10px;
}
<scriptsrc="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script><divdata-bind="component: { name: componentName, params: { value: $data } }"></div>
Post a Comment for "Transitions Between Different Knockout Components"