Add responsive and lazy image loading
This commit is contained in:
@ -1,37 +1,37 @@
|
||||
<script>
|
||||
import { getFileName } from '$lib/util/links';
|
||||
import { strf } from '$lib/data/language.js';
|
||||
|
||||
function sortItems() {
|
||||
items.sort((a, b) => {
|
||||
if (a.item < b.item) return -1;
|
||||
if (a.item > b.item) return 1;
|
||||
return 0;
|
||||
});
|
||||
}
|
||||
import Photo from './Photo.svelte';
|
||||
|
||||
/** @type {Item[]} */
|
||||
export let items = [];
|
||||
|
||||
export let base = '';
|
||||
|
||||
export let layout = 'grid';
|
||||
</script>
|
||||
|
||||
<section class="gallery">
|
||||
<ul>
|
||||
{#each items as item (item.item)}
|
||||
<!-- <Photo src={`${uriBase}&item=${item.item}`} alt={$strf(item.title)} /> -->
|
||||
<li>
|
||||
<h3>{item.item}</h3>
|
||||
<p>
|
||||
<b>Title:</b> {#if item.title}{$strf(item.title)}{:else}<i>no title</i>{/if}<br />
|
||||
<b>Description:</b> {#if item.description}{$strf(item.description)}{:else}<i>no description</i>{/if}<br />
|
||||
<b>Links:</b> <a href={`${base}${item.item}`}>Open</a> • <a href={`${base}${item.item}/download`}>Download</a><br />
|
||||
<b>Thumbnails:</b> <a href={`${base}${item.item}/t/s`}>Small</a> • <a href={`${base}${item.item}/t/m`}>Medium</a> • <a href={`${base}${item.item}/t/l`}>Large</a> • <a href={`${base}${item.item}/t/full`}>Full</a>
|
||||
</p>
|
||||
<img src={`${base}${item.item}/t`} alt={(item.title ? $strf(item.title) : null) || (item.description ? $strf(item.description) : null) || getFileName(item.item)} />
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
<section class="gallery"
|
||||
class:gallery--grid={layout === 'grid'}>
|
||||
{#if layout === 'grid'}
|
||||
<ul>
|
||||
{#each items as item (item.item)}
|
||||
<!-- <Photo src={`${uriBase}&item=${item.item}`} alt={$strf(item.title)} /> -->
|
||||
<li>
|
||||
<h3>{item.item}</h3>
|
||||
<p>
|
||||
<b>Title:</b> {#if item.title}{$strf(item.title)}{:else}<i>no title</i>{/if}<br />
|
||||
<b>Description:</b> {#if item.description}{$strf(item.description)}{:else}<i>no description</i>{/if}<br />
|
||||
<b>Links:</b> <a href={`${base}${item.item}`}>Open</a> • <a href={`${base}${item.item}/download`}>Download</a><br />
|
||||
<b>Thumbnails:</b> <a href={`${base}${item.item}/t/s`}>Small</a> • <a href={`${base}${item.item}/t/m`}>Medium</a> • <a href={`${base}${item.item}/t/l`}>Large</a> • <a href={`${base}${item.item}/t/full`}>Full</a>
|
||||
</p>
|
||||
<Photo src={`${base}${item.item}`} alt={(item.title ? $strf(item.title) : null) || (item.description ? $strf(item.description) : null) || getFileName(item.item)} />
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
{:else}
|
||||
<i>layout not available</i>
|
||||
{/if}
|
||||
</section>
|
||||
|
||||
<style>
|
||||
|
||||
@ -11,6 +11,12 @@
|
||||
*/
|
||||
export let alt = undefined;
|
||||
|
||||
/**
|
||||
* Whether the image is the first one in the viewport.
|
||||
* @type {boolean}
|
||||
*/
|
||||
export let firstViewport = false;
|
||||
|
||||
/**
|
||||
* The click handler for the image.
|
||||
* @param {MouseEvent} event
|
||||
@ -21,10 +27,12 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<a href="{src}" on:click={clickHandler}>
|
||||
<figure class="photo">
|
||||
<img src="{src}" alt="{alt}" />
|
||||
</figure>
|
||||
<a href={src} on:click={clickHandler}>
|
||||
<picture>
|
||||
<source media="(min-width: 850px)" srcset="{src}/t/l" />
|
||||
<source media="(min-width: 450px)" srcset="{src}/t/m" />
|
||||
<img src="{src}/t" {alt} loading={firstViewport ? 'eager' : 'lazy'} />
|
||||
</picture>
|
||||
</a>
|
||||
|
||||
<style>
|
||||
|
||||
34
src/lib/styles/base.css
Normal file
34
src/lib/styles/base.css
Normal file
@ -0,0 +1,34 @@
|
||||
:root {
|
||||
--main-text-color: #1a1a1a;
|
||||
--main-bg-color: #fff;
|
||||
}
|
||||
|
||||
@media screen and (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--main-text-color: #e7e7e7;
|
||||
--main-bg-color: #1a1a1a;
|
||||
}
|
||||
}
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
box-sizing: border-box;
|
||||
font-size: 1em;
|
||||
font-family: 'Roboto', 'Open Sans', sans-serif;
|
||||
}
|
||||
|
||||
html,
|
||||
body,
|
||||
.container,
|
||||
.footer {
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: sans-serif;
|
||||
background-color: var(--main-bg-color);
|
||||
color: var(--main-text-color);
|
||||
}
|
||||
2
src/lib/types.d.ts
vendored
2
src/lib/types.d.ts
vendored
@ -43,5 +43,3 @@ type Album = AlbumMetadata & {
|
||||
type ApiError = {
|
||||
error: string;
|
||||
};
|
||||
|
||||
type GetFileFunction = (zipName: string, entryName: string, keepOpen: T) => Promise<T extends true ? { content: Buffer, zip: import('node-stream-zip').StreamZipAsync } : Buffer>;
|
||||
|
||||
@ -3,7 +3,14 @@ import StreamZip from "node-stream-zip";
|
||||
|
||||
/** @param {string} zipName */
|
||||
async function _getZip(zipName) {
|
||||
return new StreamZip.async({ file: `./zip/${zipName}` });
|
||||
let zip = null;
|
||||
try {
|
||||
zip = new StreamZip.async({ file: `./zip/${zipName}` });
|
||||
} catch (err) {
|
||||
console.error(`${err.stack}`.replaceAll('/home/sveltekit', '.'));
|
||||
throw error(404, `Album not found.`);
|
||||
}
|
||||
return zip;
|
||||
}
|
||||
/** @param {import('node-stream-zip').StreamZipAsync} zip @param {string} entryName */
|
||||
async function _getFile(zip, entryName) {
|
||||
|
||||
@ -8,21 +8,10 @@
|
||||
</div>
|
||||
|
||||
<style>
|
||||
*{
|
||||
margin: 0px 0px 0px 0px;
|
||||
padding: 0px 0px 0px 0px;
|
||||
border: none;
|
||||
outline: none;
|
||||
}
|
||||
:global(body){
|
||||
background-color: #22313F;
|
||||
}
|
||||
.container {
|
||||
font-family: Roboto, open-sans, Arial;
|
||||
position: fixed;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
color: #FFF;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
a {
|
||||
|
||||
@ -1,9 +1,5 @@
|
||||
<slot />
|
||||
<script>
|
||||
import "$lib/styles/base.css";
|
||||
</script>
|
||||
|
||||
<style>
|
||||
:global(*) {
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
</style>
|
||||
<slot />
|
||||
|
||||
26
src/routes/[slug]/[[timestamp]]/i/[...item]/+page.svelte
Normal file
26
src/routes/[slug]/[[timestamp]]/i/[...item]/+page.svelte
Normal file
@ -0,0 +1,26 @@
|
||||
<script>
|
||||
import { strf } from '$lib/data/language';
|
||||
|
||||
/** @type {import('./$types').PageData} */
|
||||
export let data;
|
||||
</script>
|
||||
|
||||
<div class="container">
|
||||
{#if data.item}
|
||||
<img class="image" src="{data.filename}/t/full" alt={(data.item.title ? $strf(data.item.title) : null) || (data.item.description ? $strf(data.item.description) : null) || data.filename} />
|
||||
{:else}
|
||||
Deine Mudda
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.container {
|
||||
display: grid;
|
||||
place-items: center;
|
||||
height: 100vh;
|
||||
}
|
||||
.image {
|
||||
max-width: 100%;
|
||||
max-height: 100vh;
|
||||
}
|
||||
</style>
|
||||
@ -1,5 +1,6 @@
|
||||
import { getFile } from '$lib/util/album';
|
||||
import { getFileName, getFilePath, getZipName } from '$lib/util/links';
|
||||
import { error } from '@sveltejs/kit';
|
||||
import sharp from 'sharp';
|
||||
|
||||
/** @type {import('./$types').RequestHandler} */
|
||||
@ -25,6 +26,7 @@ export async function GET({ params }) {
|
||||
thumbnail = thumbnail || await sharp(content).resize(width).webp({ quality: 90 }).toBuffer();
|
||||
} catch (err) {
|
||||
console.error(`${err.stack}`.replaceAll('/home/sveltekit', '.'));
|
||||
throw error(500, 'Error getting thumbnail');
|
||||
}
|
||||
return new Response(thumbnail, {
|
||||
headers: {
|
||||
@ -1,19 +0,0 @@
|
||||
<script>
|
||||
import { strf } from '$lib/data/language';
|
||||
|
||||
/** @type {import('./$types').PageData} */
|
||||
export let data;
|
||||
</script>
|
||||
|
||||
{#if data.item}
|
||||
<img class="image" src="{data.filename}/t/full" alt={(data.item.title ? $strf(data.item.title) : null) || (data.item.description ? $strf(data.item.description) : null) || data.filename} />
|
||||
{:else}
|
||||
<p>Item not found.</p>
|
||||
{/if}
|
||||
|
||||
<style>
|
||||
.image {
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user