feat: improved image optimization

This commit is contained in:
Cory Dransfeldt 2024-02-01 10:53:34 -08:00
parent 9e118c83db
commit 0534bc5607
No known key found for this signature in database
10 changed files with 70 additions and 104 deletions

View file

@ -1,69 +1,64 @@
import outdent from 'outdent'
import Image from '@11ty/eleventy-img' import Image from '@11ty/eleventy-img'
import path from 'path'
import htmlmin from 'html-minifier-terser'
const stringifyAttributes = (attributeMap) => {
return Object.entries(attributeMap)
.map(([attribute, value]) => {
if (typeof value === 'undefined') return '';
return `${attribute}="${value}"`;
})
.join(' ');
};
export const img = async ( export const img = async (
src, src,
alt, alt = '',
className = undefined, className,
loading = 'lazy', loading = 'lazy',
widths = [75, 150, 300, 600, 900, 1200], maxWidth = 1248,
formats = ['webp', 'jpeg'], sizes = '90vw',
sizes = '100vw' formats = ['avif', 'webp', 'jpeg']
) => { ) => {
if (!src) return; const widths = [320, 570, 880, 1024, 1248];
const imageMetadata = await Image(src, { const metadata = await Image(src, {
widths: [...widths, null], widths: widths.filter((width) => width <= maxWidth),
formats: [...formats, null], formats: [...formats],
outputDir: './_site/assets/img/cache/', outputDir: './_site/assets/img/cache/',
urlPath: '/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 stringifyAttributes = (attributeMap) => { const lowsrc = metadata.jpeg[metadata.jpeg.length - 1];
return Object.entries(attributeMap)
.map(([attribute, value]) => {
if (typeof value === 'undefined') return ''
return `${attribute}="${value}"`
})
.join(' ')
}
const sourceHtmlString = Object.values(imageMetadata) const imageSources = Object.values(metadata)
.map((images) => { .map((imageFormat) => {
const { sourceType } = images[0] return ` <source type="${
const sourceAttributes = stringifyAttributes({ imageFormat[0].sourceType
type: sourceType, }" srcset="${imageFormat
srcset: images.map((image) => image.srcset).join(', '), .map((entry) => entry.srcset)
sizes, .join(', ')}" sizes="${sizes}">`;
})
return `<source ${sourceAttributes}>`
}) })
.join('\n') .join('\n');
const getLargestImage = (format) => { const imgageAttributes = stringifyAttributes({
const images = imageMetadata[format] src: lowsrc.url,
return images[images.length - 1] width: lowsrc.width,
} height: lowsrc.height,
const largestUnoptimizedImg = getLargestImage(formats[0])
const imgAttributes = stringifyAttributes({
src: largestUnoptimizedImg.url,
width: largestUnoptimizedImg.width,
height: largestUnoptimizedImg.height,
alt, alt,
class: className,
loading, loading,
decoding: 'async', decoding: 'async',
}) });
const imgHtmlString = `<img ${imgAttributes}>` const imageElement = `<picture>
const pictureAttributes = stringifyAttributes({ ${imageSources}
class: className, <img ${imgageAttributes} />
}) </picture>`;
const picture = `<picture ${pictureAttributes}> return htmlmin.minify(imageElement, { collapseWhitespace: true });
${sourceHtmlString} };
${imgHtmlString}
</picture>`
return outdent`${picture}`
}

11
package-lock.json generated
View file

@ -1,12 +1,12 @@
{ {
"name": "coryd.dev", "name": "coryd.dev",
"version": "4.14.0", "version": "5.0.4",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "coryd.dev", "name": "coryd.dev",
"version": "4.14.0", "version": "5.0.4",
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"@remy/webmention": "^1.5.0", "@remy/webmention": "^1.5.0",
@ -32,7 +32,6 @@
"markdown-it": "^14.0.0", "markdown-it": "^14.0.0",
"markdown-it-anchor": "^8.4.1", "markdown-it-anchor": "^8.4.1",
"markdown-it-footnote": "^4.0.0", "markdown-it-footnote": "^4.0.0",
"outdent": "^0.8.0",
"sanitize-html": "^2.11.0", "sanitize-html": "^2.11.0",
"slugify": "^1.6.6", "slugify": "^1.6.6",
"writing-stats": "^1.0.6" "writing-stats": "^1.0.6"
@ -3598,12 +3597,6 @@
"resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz", "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz",
"integrity": "sha512-iotkTvxc+TwOm5Ieim8VnSNvCDjCK9S8G3scJ50ZthspSxa7jx50jkhYduuAtAjvfDUwSgOwf8+If99AlOEhyw==" "integrity": "sha512-iotkTvxc+TwOm5Ieim8VnSNvCDjCK9S8G3scJ50ZthspSxa7jx50jkhYduuAtAjvfDUwSgOwf8+If99AlOEhyw=="
}, },
"node_modules/outdent": {
"version": "0.8.0",
"resolved": "https://registry.npmjs.org/outdent/-/outdent-0.8.0.tgz",
"integrity": "sha512-KiOAIsdpUTcAXuykya5fnVVT+/5uS0Q1mrkRHcF89tpieSmY33O/tmc54CqwA+bfhbtEfZUNLHaPUiB9X3jt1A==",
"dev": true
},
"node_modules/p-finally": { "node_modules/p-finally": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz",

View file

@ -1,6 +1,6 @@
{ {
"name": "coryd.dev", "name": "coryd.dev",
"version": "5.0.4", "version": "5.1.4",
"description": "The source for my personal site. Built using 11ty and hosted on Netlify.", "description": "The source for my personal site. Built using 11ty and hosted on Netlify.",
"type": "module", "type": "module",
"scripts": { "scripts": {
@ -43,7 +43,6 @@
"markdown-it": "^14.0.0", "markdown-it": "^14.0.0",
"markdown-it-anchor": "^8.4.1", "markdown-it-anchor": "^8.4.1",
"markdown-it-footnote": "^4.0.0", "markdown-it-footnote": "^4.0.0",
"outdent": "^0.8.0",
"sanitize-html": "^2.11.0", "sanitize-html": "^2.11.0",
"slugify": "^1.6.6", "slugify": "^1.6.6",
"writing-stats": "^1.0.6" "writing-stats": "^1.0.6"

View file

@ -8,13 +8,13 @@ layout: default
{% endcapture %} {% endcapture %}
<style>{{ css }}</style> <style>{{ css }}</style>
{{ content }} {{ content }}
{% render "partials/now/media-grid.liquid", data:artists, icon: "microphone-2", title: "Artists", shape: "square", count: 8, loading: 'eager' %} {% render "partials/now/media-grid.liquid", data:artists, icon: "microphone-2", title: "Artists", shape: "square", count: 8, loading: 'eager', imageMaxWidth: 320 %}
{% render "partials/now/media-grid.liquid", data:albums, icon: "vinyl", title: "Albums", shape: "square", count: 8 %} {% render "partials/now/media-grid.liquid", data:albums, icon: "vinyl", title: "Albums", shape: "square", count: 8, imageMaxWidth: 320 %}
{% render "partials/now/albumReleases.liquid", albumReleases:albumReleases %} {% render "partials/now/albumReleases.liquid", albumReleases:albumReleases %}
{% render "partials/now/media-grid.liquid", data:books, icon: "books", title: "Books", shape: "vertical", count: 6 %} {% render "partials/now/media-grid.liquid", data:books, icon: "books", title: "Books", shape: "vertical", count: 6, imageMaxWidth: 320 %}
{% render "partials/now/links.liquid", links:links %} {% render "partials/now/links.liquid", links:links %}
{% render "partials/now/media-grid.liquid", data:movies, icon: "movie", title: "Movies", shape: "vertical", count: 6 %} {% render "partials/now/media-grid.liquid", data:movies, icon: "movie", title: "Movies", shape: "vertical", count: 6, imageMaxWidth: 320 %}
{% render "partials/now/media-grid.liquid", data:tv, icon: "device-tv", title: "TV", shape: "vertical", count: 6 %} {% render "partials/now/media-grid.liquid", data:tv, icon: "device-tv", title: "TV", shape: "vertical", count: 6, imageMaxWidth: 320 %}
<p class="now__explainer text-center"> <p class="now__explainer text-center">
This is a <a href="https://nownownow.com/about">now page</a>, and if you have your own site, <a href="https://nownownow.com/about">you should make one too</a>. This is a <a href="https://nownownow.com/about">now page</a>, and if you have your own site, <a href="https://nownownow.com/about">you should make one too</a>.
</p> </p>

View file

@ -37,7 +37,11 @@
'lazy' 'lazy'
{%- endif -%} {%- endif -%}
{%- endcapture -%} {%- endcapture -%}
{% image item.image, alt, 'item__image', loadingStrategy %} {% if imageMaxWidth %}
{% image item.image, alt, '', loadingStrategy, imageMaxWidth %}
{% else %}
{% image item.image, alt, '', loadingStrategy %}
{% endif %}
</div> </div>
</a> </a>
{% endfor %} {% endfor %}

View file

@ -403,17 +403,8 @@ li {
.image__banner { .image__banner {
border: 1px solid var(--accent-color); border: 1px solid var(--accent-color);
border-radius: var(--rounded-lg); border-radius: var(--rounded-lg);
overflow: hidden;
}
.image__banner,
.image__banner > * {
display: block;
width: 100%;
}
.image__banner > * {
height: auto; height: auto;
width: 100%;
} }
/* pages */ /* pages */

View file

@ -4,30 +4,18 @@
.avatar__wrapper { .avatar__wrapper {
width: 100%; width: 100%;
margin-bottom: 1.5rem; justify-content: center;
} }
.avatar__wrapper .avatar__wrapper--interior { .avatar__wrapper img {
border-radius: var(--rounded-full);
border: 1px solid var(--accent-color);
width: var(--avatar-size); width: var(--avatar-size);
height: var(--avatar-size); height: var(--avatar-size);
border: 1px solid var(--accent-color);
border-radius: var(--rounded-full);
display: flex;
padding: 1rem; padding: 1rem;
background-color: var(--color-lightest); background-color: var(--color-lightest);
} }
.avatar__wrapper,
.avatar__wrapper .avatar__wrapper--interior {
justify-content: center;
overflow: hidden;
}
.avatar__wrapper .avatar__wrapper--interior > * {
width: 100%;
height: 100%;
}
@media screen and (min-width: 768px) { @media screen and (min-width: 768px) {
:root { :root {
--avatar-size: 24rem; --avatar-size: 24rem;

View file

@ -76,8 +76,7 @@
font-weight: 700; font-weight: 700;
} }
.media__grid .item__image, .media__grid img {
.media__grid .item__image > * {
width: 100%; width: 100%;
height: 100%; height: 100%;
} }

View file

@ -9,10 +9,8 @@ image: /assets/img/ogi/about.jpg
{% endcapture %} {% endcapture %}
<style>{{ css }}</style> <style>{{ css }}</style>
<div class="avatar__wrapper flex--centered"> <div class="avatar__wrapper flex--centered">
<div class="avatar__wrapper--interior"> {% capture about_alt %}{{ meta.siteName }} - image by David Neal / @reverentgeek{% endcapture %}
{% capture about_alt %}{{ meta.siteName }} - image by David Neal / @reverentgeek{% endcapture %} {% image './src/assets/img/avatar.webp', about_alt %}
{% image './src/assets/img/avatar.webp', about_alt %}
</div>
</div> </div>
<h2 class="page__header text-center">Hi, I'm Cory</h2> <h2 class="page__header text-center">Hi, I'm Cory</h2>

View file

@ -14,9 +14,8 @@ tags: ['books', 'music', 'development', 'Eleventy']
<li><a href="https://www.last.fm/user/coryd_">I listened to a bunch of music</a></li> <li><a href="https://www.last.fm/user/coryd_">I listened to a bunch of music</a></li>
<li><a href="https://trakt.tv/users/cdransf">I watched a bunch of movies and TV</a>, but picking favorites feels weird when so much of that consisted of catching up on "classics" and things I'd either ignored or never seen.</li> <li><a href="https://trakt.tv/users/cdransf">I watched a bunch of movies and TV</a>, but picking favorites feels weird when so much of that consisted of catching up on "classics" and things I'd either ignored or never seen.</li>
</ul> </ul>
{% render "partials/now/media-grid.liquid", data:roundups['2023'].books, icon: "books", title: "Favorite books", shape: "vertical", count: 6, embeddedStyles: true, imageMaxWidth: 320 %}
{% render "partials/now/media-grid.liquid", data:roundups['2023'].books, icon: "books", title: "Favorite books", shape: "vertical", count: 6, embeddedStyles: true %} {% render "partials/now/media-grid.liquid", data:roundups['2023'].albums, icon: "vinyl", title: "Favorite albums", shape: "square", count: 8, imageMaxWidth: 320 %}
{% render "partials/now/media-grid.liquid", data:roundups['2023'].albums, icon: "vinyl", title: "Favorite albums", shape: "square", count: 8 %}
<p><strong><a href="https://coryd.dev">I wrote some things:</a></strong></p> <p><strong><a href="https://coryd.dev">I wrote some things:</a></strong></p>
<ul> <ul>