astro-tips.dev icon indicating copy to clipboard operation
astro-tips.dev copied to clipboard

Image Placeholders

Open OliverSpeir opened this issue 1 year ago • 5 comments

Some inspiration:

  • https://imgit.dev/guide/integrations/astro

  • https://unpic.pics/img/astro/

  • https://github.com/Princesseuh/erika.florist/blob/main/src%2FimageService.ts#L53-L76

  • https://discord.com/channels/830184174198718474/830184175176122389/1227190628828446730

  • CleanShot_2024-04-03_at_01 32 53

OliverSpeir avatar Apr 03 '24 07:04 OliverSpeir

https://discord.com/channels/830184174198718474/846469231176056853/1277731809467175014

---
import { getPixels } from "@unpic/pixels";
import type { UnresolvedImageTransform } from "astro";
import type { HTMLAttributes } from "astro/types";
import { getImage, type LocalImageProps } from "astro:assets";
import { readFile } from "node:fs/promises";
import sharp from "sharp";
import { rgbaToThumbHash, thumbHashToDataURL } from "thumbhash";

type Props = Omit<LocalImageProps, "src"> & {
  src: ImageMetadata;
};

const props = Astro.props;

if (typeof props.width === "string") {
  props.width = parseInt(props.width);
}

if (typeof props.height === "string") {
  props.height = parseInt(props.height);
}

const image = await getImage(props);

const opts: UnresolvedImageTransform = {
  src: Astro.props.src,
  format: "png",
};

const [w, h] = [image.attributes.width, image.attributes.height];

// if pic is 1:1 or wider than tall
if (w >= h) {
  opts.width = 100;
}

// if pic is 1:1 or taller than wide
if (h >= w) {
  opts.height = 100;
}
let buf = import.meta.env.PROD
  ? await sharp(await readFile(`./dist/${props.src.src}`))
    .resize({
      width: 100,
      height: 100,
      fit: "inside",
      withoutEnlargement: true,
    })
    .png()
    .modulate({ saturation: 1.2 })
    .toBuffer()
  : await getImage(opts)
    .then(_ => new URL(_.src, Astro.url));

const { height, width, data } = await getPixels(buf);
const hash = rgbaToThumbHash(width, height, data);
const src = thumbHashToDataURL(hash);

const additionalAttributes: HTMLAttributes<"img"> = {};
if (image.srcSet.values.length > 0) {
  additionalAttributes.srcset = image.srcSet.attribute;
}
---

<img
  src={src}
  data-src={image.src}
  data-srcset={additionalAttributes.srcset}
  {...additionalAttributes}
  {...image.attributes}
/>

<script>
import { lazyLoad } from "unlazy";

lazyLoad();
</script>

OliverSpeir avatar Sep 05 '24 11:09 OliverSpeir

https://www.robinosborne.co.uk/2018/01/05/image-placeholders-do-it-right-or-dont-do-it-at-all-please/

OliverSpeir avatar Sep 05 '24 11:09 OliverSpeir

haha.. just came here to post that same article!

sarah11918 avatar Sep 05 '24 11:09 sarah11918

https://www.robinosborne.co.uk/2018/01/05/image-placeholders-do-it-right-or-dont-do-it-at-all-please/

Not sure I agree with this honestly

The main point of the article I got was don't do blurry LQIP (low quality image placeholders) instead just do a lower quality (but still recognizable) version of the original image

OliverSpeir avatar Sep 07 '24 16:09 OliverSpeir

Thanks for all the information and context.. I need to block myself some time to go over this.

alexanderniebuhr avatar Sep 09 '24 12:09 alexanderniebuhr