feat: improved image optimization
This commit is contained in:
parent
9e118c83db
commit
0534bc5607
10 changed files with 70 additions and 104 deletions
|
@ -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
11
package-lock.json
generated
|
@ -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",
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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 %}
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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%;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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>
|
||||||
|
|
||||||
|
|
|
@ -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>
|
||||||
|
|
Reference in a new issue