更新
我修改了我的原始代码。虽然这显然未经测试,但它应该给你一个合理的开始。一旦img
创建了元素并src
设置了属性(如here所述),图像就会开始预加载。它们不必附加到 DOM。我在下面包含的这个改进版本将根据paginationPadding
变量在当前图像之前和之后预加载图像。
结束更新
在 Backbone 中处理事物时,它有助于将所有与状态相关的信息保留在模型中,然后让 UI 响应它。在这种情况下,您可以简单地跟踪要在模型中显示的图像,然后响应模型中的更改跟踪您要查看的照片,您可以简单地重新渲染照片库以显示所需的图片。它是反应性的。
这是一个粗略的实现,但希望能让您了解如何实现一个图库,您可以在其中单击像这样的照片。当然,你仍然有一堆 CSS 之类的事情要做。它不完整。如果您有任何问题,请告诉我。
var StateModel = Backbone.Model.extend({
defaults: {
visibleImage: 0
},
initialize: function(attributes, options) {
this.photoCollection = options.photoCollection;
},
setIndex: function(photoIndex) {
if(!this.photoCollection.length) return;
if(photoIndex < 0) {
photoIndex = this.photoCollection.length - 1;
} else if(photoIndex >= this.photoCollection.length) {
photoIndex = 0;
}
this.set('visibleImage', photoIndex);
},
setPrev: function() {
this.setIndex(this.get('visibleImage') - 1);
},
setNext: function() {
this.setIndex(this.get('visibleImage') + 1);
}
});
var PhotoControls = Backbone.View.extend({
tagName: 'div',
initialize: function(options) {
this.stateModel = options.stateModel;
this.paginationPadding = options.paginationPadding;
},
render: function() {
var that = this;
this.$el.empty();
var ctrlStyle = {cursor: 'pointer', display: 'inline-block', padding: '5px 10px', margin: '0px 5px', border: '1px solid #000'};
this.$el.append($('<div><< Previous</div>')
.css(ctrlStyle))
.click(function() {
that.stateModel.setNext();
});
// Display numbers
var visibleImage = this.stateModel.get('visibleImage');
var pgStart = Math.max(visibleImage - this.paginationPadding, 0);
var pgEnd = Math.min(visibleImage + this.paginationPadding, this.stateModel.photoCollection.length - 1);
for(var i = pgStart; i <= pgEnd; i++) {
var $numEl = that.$el.append(
$('<div>' + (i + 1) + '</div>')
.css(ctrlStyle)
.click(function() {
that.stateModel.setIndex(i);
});
if(i == visibleImage) {
$numEl.css({fontWeight: 'bold', textDecoration: 'underline'});
}
);
}
this.$el.$('<div>Next >></div>')
.css(ctrlStyle))
.click(function() {
that.stateModel.setPrev();
});
return this;
}
});
var PhotoView = Backbone.View.extend({
render: function() {
this.$el.html('<img src="' + this.model.get('url') + '" />');
return this;
}
});
var GalleryView = Backbone.View.extend({
tagName: 'div',
initialize: function(options) {
this.paginationPadding = 2;
this.collection = PhotosCollection();
this.renderedViews = {};
this.state = new StateModel(null, {photoCollection: this.collection});
this.photoControls = new PhotoControls({
stateModel: this.state,
paginationPadding: this.paginationPadding
}).render();
},
render: function() {
if(this.photoView) {
this.photoView.remove();
}
// Pre-fetch images before and after current one based on pagination padding
var visibleImage = this.stateModel.get('visibleImage');
var pgStart = Math.max(visibleImage - this.paginationPadding, 0);
var pgEnd = Math.min(visibleImage + this.paginationPadding, this.stateModel.photoCollection.length - 1);
for(var i = pgStart; i <= pgEnd; i++) {
if(!this.renderedViews[fetchModel.cid]) {
// Images will begin fetching as soon as the 'src' attribute is set on the 'img' element rendered by the view
this.renderedViews[model.cid] = new PhotoView(this.collection.at(i));
this.renderedViews[model.cid].render();
}
this.$el.html(this.photoView.render().el);
}
// Attach the view for the current image
var renderModel = this.collection.at(this.state.get('visibleImage'));
if(renderModel && this.renderedViews[renderModel.cid]) {
this.photoView = this.renderedViews[renderModel.cid];
this.$el.html(this.photoView.el);
}
this.$el.append(this.photoControls.el);
return this;
}
});