1
0
mirror of https://github.com/xuthus83/pigallery2.git synced 2025-01-14 14:43:17 +08:00

improving thumbnail loading

This commit is contained in:
Braun Patrik 2016-06-17 11:25:15 +02:00
parent 08d6dc994e
commit cc43db58ac
8 changed files with 242 additions and 145 deletions

View File

@ -18,6 +18,7 @@ interface SearchConfig {
interface ClientConfig { interface ClientConfig {
thumbnailSizes:Array<number>; thumbnailSizes:Array<number>;
Search:SearchConfig; Search:SearchConfig;
concurrentThumbnailGenerations:number;
} }
export class ConfigClass { export class ConfigClass {
@ -29,7 +30,8 @@ export class ConfigClass {
searchEnabled: true, searchEnabled: true,
instantSearchEnabled: true, instantSearchEnabled: true,
autocompleteEnabled: true autocompleteEnabled: true
} },
concurrentThumbnailGenerations: 1
}; };
public setDatabaseType(type:DatabaseType) { public setDatabaseType(type:DatabaseType) {

View File

@ -0,0 +1,97 @@
.static {
width: 100%;
height: 100%;
background-color: #bbbbbb;
color: #7f7f7f;
font-size: 50px;
}
.static span {
top: calc(50% - 25px);
left: calc(50% - 25px);
}
.sk-cube-grid {
width: 100%;
height: 100%;
}
.sk-cube-grid .sk-cube {
width: 33%;
height: 33%;
background-color: #bbbbbb;
float: left;
}
.sk-cube-grid.animate .sk-cube {
-webkit-animation: sk-cubeGridScaleDelay 4.6s infinite ease-in-out;
animation: sk-cubeGridScaleDelay 4.6s infinite ease-in-out;
}
.sk-cube-grid.animate .sk-cube1 {
-webkit-animation-delay: 0.4s;
animation-delay: 0.4s;
}
.sk-cube-grid.animate .sk-cube2 {
-webkit-animation-delay: 0.6s;
animation-delay: 0.6s;
}
.sk-cube-grid.animate .sk-cube3 {
-webkit-animation-delay: 0.8s;
animation-delay: 0.8s;
}
.sk-cube-grid.animate .sk-cube4 {
-webkit-animation-delay: 0.2s;
animation-delay: 0.2s;
}
.sk-cube-grid.animate .sk-cube5 {
-webkit-animation-delay: 0.4s;
animation-delay: 0.4s;
}
.sk-cube-grid.animate .sk-cube6 {
-webkit-animation-delay: 0.6s;
animation-delay: 0.6s;
}
.sk-cube-grid.animate .sk-cube7 {
-webkit-animation-delay: 0s;
animation-delay: 0s;
}
.sk-cube-grid.animate .sk-cube8 {
-webkit-animation-delay: 0.2s;
animation-delay: 0.2s;
}
.sk-cube-grid.animate .sk-cube9 {
-webkit-animation-delay: 0.4s;
animation-delay: 0.4s;
}
@-webkit-keyframes sk-cubeGridScaleDelay {
0%, 70%, 100% {
-webkit-transform: scale3D(1, 1, 1);
transform: scale3D(1, 1, 1);
}
35% {
-webkit-transform: scale3D(0, 0, 1);
transform: scale3D(0, 0, 1);
}
}
@keyframes sk-cubeGridScaleDelay {
0%, 70%, 100% {
-webkit-transform: scale3D(1, 1, 1);
transform: scale3D(1, 1, 1);
}
35% {
-webkit-transform: scale3D(0, 0, 1);
transform: scale3D(0, 0, 1);
}
}

View File

@ -0,0 +1,12 @@
<div class="static" *ngIf="!animate"><span class="glyphicon glyphicon-picture" aria-hidden="true"></span></div>
<div class="sk-cube-grid animate" *ngIf="animate">
<div class="sk-cube sk-cube1"></div>
<div class="sk-cube sk-cube2"></div>
<div class="sk-cube sk-cube3"></div>
<div class="sk-cube sk-cube4"></div>
<div class="sk-cube sk-cube5"></div>
<div class="sk-cube sk-cube6"></div>
<div class="sk-cube sk-cube7"></div>
<div class="sk-cube sk-cube8"></div>
<div class="sk-cube sk-cube9"></div>
</div>

View File

@ -0,0 +1,19 @@
///<reference path="../../../../../browser.d.ts"/>
import {Component} from "@angular/core";
@Component({
selector: 'gallery-grid-photo-loading',
templateUrl: 'app/gallery/grid/photo/loading/loading.photo.grid.gallery.component.html',
styleUrls: ['app/gallery/grid/photo/loading/loading.photo.grid.gallery.component.css'],
})
export class GalleryPhotoLoadingComponent {
animate = false;
startAnimation() {
console.log("animate");
this.animate = true;
}
}

View File

@ -1,6 +1,51 @@
img { img {
width: inherit; width: inherit;
height: inherit; height: inherit;
-webkit-animation: fadein 2s; /* Safari, Chrome and Opera > 12.1 */
-moz-animation: fadein 2s; /* Firefox < 16 */
-ms-animation: fadein 2s; /* Internet Explorer */
-o-animation: fadein 2s; /* Opera < 12.1 */
animation: fadein 2s;
}
@keyframes fadein {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
/* Firefox < 16 */
@-moz-keyframes fadein {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
/* Safari, Chrome and Opera > 12.1 */
@-webkit-keyframes fadein {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
/* Internet Explorer */
@-ms-keyframes fadein {
from {
opacity: 0;
}
to {
opacity: 1;
}
} }
.info { .info {
@ -44,85 +89,3 @@ a {
display: inline-block; display: inline-block;
width: 100%; width: 100%;
} }
.sk-cube-grid {
width: 100%;
height: 100%;
}
.sk-cube-grid .sk-cube {
width: 33%;
height: 33%;
background-color: rgba(0, 0, 0, 0.1);
float: left;
-webkit-animation: sk-cubeGridScaleDelay 4.6s infinite ease-in-out;
animation: sk-cubeGridScaleDelay 4.6s infinite ease-in-out;
}
.sk-cube-grid .sk-cube1 {
-webkit-animation-delay: 2.4s;
animation-delay: 2.4s;
}
.sk-cube-grid .sk-cube2 {
-webkit-animation-delay: 2.6s;
animation-delay: 2.6s;
}
.sk-cube-grid .sk-cube3 {
-webkit-animation-delay: 2.8s;
animation-delay: 2.8s;
}
.sk-cube-grid .sk-cube4 {
-webkit-animation-delay: 2.2s;
animation-delay: 2.2s;
}
.sk-cube-grid .sk-cube5 {
-webkit-animation-delay: 2.4s;
animation-delay: 2.4s;
}
.sk-cube-grid .sk-cube6 {
-webkit-animation-delay: 2.6s;
animation-delay: 2.6s;
}
.sk-cube-grid .sk-cube7 {
-webkit-animation-delay: 2s;
animation-delay: 2s;
}
.sk-cube-grid .sk-cube8 {
-webkit-animation-delay: 2.2s;
animation-delay: 2.2s;
}
.sk-cube-grid .sk-cube9 {
-webkit-animation-delay: 2.4s;
animation-delay: 2.4s;
}
@-webkit-keyframes sk-cubeGridScaleDelay {
0%, 70%, 100% {
-webkit-transform: scale3D(1, 1, 1);
transform: scale3D(1, 1, 1);
}
35% {
-webkit-transform: scale3D(0, 0, 1);
transform: scale3D(0, 0, 1);
}
}
@keyframes sk-cubeGridScaleDelay {
0%, 70%, 100% {
-webkit-transform: scale3D(1, 1, 1);
transform: scale3D(1, 1, 1);
}
35% {
-webkit-transform: scale3D(0, 0, 1);
transform: scale3D(0, 0, 1);
}
}

View File

@ -1,16 +1,8 @@
<div class="photo-container" (mouseover)="hover()" (mouseout)="mouseOut()"> <div class="photo-container" (mouseover)="hover()" (mouseout)="mouseOut()">
<img #image [src]="imageSrc" [hidden]="!showImage"> <img #image [src]="imageSrc" [hidden]="!showImage">
<div class="sk-cube-grid" *ngIf="!showImage">
<div class="sk-cube sk-cube1"></div> <gallery-grid-photo-loading #loading *ngIf="!showImage">
<div class="sk-cube sk-cube2"></div> </gallery-grid-photo-loading>
<div class="sk-cube sk-cube3"></div>
<div class="sk-cube sk-cube4"></div>
<div class="sk-cube sk-cube5"></div>
<div class="sk-cube sk-cube6"></div>
<div class="sk-cube sk-cube7"></div>
<div class="sk-cube sk-cube8"></div>
<div class="sk-cube sk-cube9"></div>
</div>
<!--Info box --> <!--Info box -->
<div #info [hidden]="!showImage" class="info" [style.margin-top.px]="-infoStyle.height" <div #info [hidden]="!showImage" class="info" [style.margin-top.px]="-infoStyle.height"

View File

@ -1,23 +1,25 @@
///<reference path="../../../../browser.d.ts"/> ///<reference path="../../../../browser.d.ts"/>
import {Component, Input, ElementRef, ViewChild, OnChanges} from "@angular/core"; import {Component, Input, ElementRef, ViewChild, AfterViewInit} from "@angular/core";
import {IRenderable, Dimension} from "../../../model/IRenderable"; import {IRenderable, Dimension} from "../../../model/IRenderable";
import {GridPhoto} from "../GridPhoto"; import {GridPhoto} from "../GridPhoto";
import {SearchTypes} from "../../../../../common/entities/AutoCompleteItem"; import {SearchTypes} from "../../../../../common/entities/AutoCompleteItem";
import {RouterLink} from "@angular/router-deprecated"; import {RouterLink} from "@angular/router-deprecated";
import {Config} from "../../../config/Config"; import {Config} from "../../../config/Config";
import {ThumbnailLoaderService} from "../thumnailLoader.service"; import {ThumbnailLoaderService} from "../thumnailLoader.service";
import {GalleryPhotoLoadingComponent} from "./loading/loading.photo.grid.gallery.component";
@Component({ @Component({
selector: 'gallery-grid-photo', selector: 'gallery-grid-photo',
templateUrl: 'app/gallery/grid/photo/photo.grid.gallery.component.html', templateUrl: 'app/gallery/grid/photo/photo.grid.gallery.component.html',
styleUrls: ['app/gallery/grid/photo/photo.grid.gallery.component.css'], styleUrls: ['app/gallery/grid/photo/photo.grid.gallery.component.css'],
directives: [RouterLink], directives: [RouterLink, GalleryPhotoLoadingComponent],
}) })
export class GalleryPhotoComponent implements IRenderable, OnChanges { export class GalleryPhotoComponent implements IRenderable, AfterViewInit {
@Input() gridPhoto:GridPhoto; @Input() gridPhoto:GridPhoto;
@ViewChild("image") imageRef:ElementRef; @ViewChild("image") imageRef:ElementRef;
@ViewChild("info") infoDiv:ElementRef; @ViewChild("info") infoDiv:ElementRef;
@ViewChild(GalleryPhotoLoadingComponent) loading:GalleryPhotoLoadingComponent;
imageSrc = "#"; imageSrc = "#";
showImage = false; showImage = false;
@ -34,21 +36,28 @@ export class GalleryPhotoComponent implements IRenderable, OnChanges {
this.searchEnabled = Config.Client.Search.searchEnabled; this.searchEnabled = Config.Client.Search.searchEnabled;
} }
ngOnChanges() { ngAfterViewInit() {
if (this.gridPhoto.isThumbnailAvailable()) { //schedule change after Angular checks the model
this.imageSrc = this.gridPhoto.getThumbnailPath(); setImmediate(() => {
// this.showImage = true; if (this.gridPhoto.isThumbnailAvailable()) {
} else {
this.thumbnailService.loadImage(this.gridPhoto).then(()=> {
this.imageSrc = this.gridPhoto.getThumbnailPath(); this.imageSrc = this.gridPhoto.getThumbnailPath();
// this.showImage = true; this.showImage = true;
this.gridPhoto.thumbnailLoaded(); } else {
}).catch((error)=> { this.thumbnailService.loadImage(this.gridPhoto,
console.error("something bad happened"); ()=> { //onLoadStarted
}); this.loading.startAnimation();
} },
()=> {//onLoaded
this.imageSrc = this.gridPhoto.getThumbnailPath();
this.showImage = true;
},
()=> {//onError
console.error("something bad happened");
});
}
});
} }
getPositionText():string { getPositionText():string {
if (!this.gridPhoto) { if (!this.gridPhoto) {

View File

@ -2,64 +2,66 @@
import {Injectable} from "@angular/core"; import {Injectable} from "@angular/core";
import {GridPhoto} from "./GridPhoto"; import {GridPhoto} from "./GridPhoto";
import {Config} from "../../config/Config";
@Injectable() @Injectable()
export class ThumbnailLoaderService { export class ThumbnailLoaderService {
que:Array<ThumbnailTask> = []; que:Array<ThumbnailTask> = [];
runningRequests:number = 0;
constructor() { constructor() {
} }
loadImage(gridPhoto:GridPhoto):Promise<void> { loadImage(gridPhoto:GridPhoto, onStartedLoading, onLoad, onError):void {
console.log("[LOAD IMG]" + gridPhoto.photo.name); console.log("[LOAD IMG]" + gridPhoto.photo.name);
return new Promise<void>((resolve:Function, reject:Function)=> { let tmp:ThumbnailTask = null;
let tmp:ThumbnailTask = null; for (let i = 0; i < this.que.length; i++) {
for (let i = 0; i < this.que.length; i++) { if (this.que[i].gridPhoto.getThumbnailPath() == gridPhoto.getThumbnailPath()) {
if (this.que[i].src == gridPhoto.getThumbnailPath()) { tmp = this.que[i];
tmp = this.que[i]; break;
break;
}
} }
if (tmp != null) { }
tmp.resolve.push(resolve); if (tmp != null) {
tmp.reject.push(reject); tmp.onStartedLoading.push(onStartedLoading);
} else { tmp.onLoad.push(onLoad);
this.que.push({src: gridPhoto.getThumbnailPath(), resolve: [resolve], reject: [reject]}); tmp.onError.push(onError);
} } else {
this.run(); this.que.push({
}); gridPhoto: gridPhoto,
onStartedLoading: [onStartedLoading],
onLoad: [onLoad],
onError: [onError]
});
}
this.run();
} }
isRunning:boolean = false;
run() { run() {
if (this.que.length === 0 || this.isRunning === true) { if (this.que.length === 0 || this.runningRequests >= Config.Client.concurrentThumbnailGenerations) {
return; return;
} }
this.isRunning = true; this.runningRequests++;
let task = this.que.shift(); let task = this.que.shift();
console.log("loadingstarted: " + task.src); task.onStartedLoading.forEach(cb=>cb());
console.log("loadingstarted: " + task.gridPhoto.getThumbnailPath());
let curImg = new Image(); let curImg = new Image();
curImg.src = task.src; curImg.src = task.gridPhoto.getThumbnailPath();
curImg.onload = () => { curImg.onload = () => {
console.log(task.src + "done"); console.log(task.gridPhoto.getThumbnailPath() + "done");
task.resolve.forEach((resolve:()=>{}) => { task.gridPhoto.thumbnailLoaded();
resolve(); task.onLoad.forEach(cb=>cb());
this.runningRequests--;
});
this.isRunning = false;
this.run(); this.run();
}; };
curImg.onerror = (error) => { curImg.onerror = (error) => {
console.error(task.src + "error"); console.error(task.gridPhoto.getThumbnailPath() + "error");
task.reject.forEach((reject:(error)=>{}) => { task.onLoad.forEach(cb=>cb(error));
reject(error); this.runningRequests--;
});
this.isRunning = false;
this.run(); this.run();
}; };
} }
@ -67,7 +69,8 @@ export class ThumbnailLoaderService {
} }
interface ThumbnailTask { interface ThumbnailTask {
src:string; gridPhoto:GridPhoto;
resolve:Array<Function>; onStartedLoading:Array<Function>;
reject:Array<Function>; onLoad:Array<Function>;
onError:Array<Function>;
} }