不同淘汰组件之间的转换

时间:2015-06-24 09:39:18

标签: javascript html css knockout.js

我尝试应用CSS转换,因为我在淘汰组件之间切换,但我没有太多的快乐来实现这一目标。基本上我想要一个具有固定宽度的div,但其内部内容将会改变。当它这样做时,我希望能够转换元素的重新调整大小。



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);
&#13;
.box {
    width: 200px;
    -webkit-transition: height 2s;
    transition: height 2s;
    -webkit-transition: width 2s;
    transition: width 2s;
}
.big {
    border: thin solid black;
}
.small {
    border: thin solid black;
    padding: 10px 10px 10px 10px;
}
.item {
    padding-left: 10px;
}
&#13;
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<div data-bind="component: { name: componentName, params: { value: $data } }">
</div>
&#13;
&#13;
&#13;

所以我问了一些模糊相关的Why doesn't CSS transition get applied?,其中我了解到DOM正在为新值重新构建(在这种情况下是新模板),因此CSS转换不适用。

那里的解决方案很简单(确保你反复绑定同一个东西)。但对于组件,我真的不想将两个模板组合在一起,这样我才能获得转换。还有另一种方法可以达到这个目的吗?

2 个答案:

答案 0 :(得分:2)

你实际上是在切换组件意味着正在重构DOM,所以我没有看到使用CSS进行动画制作的方法。

你可以做的是建立你自己的绑定处理程序,它使用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 component
        var 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: 10px 10px 10px 10px;
}
.item {
    padding-left: 10px;
}
<script src="https://code.jquery.com/jquery-1.11.3.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<div data-bind="animatingComponent: { name: componentName, params: { value: $data } }">
</div>

答案 1 :(得分:1)

它只花了一天的大部分时间,但我完成了过渡工作。我有一个变量,它记住了最后一个显示大小(它是组件发生变化时的起始位置)。当组件发生变化时,我将隐藏的可见性和大小设置为默认值,这样我就可以得到div的预期大小。然后我将其调整为最后一个显示尺寸并使其可见,然后将其调整为新尺寸,然后进行转换。

请注意,我还必须稍微改变一下CSS。

&#13;
&#13;
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);
&#13;
.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: 10px 10px 10px 10px;
}
.item {
    padding-left: 10px;
}
&#13;
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<div data-bind="component: { name: componentName, params: { value: $data } }">
</div>
&#13;
&#13;
&#13;