Lazy load images

This commit is contained in:
Eduard Urbach 2017-06-24 16:17:38 +02:00
parent 79ba7ecf3b
commit b6c4968c6c
6 changed files with 65 additions and 23 deletions

View File

@ -1,14 +1,14 @@
component InputText(id string, value string, label string, placeholder string)
.widget-input
label(for=id)= label + ":"
input.widget-element.action(id=id, type="text", value=value, placeholder=placeholder, data-action="save", data-trigger="change")
input.widget-element.action(id=id, type="text", value=value, placeholder=placeholder, title=placeholder, data-action="save", data-trigger="change")
component InputTextArea(id string, value string, label string, placeholder string)
.widget-input
label(for=id)= label + ":"
textarea.widget-element.action(id=id, placeholder=placeholder, data-action="save", data-trigger="change")= value
textarea.widget-element.action(id=id, placeholder=placeholder, title=placeholder, data-action="save", data-trigger="change")= value
component InputNumber(id string, value int, label string, placeholder string, min string, max string)
.widget-input
label(for=id)= label + ":"
input.widget-element.action(id=id, type="number", value=value, min=min, max=max, placeholder=placeholder, data-action="save", data-trigger="change")
input.widget-element.action(id=id, type="number", value=value, min=min, max=max, placeholder=placeholder, title=placeholder, data-action="save", data-trigger="change")

View File

@ -159,8 +159,10 @@ component Anime(anime *arn.Anime, user *arn.User)
//- if providers.AnimePlanet
//- a.light-button(href="http://www.anime-planet.com/anime/" + providers.AnimePlanet.providerId, target="_blank") AnimePlanet
.sources
p Powered by Kitsu.
.footer
a(href="/api/anime/" + anime.ID) Anime API
span |
span Powered by Kitsu.
//- if descriptionSource
//- span= " Summary by " + summarySource + "."
//- //-

View File

@ -87,10 +87,11 @@
.anime-rating-categories
vertical
.sources
.footer
font-size 0.8rem
opacity 0.5
opacity 0.7
margin-top 0.5rem
text-align center
.relations
horizontal-wrap

View File

@ -84,6 +84,6 @@ component Profile(viewUser *arn.User, user *arn.User, animeList *arn.AnimeList,
.spacer
.post-likes= len(post.Likes)
if user != nil && user.Role == "admin"
.side-note
a(href="/api/user/" + viewUser.ID)= "API: ", viewUser.Nick
.footer
a(href="/api/user/" + viewUser.ID) User API

View File

@ -1,7 +1,7 @@
component Settings(user *arn.User)
h2.page-title Settings
.widgets(data-api="/api/user/" + user.ID)
.widget.mountable
.widgets
.widget.mountable(data-api="/api/user/" + user.ID)
h3.widget-title
Icon("user")
span Personal
@ -10,11 +10,19 @@ component Settings(user *arn.User)
InputText("Tagline", user.Tagline, "Tagline", "Text that appears below your username")
InputText("Website", user.Website, "Website", "Your homepage")
.widget.mountable
.widget.mountable(data-api="/api/user/" + user.ID)
h3.widget-title
Icon("cubes")
span Accounts
InputText("Accounts.AniList.Nick", user.Accounts.AniList.Nick, "AniList", "Your username on anilist.co")
InputText("Accounts.MyAnimeList.Nick", user.Accounts.MyAnimeList.Nick, "MyAnimeList", "Your username on myanimelist.net")
InputText("Accounts.Kitsu.Nick", user.Accounts.Kitsu.Nick, "Kitsu", "Your username on kitsu.io")
InputText("Accounts.AnimePlanet.Nick", user.Accounts.Kitsu.Nick, "Kitsu", "Your username on kitsu.io")
InputText("Accounts.AnimePlanet.Nick", user.Accounts.AnimePlanet.Nick, "AnimePlanet", "Your username on anime-planet.com")
.widget.mountable(data-api="/api/settings/" + user.ID)
h3.widget-title
Icon("cogs")
span Settings
InputText("TitleLanguage", user.Settings().TitleLanguage, "Title language", "Language of anime titles")

View File

@ -5,9 +5,21 @@ import * as actions from "./actions"
export class AnimeNotifier {
app: Application
visibilityObserver: IntersectionObserver
constructor(app: Application) {
this.app = app
this.visibilityObserver = new IntersectionObserver(
entries => {
for(let entry of entries) {
if(entry.intersectionRatio > 0) {
entry.target["became visible"]()
this.visibilityObserver.unobserve(entry.target)
}
}
},
{}
)
}
onReadyStateChange() {
@ -24,6 +36,15 @@ export class AnimeNotifier {
this.app.run()
}
onContentLoaded() {
this.visibilityObserver.disconnect()
// Update each of these asynchronously
Promise.resolve().then(() => this.updateMountables())
Promise.resolve().then(() => this.updateActions())
Promise.resolve().then(() => this.lazyLoadImages())
}
reloadContent() {
return fetch("/_" + this.app.currentPath, {
credentials: "same-origin"
@ -57,9 +78,24 @@ export class AnimeNotifier {
}
}
updateAvatars() {
lazyLoadImages() {
for(let element of findAll("user-image")) {
let img = element as HTMLImageElement
this.lazyLoadImage(element as HTMLImageElement)
}
for(let element of findAll("anime-cover-image")) {
this.lazyLoadImage(element as HTMLImageElement)
}
}
lazyLoadImage(img: HTMLImageElement) {
// Prevent browser from loading it instantly
img["original source"] = img.src
img.src = ""
// Once the image becomes visible, load it
img["became visible"] = () => {
img.src = img["original source"]
if(img.naturalWidth === 0) {
img.onload = function() {
@ -73,6 +109,8 @@ export class AnimeNotifier {
img.classList.add("user-image-found")
}
}
this.visibilityObserver.observe(img)
}
updateMountables() {
@ -94,13 +132,6 @@ export class AnimeNotifier {
}
}
onContentLoaded() {
// Update each of these asynchronously
Promise.resolve().then(() => this.updateMountables())
Promise.resolve().then(() => this.updateAvatars())
Promise.resolve().then(() => this.updateActions())
}
onPopState(e: PopStateEvent) {
if(e.state) {
this.app.load(e.state, {