Has anyone implemented a clean robust srcset feature for Hakyll or
understand how it's supposed to work? I tried and my version is hacky
and fragile.
The <img> attributes 'srcset'/'sizes' in theory let you serve
statically different-size images to desktop and mobile users, reducing
bandwidth to easily a quarter if you serve a screen-sized image to
both:
https://developer.mozilla.org/en-US/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images
As one of the main missing optimizations for
gwern.net and potentially
very useful on image-heavy pages like
https://www.gwern.net/Faces /
https://www.gwern.net/TWDNE /
https://www.gwern.net/Red , and looking
fairly straightforward in the examples, I thought I'd implement it to
complement my existing lazy-loading optimization: with both on, images
are loaded only when necessary and only at the necessary size.
I could implement it as another Pandoc rewrite pass: for every Image
element, check that a smaller version exists, if not create it, and
then inline an appropriate srcset/sizes declaration to an updated
Image element; just another `Inline -> IO Inline` which can be
`walkM`ed over. As usual, the devil was in the details. It's hard to
simply create an image in _site because the static file phase runs
last so the directories don't exist (the easiest way is to create it
in the original directory tree, but now you have to make your VCS
ignore it or double the number of images tracked); the srcset/sizes
require the width of the original image, so you have to query both the
existence and size of the original (fortunately, I already have that
for adding height/width for layout hinting); you might as well lossily
optimize it or else it'll be twice as big as necessary; and then the
biggest problem proved to be getting the damn srcset/sizes to fire at
all! Watching in dev tools, I could see they weren't fetching the
smaller version no matter how many tutorials or docs I checked.
Eventually, I found the magic incantation '(max-width: 30em) 80vw,'
*seems* to be HTML valid, makes it fire in Chromium mobile simulator
but not in Chromium desktop mode, and Google Pagespeed Insights seems
to start fetching the small versions... but I'm not confident in this
at all.
My version (which you can see in action - for now - at the 3 links
above) goes like this:
-- Example: Image ("",["full-width"],[]) [Str "..."]
("/images/gan/thiswaifudoesnotexist.png","fig:")
-- type Text.Pandoc.Definition.Attr = (T.Text, [T.Text], [(T.Text, T.Text)])
imageSrcset :: Inline -> IO Inline
imageSrcset (Image (c, t, pairs) inlines (target, title)) =
do let ext = takeExtension $ T.unpack target
let target' = T.unpack target
(_,w) <- imageMagick $ tail target'
let smallerPath = (tail target')++"-768px"++ext
notExist <- fmap not $ doesFileExist smallerPath
when notExist $ do
(status,_,bs) <- runShellCommand "./" Nothing "convert"
[tail target', "-resize", "768x768", (tail target')++"-768px"++ext]
case status of
ExitFailure _ -> error $ show status ++ show bs
_ -> do if ext == ".png" then -- lossily optimize using
my pngnq/mozjpeg scripts:
void $ runShellCommand "./" Nothing
"/home/gwern/bin/bin/png" [tail target']
else
void $ runShellCommand "./" Nothing
"/home/gwern/bin/bin/compressJpg" [tail target']
void $ print ("Created smaller image: " ++ smallerPath)
let srcset = T.pack (target'++"-768px"++ext++" 768w, " ++
target'++" "++w++"w")
return $ Image (c, t, pairs++[("srcset", srcset), ("sizes",
T.pack ("(max-width: 30em) 80vw, "++w++"px"))])
inlines (target, title)
imageSrcset x = return x
Has anyone else tried this?
--
gwern