Skip to content

Commit 2946f2b

Browse files
committed
Big v2 refactor ftw
1 parent 67ffa48 commit 2946f2b

File tree

6 files changed

+117
-99
lines changed

6 files changed

+117
-99
lines changed

src/lib/FxReveal.svelte

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,44 @@
11
<script>
22
import Img from './SvelteImg.svelte'
3-
import { observe } from './utils.js'
3+
import { observe, len, lqipToBackground } from './utils.js'
44
import { onMount } from 'svelte'
55
6-
/** @type {Object[]} imagetools import */
7-
export let src = []
6+
/** @type {Object} imagetools import */
7+
export let src = {}
88
/** @type {HTMLImageElement|undefined} bindable reference to <img> element */
99
export let ref = undefined
1010
11-
let sources = []
11+
let meta = {}
1212
let background
1313
let mounted = false
1414
let loaded = false
1515
let inview = false
1616
17-
$: if (src.length) {
18-
const { base64 } = src.find((i) => i.base64) || {}
19-
background = base64 && `url('${base64}') no-repeat center/cover`
20-
sources = src.filter((i) => !i.base64)
17+
$: if (len(src)) {
18+
loaded = false
19+
const { lqip, src: s, w, h } = src.img
20+
background = lqip ? lqipToBackground(lqip) : undefined
21+
const { sources = {} } = src
22+
meta = { img: { src: s, w, h }, sources }
23+
} else {
24+
meta = {}
2125
}
2226
23-
onMount(() => {
27+
onMount(async () => {
2428
mounted = true
2529
if (ref.complete) loaded = true
2630
})
2731
</script>
2832

29-
{#if src.length}
33+
{#if len(meta)}
3034
<div
3135
class="wrap"
3236
class:mounted
3337
class:reveal={loaded && inview}
3438
use:observe
3539
on:enter={() => (inview = true)}
3640
>
37-
<Img src={sources} bind:ref on:load={() => (loaded = true)} on:click {...$$restProps} />
41+
<Img src={meta} bind:ref on:load on:load={() => (loaded = true)} on:click {...$$restProps} />
3842
<div class="lqip" style:background />
3943
</div>
4044
{/if}
@@ -49,23 +53,23 @@ onMount(() => {
4953
}
5054
.mounted :global(img) {
5155
opacity: 0;
52-
transform: scale(var(--reveal-scale, 1.03));
56+
transform: var(--reveal-transform, scale(1.05));
5357
}
54-
.reveal :global(img) {
55-
transition: opacity var(--reveal-opacity-duration, 1s) linear,
56-
transform var(--reveal-transform-duration, 0.6s) ease-out;
58+
.mounted.reveal :global(img) {
59+
transition: var(--reveal-transition, opacity 1s linear, transform 0.75s ease-out);
5760
opacity: 1;
5861
transform: scale(1);
5962
}
6063
.lqip {
6164
position: absolute;
6265
inset: 0;
6366
z-index: -1;
67+
transform: var(--reveal-transform, scale(1.05));
6468
}
6569
.lqip::after {
6670
content: '';
6771
position: absolute;
6872
inset: 0;
69-
backdrop-filter: blur(20px);
73+
backdrop-filter: var(--reveal-filter, blur(20px));
7074
}
7175
</style>

src/lib/Picture.svelte

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<script>
2+
import { len } from './utils.js'
3+
4+
export let sources = {}
5+
export let sizes = undefined
6+
7+
let srcs = []
8+
9+
$: if (len(sources)) {
10+
const list = []
11+
for (const [format, imgs] of Object.entries(sources)) {
12+
list.push({
13+
format,
14+
srcset: imgs.map((i) => `${i.src} ${i.w}w`).join()
15+
})
16+
}
17+
srcs = list
18+
} else {
19+
srcs = []
20+
}
21+
</script>
22+
23+
{#if len(srcs)}
24+
<picture>
25+
{#each srcs as { format, srcset }}
26+
<source type="image/{format}" {sizes} {srcset} />
27+
{/each}
28+
<slot />
29+
</picture>
30+
{:else}
31+
<slot />
32+
{/if}

src/lib/SvelteImg.svelte

Lines changed: 24 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,71 +1,48 @@
11
<script>
2-
/** @type {Object[]} imagetools import */
3-
export let src = []
2+
import Picture from './Picture.svelte'
3+
import { len, lqipToBackground } from './utils.js'
4+
5+
/** @type {Object} imagetools import */
6+
export let src = {}
47
/** @type {string|undefined} img tag `sizes` attr */
58
export let sizes = undefined
6-
/** @type {number|undefined} image width override */
9+
/** @type {number|undefined} img width override */
710
export let width = undefined
8-
/** @type {number|undefined} image height override */
11+
/** @type {number|undefined} img height override */
912
export let height = undefined
1013
/** @type {'lazy'|'eager'} img tag `loading` attr */
1114
export let loading = 'lazy'
12-
/** @type {'async'|'auto'|'sync'} */
15+
/** @type {'async'|'auto'|'sync'} img tag `decoding` attr */
1316
export let decoding = 'async'
14-
/** @type {HTMLImageElement|undefined} bindable reference to <img> element */
17+
/** @type {HTMLImageElement|undefined} bindable reference to `<img>` element */
1518
export let ref = undefined
1619
17-
const priority = ['heic', 'heif', 'avif', 'webp', 'jpeg', 'jpg', 'png', 'gif', 'tiff']
18-
let image = {}
1920
let sources = []
21+
let img = {}
22+
let background = undefined
2023
21-
$: if (src.length) {
22-
const { list, lqip } = src.reduce(
23-
(a, c) => {
24-
if (c.base64) a.lqip = `url('${c.base64}') no-repeat center/cover`
25-
else a.list.push(c)
26-
return a
27-
},
28-
{ list: [], lqip: undefined }
29-
)
30-
const groups = []
31-
for (const format of priority) {
32-
const group = list.filter((i) => i.format === format)
33-
if (group.length) {
34-
group.sort((a, b) => a.width - b.width)
35-
const { src, width, height } = group[group.length - 1]
36-
groups.push({
37-
format: format === 'jpg' ? 'jpeg' : format,
38-
srcset: group.reduce((a, c) => [...a, `${c.src} ${c.width}w`], []).join(','),
39-
src,
40-
width,
41-
height
42-
})
43-
}
44-
}
45-
image = { ...groups.pop(), lqip }
46-
sources = groups
24+
$: sources = src.sources || {}
25+
$: img = src.img || {}
26+
$: if (len(img)) {
27+
const { lqip } = img
28+
background = lqip ? lqipToBackground(lqip) : undefined
4729
}
4830
</script>
4931

50-
{#if image.src}
51-
<picture>
52-
{#each sources as { format, srcset }}
53-
<source type="image/{format}" {srcset} {sizes} />
54-
{/each}
55-
<!-- svelte-ignore a11y-missing-attribute -->
32+
{#if len(img)}
33+
<Picture {sources} {sizes}>
34+
<!-- svelte-ignore a11y-missing-attribute a11y-no-noninteractive-element-interactions -->
5635
<img
5736
{loading}
5837
{decoding}
59-
{sizes}
60-
width={width || image.width || undefined}
61-
height={height || image.height || undefined}
62-
style:background={image.lqip}
38+
width={width || img.w || undefined}
39+
height={height || img.h || undefined}
40+
style:background
6341
bind:this={ref}
6442
on:click
6543
on:load
6644
{...$$restProps}
67-
srcset={image.srcset}
68-
src={image.src}
45+
src={img.src}
6946
/>
70-
</picture>
47+
</Picture>
7148
{/if}

src/lib/index.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import Img from './SvelteImg.svelte'
22
import FxReveal from './FxReveal.svelte'
33
import FxParallax from './FxParallax.svelte'
4+
import { observe } from './utils.js'
45

56
export default Img
6-
export { FxReveal, FxParallax }
7+
export { FxReveal, FxParallax, observe }

src/lib/utils.js

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,12 @@ function observe(node) {
1919
}
2020
}
2121

22-
export { observe }
22+
function len(obj) {
23+
return obj && Object.keys(obj).length
24+
}
25+
26+
function lqipToBackground(lqip) {
27+
return lqip[0] === '#' ? lqip : `url(data:image/webp;base64,${lqip}) no-repeat center/cover`
28+
}
29+
30+
export { observe, len, lqipToBackground }

src/lib/vite.js

Lines changed: 30 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,40 @@
1-
// @ts-nocheck
1+
import { imagetools, pictureFormat } from 'vite-imagetools'
22

3-
import { imagetools } from 'vite-imagetools'
4-
import { resize, format, setMetadata, metadataFormat, resolveConfigs } from 'imagetools-core'
5-
6-
function lqip(cfg, ctx) {
7-
if (cfg.lqip) {
8-
const r = resize({ width: cfg.lqip }, ctx)
9-
const f = format({ format: 'webp', quality: '20' }, ctx)
10-
return async function (image) {
11-
const img = f(r(image))
12-
const buffer = await img.toBuffer()
13-
setMetadata(img, 'base64', `data:image/webp;base64,${buffer.toString('base64')}`)
14-
return img
3+
function run(cfg) {
4+
return async function (metadatas) {
5+
const pic = pictureFormat()(metadatas)
6+
const lqip = (cfg && parseInt(cfg)) ?? 16
7+
if (lqip) {
8+
const { image } = metadatas.find((i) => i.src === pic.img.src)
9+
let data
10+
if (lqip > 1) {
11+
const buf = await image.resize({ width: lqip }).toFormat('webp', { quality: 20 }).toBuffer()
12+
data = buf.toString('base64')
13+
} else {
14+
const { dominant } = await image.stats()
15+
const { r, g, b } = dominant
16+
data = `#${((1 << 24) | (r << 16) | (g << 8) | b).toString(16).slice(1)}`
17+
}
18+
pic.img.lqip = data
1519
}
20+
return pic
1621
}
1722
}
1823

19-
function main(overrides = {}) {
24+
function main({
25+
runDefaultDirectives = new URLSearchParams('w=480;1024;1920&format=avif;webp;jpg'),
26+
defaultDirectives = new URLSearchParams(),
27+
exclude = '{build,dist,node_modules}/**/*',
28+
extendOutputFormats = (i) => i, //noop
29+
...rest
30+
} = {}) {
2031
return imagetools({
2132
defaultDirectives: (url) =>
22-
url.searchParams.has('run')
23-
? new URLSearchParams('width=480;1024;1920&format=avif;webp;jpg')
24-
: new URLSearchParams(''),
25-
extendTransforms: (builtins) => [...builtins, lqip],
26-
extendOutputFormats: (builtinOutputFormats) => ({
27-
...builtinOutputFormats,
28-
run: () => metadataFormat(['format', 'src', 'width', 'height', 'base64'])
29-
}),
30-
resolveConfigs: (e, f) => {
31-
if (e.findIndex((i) => i[0] === 'run') > -1) {
32-
const idx = e.findIndex((i) => i[0] === 'lqip')
33-
const lqip = idx > -1 ? parseInt(e.splice(idx, 1)[0][1][0]) : 16
34-
const merge = new Map()
35-
for (const [key, val] of e) merge.set(key, val)
36-
return [...resolveConfigs([...merge], f), ...(lqip ? [{ lqip }] : [])]
37-
}
38-
return resolveConfigs(e, f)
39-
},
40-
...overrides
33+
url.searchParams.get('as') === 'run' ? runDefaultDirectives : defaultDirectives,
34+
extendOutputFormats: (builtins) => ({ ...extendOutputFormats(builtins), run }),
35+
exclude,
36+
...rest
4137
})
4238
}
4339

44-
export { main as imagetools, lqip }
40+
export { main as imagetools, run }

0 commit comments

Comments
 (0)