From 33654b32573b68b9a3d0cdc224a9e98376b647e0 Mon Sep 17 00:00:00 2001 From: Olivier Paroz Date: Sun, 11 Oct 2015 21:17:14 +0200 Subject: [PATCH 1/5] Show thumbnails as soon as they're received --- css/styles.css | 17 ++++++++++- js/galleryalbum.js | 75 ++++++++++++++++++++++++++++++--------------- js/galleryimage.js | 76 +++++++++++++++++++++++++++------------------- js/galleryrow.js | 61 +++++++++++++++++++++++-------------- js/galleryview.js | 55 +++++++++++++++------------------ 5 files changed, 174 insertions(+), 110 deletions(-) diff --git a/css/styles.css b/css/styles.css index 5837a34452..172e63a932 100644 --- a/css/styles.css +++ b/css/styles.css @@ -189,13 +189,18 @@ div.crumb.last a { opacity: .5; } +#gallery a { + -webkit-transition: all .2s ease-in-out; + transition: all .2s ease-in-out; +} + #gallery a.image > img { max-height: 200px; } #gallery .cropped, #gallery .row { - opacity: 1; + opacity: 0.5; -webkit-transition: opacity 500ms; transition: opacity 500ms; } @@ -208,6 +213,16 @@ div.crumb.last a { background-size: cover; } +#gallery a.album .icon-loading, +#gallery .image-container .icon-loading { + margin: auto; + position: absolute; + top: 0; + left: 0; + bottom: 0; + right: 0; +} + #gallery > h2 { background: #f7f7f7; height: 2.8em; diff --git a/js/galleryalbum.js b/js/galleryalbum.js index 930936907f..5ea191708b 100644 --- a/js/galleryalbum.js +++ b/js/galleryalbum.js @@ -8,7 +8,8 @@ 'data-width="{{targetWidth}}" data-height="{{targetHeight}}">' + '
' + ' {{label}}' + - ' ' + + ' ' + ''; /** @@ -33,9 +34,22 @@ Album.prototype = { /** - * Creates the album, which will include between 1 and 4 images + * Returns a new album row * - * * Each album is also a link to open that folder + * @param {number} width + * @param requestId + * + * @returns {Gallery.Row} + */ + getRow: function (width, requestId) { + return new Gallery.Row(width, requestId); + }, + + /** + * Creates the DOM element for the album and return it immediately so as to not block the + * rendering of the rest of the interface + * + * * Each album also contains a link to open that folder * * An album has a natural size of 200x200 and is comprised of 4 thumbnails which have a * natural size of 200x200 * * Thumbnails are checked first in order to make sure that we have something to show @@ -45,35 +59,40 @@ * @return {$} The album to be placed on the row */ getDom: function (targetHeight) { - var album = this; - return $.Deferred().resolve(function () { - if (!album._template) { - album._template = Handlebars.compile(TEMPLATE); - } - var template = album._template({ + if (this.domDef === null) { + var template = Handlebars.compile(TEMPLATE); + var albumElement = template({ targetHeight: targetHeight, targetWidth: targetHeight, - label: album.name, - targetPath: '#' + encodeURIComponent(album.path) + label: this.name, + targetPath: '#' + encodeURIComponent(this.path) }); - album.domDef = $(template); - album.loader = album.domDef.children('.album-loader'); - album.loader.hide(); - album.domDef.click(album._showLoader.bind(album)); + this.domDef = $(albumElement); + this.loader = this.domDef.children('.album-loader'); + this.loader.hide(); + this.domDef.click(this._showLoader.bind(this)); - album._fillSubAlbum(targetHeight); + // Define a if you don't want to set the style in the template + //a.width(targetHeight); + //a.height(targetHeight); - return album.domDef; - }); + this._fillSubAlbum(targetHeight); + } else { + this.loader.hide(); + } + + return this.domDef; }, /** * Fills the row with albums and images * - * @param {number} width + * @param {Gallery.Row} row The row to append elements to + * * @returns {$.Deferred} */ - getNextRow: function (width) { + fillNextRow: function (row) { + var def = new $.Deferred(); var numberOfThumbnailsToPreload = 6; var buffer = 5; @@ -100,12 +119,13 @@ if (more && album.viewedItems < images.length) { return addRowElements(album, row, images); } - return row; + row.fit(); + def.resolve(row); }); }; var items = this.subAlbums.concat(this.images); - var row = new Gallery.Row(width, this.requestId); - return addRowElements(this, row, items); + addRowElements(this, row, items); + return def.promise(); }, /** @@ -160,12 +180,16 @@ imageHolder.css("height", backgroundHeight) .css("width", backgroundWidth); + var spinner = $('
'); + imageHolder.append(spinner); // img is a Thumbnail.image, true means square thumbnails return image.getThumbnail(true).then(function (img) { if (image.thumbnail.valid) { img.alt = ''; - imageHolder.css("background-image", "url('" + img.src + "')"); + spinner.remove(); + imageHolder.css("background-image", "url('" + img.src + "')") + .css('opacity', 1); } }); }, @@ -272,9 +296,10 @@ image.thumbnail = thumb; this.images.push(image); thumb.loadingDeferred.done(function (img) { - imageHolder.append(img); img.height = (targetHeight - 2); img.width = (targetHeight) - 2; + imageHolder.append(img); + imageHolder.css('opacity', 1); }); }, diff --git a/js/galleryimage.js b/js/galleryimage.js index bdd21595dc..f7789e6452 100644 --- a/js/galleryimage.js +++ b/js/galleryimage.js @@ -4,12 +4,12 @@ var TEMPLATE = '
' + + 'style="width: {{targetWidth}}px; height: {{targetHeight}}px;">' + + '
' + ' ' + ' {{label}}' + ' ' + - ' ' + + ' ' + '
'; /** @@ -32,7 +32,7 @@ this.etag = etag; this.thumbnail = null; this.domDef = null; - this.domHeight = null; + this.spinner = null; }; GalleryImage.prototype = { @@ -88,37 +88,49 @@ * @return {a} */ getDom: function (targetHeight) { - var image = this; - if (this.domDef === null || this.domHeight !== targetHeight) { - this.domHeight = targetHeight; - // img is a Thumbnail.image - return this.getThumbnail(false).then(function (img) { - if (!image._template) { - image._template = Handlebars.compile(TEMPLATE); - } - var newWidth = Math.round(targetHeight * image.thumbnail.ratio); - var url = image._getLink(); - var template = image._template({ - targetHeight: targetHeight, - targetWidth: newWidth, - label: OC.basename(image.path), - targetPath: url, - path: image.path - }); - image.domDef = $(template); - image._addLabel(); - // This will stretch wide images to make them reach targetHeight - $(img).css({ - 'width': newWidth, - 'height': targetHeight - }); - img.alt = encodeURI(image.path); - image.domDef.children('a').append(img); + if (this.domDef === null) { + var template = Handlebars.compile(TEMPLATE); + var imageElement = template({ + targetHeight: targetHeight, + targetWidth: targetHeight, + label: OC.basename(this.path), + path: this.path + }); + this.domDef = $(imageElement); + this._addLabel(); + this.spinner = this.domDef.children('.image-loader'); + } + return this.domDef; + }, + + /** + * Resizes the image once it has been loaded + * + * @param targetHeight + */ + resize: function (targetHeight) { + if (this.spinner !== null) { + var img = this.thumbnail.image; + this.spinner.remove(); + this.spinner = null; + + var newWidth = Math.round(targetHeight * this.thumbnail.ratio); + this.domDef.attr('data-width', newWidth) + .attr('data-height', targetHeight); + + var url = this._getLink(); + var a = this.domDef.children('a'); + a.attr('href', url) + .attr('data-path', this.path); - return image.domDef; + // This will stretch wide images to make them reach targetHeight + $(img).css({ + 'width': newWidth, + 'height': targetHeight }); + img.alt = encodeURI(this.path); + a.append(img); } - return $.Deferred().resolve(this.domDef); }, /** diff --git a/js/galleryrow.js b/js/galleryrow.js index 1c841ec66e..c757218835 100644 --- a/js/galleryrow.js +++ b/js/galleryrow.js @@ -13,7 +13,7 @@ this.items = []; this.width = 4; // 4px margin to start with this.requestId = requestId; - this.domDef = null; + this.domDef = $('
').addClass('row'); }; Row.prototype = { @@ -29,6 +29,7 @@ var row = this; var fileNotFoundStatus = 404; var def = new $.Deferred(); + var itemDom; var validateRowWidth = function (width) { row.items.push(element); @@ -36,6 +37,9 @@ def.resolve(!row._isFull()); }; + itemDom = element.getDom(row.targetHeight); + row.domDef.append(itemDom); + // No need to use getThumbnailWidth() for albums, the width is always 200 if (element instanceof Album) { var width = row.targetHeight; @@ -44,11 +48,14 @@ // We can't calculate the total width if we don't have the width of the thumbnail element.getThumbnailWidth().then(function (width) { if (element.thumbnail.status !== fileNotFoundStatus) { + element.resize(row.targetHeight); validateRowWidth(width); } else { + itemDom.remove(); def.resolve(true); } }, function () { + itemDom.remove(); def.resolve(true); }); } @@ -57,33 +64,43 @@ }, /** - * Creates the row element in the DOM + * Returns the DOM element of the row * * @returns {*} */ getDom: function () { + return this.domDef; + }, + + /** + * Resizes the row once it's full + */ + fit: function () { var scaleRatio = (this.width > this.targetWidth) ? this.targetWidth / this.width : 1; - var targetHeight = this.targetHeight * scaleRatio; + + // This animates the elements when the window is resized + var targetHeight = 4 + (this.targetHeight * scaleRatio); targetHeight = targetHeight.toFixed(3); - var row = $('
').addClass('row'); - /** - * @param {*} row - * @param {GalleryImage[]|Album[]} items - * @param {number} i - * - * @returns {*} - */ - var addImageToDom = function (row, items, i) { - return items[i].getDom(targetHeight).then(function (itemDom) { - i++; - row.append(itemDom); - if (i < items.length) { - return addImageToDom(row, items, i); - } - return row; - }); - }; - return addImageToDom(row, this.items, 0); + this.domDef.height(targetHeight); + this.domDef.width(this.width * scaleRatio); + + // Resizes and scales all photowall elements to make them fit within the window's width + this.domDef.find('.item-container').each(function () { + // Necessary since DOM elements are not resized when CSS transform is used + $(this).css('width', $(this).data('width') * scaleRatio) + .css('height', $(this).data('height') * scaleRatio); + // This scales the anchors inside the item-container divs + $(this).children('a').css('transform-origin', 'left top') + .css('-webkit-transform-origin', 'left top') + .css('-ms-transform-origin', 'left top') + .css('transform', 'scale(' + scaleRatio + ')') + .css('-webkit-transform', 'scale(' + scaleRatio + ')') + .css('-ms-transform', 'scale(' + scaleRatio + ')'); + }); + + // Restore the rows to their normal opacity. This happens immediately with rows + // containing albums only + this.domDef.css('opacity', 1); }, /** diff --git a/js/galleryview.js b/js/galleryview.js index 370a88e709..f075ded912 100644 --- a/js/galleryview.js +++ b/js/galleryview.js @@ -153,46 +153,41 @@ // Everything is still in sync, since no deferred calls have been placed yet - return album.getNextRow($(window).width()).then(function (row) { + var row = album.getRow($(window).width(), view.requestId); + var rowDom = row.getDom(); + view.element.append(rowDom); + return album.fillNextRow(row).then(function () { /** * At this stage, the row has a width and contains references to images based * on * information available when making the request, but this information may have * changed while we were receiving thumbnails for the row */ - if (view.requestId === row.requestId) { - return row.getDom().then(function (dom) { - - if (Gallery.currentAlbum !== path) { - view.loadVisibleRows.loading = null; - return; //throw away the row if the user has navigated away in the - // meantime - } - if (view.element.length === 1) { - Gallery.showNormal(); - } - - view.element.append(dom); - - if (album.viewedItems < album.subAlbums.length + album.images.length && - view.element.height() < targetHeight) { - return showRows(album); - } - - // No more rows to load at the moment + if (Gallery.currentAlbum !== path) { view.loadVisibleRows.loading = null; - $('#loading-indicator').hide(); - }, function () { - // Something went wrong, so kill the loader - view.loadVisibleRows.loading = null; - $('#loading-indicator').hide(); - }); + return; //throw away the row if the user has navigated away in the + // meantime + } + if (view.element.length === 1) { + Gallery.showNormal(); + } + if (album.viewedItems < album.subAlbums.length + album.images.length && + view.element.height() < targetHeight) { + return showRows(album); + } + // No more rows to load at the moment + view.loadVisibleRows.loading = null; + $('#loading-indicator').hide(); + } else { + // This is the safest way to do things + view.viewAlbum(Gallery.currentAlbum); } - // This is the safest way to do things - view.viewAlbum(Gallery.currentAlbum); - + }, function () { + // Something went wrong, so kill the loader + view.loadVisibleRows.loading = null; + $('#loading-indicator').hide(); }); }; if (this.element.height() < targetHeight) { From a8d0c35ad23782fb47802a49ea3cb717d09a6e85 Mon Sep 17 00:00:00 2001 From: Olivier Paroz Date: Sun, 11 Oct 2015 21:21:13 +0200 Subject: [PATCH 2/5] Remove bottom row spinner (cherry picked from commit 11d33eb) --- css/public.css | 7 ------- css/styles.css | 6 ------ js/gallery.js | 1 - js/galleryview.js | 5 ----- templates/part.content.php | 1 - templates/public.php | 1 - 6 files changed, 21 deletions(-) diff --git a/css/public.css b/css/public.css index a5a947a029..72eb44ef50 100644 --- a/css/public.css +++ b/css/public.css @@ -57,10 +57,3 @@ body { #gallery.hascontrols { padding-bottom: 0; } - -#loading-indicator { - height: 32px; - width: 100%; - margin-top: 100px; - margin-bottom: 0; -} diff --git a/css/styles.css b/css/styles.css index 172e63a932..ba90b8aefd 100644 --- a/css/styles.css +++ b/css/styles.css @@ -285,12 +285,6 @@ http://www.bypeople.com/author/comatosed/*/ border-color: #bbb #bbb transparent #bbb; } -#loading-indicator { - height: 32px; - width: 100%; - margin-bottom: 50px; -} - .icon-gallery { background-image: url('../img/gallery-dark.svg'); } diff --git a/js/gallery.js b/js/gallery.js index c16b706236..ff4f5af4b2 100644 --- a/js/gallery.js +++ b/js/gallery.js @@ -272,7 +272,6 @@ emptyContentElement.html(message); emptyContentElement.removeClass('hidden'); $('#controls').addClass('hidden'); - $('#loading-indicator').hide(); }, /** diff --git a/js/galleryview.js b/js/galleryview.js index f075ded912..3fd6449e5b 100644 --- a/js/galleryview.js +++ b/js/galleryview.js @@ -80,7 +80,6 @@ } this.clear(); - $('#loading-indicator').show(); if (albumPath !== Gallery.currentAlbum) { this.loadVisibleRows.loading = false; @@ -147,7 +146,6 @@ // If we've reached the end of the album, we kill the loader if (!(album.viewedItems < album.subAlbums.length + album.images.length)) { view.loadVisibleRows.loading = null; - $('#loading-indicator').hide(); return; } @@ -179,7 +177,6 @@ } // No more rows to load at the moment view.loadVisibleRows.loading = null; - $('#loading-indicator').hide(); } else { // This is the safest way to do things view.viewAlbum(Gallery.currentAlbum); @@ -187,7 +184,6 @@ }, function () { // Something went wrong, so kill the loader view.loadVisibleRows.loading = null; - $('#loading-indicator').hide(); }); }; if (this.element.height() < targetHeight) { @@ -198,7 +194,6 @@ }, hideButtons: function () { - $('#loading-indicator').hide(); $('#album-info-button').hide(); $('#share-button').hide(); $('#sort-name-button').hide(); diff --git a/templates/part.content.php b/templates/part.content.php index ed62513902..c2fc863ec2 100644 --- a/templates/part.content.php +++ b/templates/part.content.php @@ -157,7 +157,6 @@ class="icon-confirm svg">
-
diff --git a/templates/public.php b/templates/public.php index 8bf67ad23e..a90aabc134 100644 --- a/templates/public.php +++ b/templates/public.php @@ -152,7 +152,6 @@ class="icon-confirm svg"> data-token="">
-