feat: check if image exists before caching optimized version
This commit is contained in:
parent
cb6b5dbd8e
commit
f0f1c0aaab
9 changed files with 101 additions and 55 deletions
|
@ -16,49 +16,72 @@ export const img = async (
|
|||
alt = '',
|
||||
className,
|
||||
loading = 'lazy',
|
||||
shape = 'square',
|
||||
icon,
|
||||
maxWidth = 1248,
|
||||
sizes = '90vw',
|
||||
formats = ['avif', 'webp', 'jpeg']
|
||||
formats = ['avif', 'webp', 'jpeg'],
|
||||
) => {
|
||||
const widths = [320, 570, 880, 1024, 1248];
|
||||
const metadata = await Image(src, {
|
||||
widths: widths.filter((width) => width <= maxWidth),
|
||||
formats: [...formats],
|
||||
outputDir: './_site/assets/img/cache/',
|
||||
urlPath: '/assets/img/cache/',
|
||||
filenameFormat: (id, src, width, format, options) => {
|
||||
const extension = path.extname(src);
|
||||
const name = path.basename(src, extension);
|
||||
return `${name}-${width}w.${format}`;
|
||||
},
|
||||
});
|
||||
const isLocal = src.includes('src/assets')
|
||||
const imageExists = async () => {
|
||||
const isOk = await fetch(src, { method: 'HEAD' }).then(res => res.ok).catch(() => false)
|
||||
return isOk
|
||||
}
|
||||
const generateImage = async () => {
|
||||
const widths = [320, 570, 880, 1024, 1248];
|
||||
const metadata = await Image(src, {
|
||||
widths: widths.filter((width) => width <= maxWidth),
|
||||
formats: [...formats],
|
||||
outputDir: './_site/assets/img/cache/',
|
||||
urlPath: '/assets/img/cache/',
|
||||
filenameFormat: (id, src, width, format) => {
|
||||
const extension = path.extname(src);
|
||||
const name = path.basename(src, extension);
|
||||
return `${name}-${width}w.${format}`;
|
||||
},
|
||||
});
|
||||
|
||||
const lowsrc = metadata.jpeg[metadata.jpeg.length - 1];
|
||||
const lowsrc = metadata.jpeg[metadata.jpeg.length - 1];
|
||||
|
||||
const imageSources = Object.values(metadata)
|
||||
.map((imageFormat) => {
|
||||
return ` <source type="${
|
||||
imageFormat[0].sourceType
|
||||
}" srcset="${imageFormat
|
||||
.map((entry) => entry.srcset)
|
||||
.join(', ')}" sizes="${sizes}">`;
|
||||
const imageSources = Object.values(metadata)
|
||||
.map((imageFormat) => {
|
||||
return ` <source type="${
|
||||
imageFormat[0].sourceType
|
||||
}" srcset="${imageFormat
|
||||
.map((entry) => entry.srcset)
|
||||
.join(', ')}" sizes="${sizes}">`;
|
||||
})
|
||||
.join('\n');
|
||||
|
||||
const imgageAttributes = stringifyAttributes({
|
||||
src: lowsrc.url,
|
||||
width: lowsrc.width,
|
||||
height: lowsrc.height,
|
||||
alt,
|
||||
class: className,
|
||||
loading,
|
||||
decoding: 'async',
|
||||
});
|
||||
|
||||
const imageElement = `<picture>
|
||||
${imageSources}
|
||||
<img ${imgageAttributes} />
|
||||
</picture>`;
|
||||
|
||||
return htmlmin.minify(imageElement, { collapseWhitespace: true });
|
||||
}
|
||||
const generatePlaceholder = async () => {
|
||||
const placeholderElement = `<div class="flex--centered image__placeholder ${shape}">
|
||||
${icon}
|
||||
</div>`
|
||||
|
||||
return htmlmin.minify(placeholderElement, { collapseWhitespace: true });
|
||||
}
|
||||
if (isLocal) return await generateImage();
|
||||
if (!isLocal) {
|
||||
return await imageExists().then(async exists => {
|
||||
if (exists) return await generateImage();
|
||||
if (!exists) return await generatePlaceholder();
|
||||
})
|
||||
.join('\n');
|
||||
|
||||
const imgageAttributes = stringifyAttributes({
|
||||
src: lowsrc.url,
|
||||
width: lowsrc.width,
|
||||
height: lowsrc.height,
|
||||
alt,
|
||||
class: className,
|
||||
loading,
|
||||
decoding: 'async',
|
||||
});
|
||||
|
||||
const imageElement = `<picture>
|
||||
${imageSources}
|
||||
<img ${imgageAttributes} />
|
||||
</picture>`;
|
||||
|
||||
return htmlmin.minify(imageElement, { collapseWhitespace: true });
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "coryd.dev",
|
||||
"version": "5.2.10",
|
||||
"version": "5.3.10",
|
||||
"description": "The source for my personal site. Built using 11ty and hosted on Netlify.",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
|
|
|
@ -7,7 +7,7 @@ export default {
|
|||
url: 'https://app.thestorygraph.com/books/9946c834-81f1-4c7f-b7d1-30a804e9874f',
|
||||
},
|
||||
{
|
||||
alt: 'Where Are Your Boys Tonigh?',
|
||||
alt: 'Where Are Your Boys Tonight?',
|
||||
author: 'Chris Payne',
|
||||
image: 'https://cd-books.b-cdn.net/vajp3jxy6kee5ka2ymbvjc2fqkvf',
|
||||
url: 'https://app.thestorygraph.com/books/f074d4e3-a9fc-42af-889e-54697a1fece0',
|
||||
|
|
|
@ -37,10 +37,11 @@
|
|||
'lazy'
|
||||
{%- endif -%}
|
||||
{%- endcapture -%}
|
||||
{% capture fallbackIcon %}{% tablericon icon alt %}{% endcapture %}
|
||||
{% if imageMaxWidth %}
|
||||
{% image item.image, alt, '', loadingStrategy, imageMaxWidth %}
|
||||
{% image item.image, alt, '', loadingStrategy, shape, fallbackIcon, imageMaxWidth %}
|
||||
{% else %}
|
||||
{% image item.image, alt, '', loadingStrategy %}
|
||||
{% image item.image, alt, '', loadingStrategy, shape, fallbackIcon, 1248 %}
|
||||
{% endif %}
|
||||
</div>
|
||||
</a>
|
||||
|
|
|
@ -6,12 +6,9 @@
|
|||
<div class="comment__wrapper--interior flex--centered">
|
||||
<a href={{mention.url}}>
|
||||
<div class="avatar__wrapper flex--centered">
|
||||
<img
|
||||
src="{{ mention.author.photo }}"
|
||||
alt="{{ mention.author.name | escape }}"
|
||||
class="avatar__image"
|
||||
loading="lazy"
|
||||
onerror="this.onerror=null; this.src='/assets/img/icons/user.webp'" />
|
||||
{% capture authorAlt %}{{ mention.author.name | escape }}{% endcapture %}
|
||||
{% capture fallbackIcon %}{% tablericon "user" authorAlt %}{% endcapture %}
|
||||
{% image mention.author.photo, authorAlt, 'avatar__image', 'lazy', 'rounded', fallbackIcon %}
|
||||
</div>
|
||||
</a>
|
||||
<div class="comment">
|
||||
|
|
|
@ -11,12 +11,9 @@
|
|||
{% for mention in mentions[type] %}
|
||||
<a href={{mention.url}}>
|
||||
<div class="avatar__wrapper flex--centered">
|
||||
<img
|
||||
src="{{ mention.author.photo }}"
|
||||
alt="{{ mention.author.name | escape }}"
|
||||
class="avatar__image"
|
||||
loading="lazy"
|
||||
onerror="this.onerror=null; this.src='/assets/img/icons/user.webp'" />
|
||||
{% capture authorAlt %}{{ mention.author.name | escape }}{% endcapture %}
|
||||
{% capture fallbackIcon %}{% tablericon "user" authorAlt %}{% endcapture %}
|
||||
{% image mention.author.photo, authorAlt, 'avatar__image', 'lazy', 'rounded', fallbackIcon %}
|
||||
</div>
|
||||
</a>
|
||||
{% endfor %}
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 610 B |
|
@ -407,6 +407,33 @@ li {
|
|||
width: 100%;
|
||||
}
|
||||
|
||||
.image__placeholder {
|
||||
background-color: var(--color-darkest);
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.image__placeholder > svg {
|
||||
stroke: var(--color-lightest) !important;
|
||||
stroke-width: var(--stroke-width-bold);
|
||||
height: 1.75rem;
|
||||
width: 1.75rem;
|
||||
}
|
||||
|
||||
.image__placeholder.square,
|
||||
.image__placeholder.round {
|
||||
aspect-ratio: 1/1;
|
||||
}
|
||||
|
||||
.image__placeholder.round {
|
||||
border-radius: var(--rounded-full);
|
||||
}
|
||||
|
||||
.image__placeholder.vertical {
|
||||
aspect-ratio: 2/3;
|
||||
}
|
||||
|
||||
/* pages */
|
||||
.page__header {
|
||||
font-size: var(--font-size-2xl);
|
||||
|
|
|
@ -94,6 +94,7 @@
|
|||
|
||||
.webmentions .interaction .avatar__wrapper img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: var(--rounded-full);
|
||||
}
|
||||
|
||||
|
|
Reference in a new issue