Giter Club home page Giter Club logo

kirby-blurry-placeholder's Introduction

Note

This plugin is not actively maintained anymore. I recommend to use Kirby ThumbHash in combination with unlazy (an evolved Loadeer.js) for a similar effect. It's the successor of this plugin and provides a more modern and flexible approach to lazy loading images.

Thank you for your support and feedback over the years, especially since it was my first plugin for Kirby!

Example of ThumHash and unlazy

Inline a thumbnail of your image:

<img
  src="<?= $image->thumbhashUri() ?>"
  loading="lazy"
  data-srcset="<?= $image->srcset() ?>"
  data-sizes="auto"
  width="<?= $image->width() ?>"
  height="<?= $image->height() ?>"
>

Lazy load all images with the loading="lazy" attribute:

import { lazyLoad } from 'unlazy'

// Apply lazy loading for all images by the selector `img[loading="lazy"]`
lazyLoad()

Preview of Kirby blurry placeholder plugin

Kirby Blurry Placeholder

This plugin implements progressive image loading, providing a better user experience. Tiny thumbnails which inherit the aspect ratio of their source image are combined with a blurry effect for a better placeholder than solid colors, without sacrificing payload.

How it works:

  1. An inline, URI-encoded SVG fills the src attribute of a given image element. The blurred image is wrapped in an SVG to avoid rasterizing the filter.
  2. The large images are then only requested when they are within the viewport.

Key Features

Requirements

  • Kirby 3 or
  • Kirby 4

Installation

Download

Download and copy this repository to /site/plugins/kirby-blurry-placeholder.

Git Submodule

git submodule add https://github.com/johannschopplich/kirby-blurry-placeholder.git site/plugins/kirby-blurry-placeholder

Composer

composer require johannschopplich/kirby-blurry-placeholder

Usage

As Kirby Image Block

Each Kirby website is tailored to its own unique use case. Thus, this plugin won't add a Kirby block by default. Instead, take a look at the provided image block example to get an idea of how to implement blurry placeholders within blocks.

Of course, you can just copy the block into your site/snippets/blocks folder of your current Kirby project, use it as is, or adapt it to your needs!

As File Method

$file->placeholderUri() creates and returns the URI-encoded SVG placeholder.

<!-- Using the `placeholderUri` file method for the `src` attribute -->
<img
  src="<?= $image->placeholderUri() ?>"
  data-src="<?= $image->url() ?>"
  data-lazyload
  alt="<?= $image->alt() ?>"
/>

As KirbyTag

This plugin provides a (blurryimage: …) KirbyTag built upon Kirby's core (image: …) tag. All of Kirby's image tag options are inferred and thus available for the custom tag as well.

The (blurryimage: …) tag:

  • Encodes a blurry image placeholder as URI in the src attribute.
  • Sets the original image's URL as data-src or a set of responsive images as data-srcset.
  • Adds a data-lazyload attribute for selection by the lazy loading library.

Example use within a KirbyText field:

(blurryimage: myimage.jpg)
(blurryimage: myimage.jpg link: https://example.com)
(blurryimage: myimage.jpg class: is-poster)

If you have enabled srcset's in the options, the KirbyTag syntax stays the same. Just the output changes.

Lazy Loading in the Frontend

To lazily load the images once they become visible in the viewport, a JavaScript library is necessary. 🦌 Loadeer.js is written with this Kirby plugin in mind. In a nutshell, it's a tiny, performant, SEO-friendly lazy loading library and can be used with or without a build step if you don't have a frontend asset build chain.

Without Build Step & Auto Initialization

Simply load it from a CDN:

<script
  src="https://unpkg.com/[email protected]/dist/loadeer.umd.js"
  defer
  init
></script>
  • The defer attribute makes the script execute after HTML content is parsed.
  • The init attribute tells Loadeer.js to automatically initialize and watch all elements that have a data-lazyload attribute.

Import As ES Module

You can use the ES module build by installing the loadeer npm package:

import Loadeer from "loadeer";

const instance = new Loadeer();
instance.observe();

Automatically Calculate the sizes Attribute

Loadeer.js supports setting the sizes attribute automatically, corresponding to the current size of your image. For this to work, the data-sizes attribute has to be set to auto. If you have srcset's enabled in your configuration, this is already done for you when using the (blurryimage: …) KirbyTag.

Use a Lazy Loader of Your Choice

Each parsed KirbyTag adds the data-lazyload attribute to the img element. Consequently, you can let a lazy loader of choice select these elements by passing [data-lazyload] as a selector.

Animating the Blur

🎨 Animating with the "Blur Down" Technique

⚠️ Disclaimer: Please avoid copying any code until reading this section. This is an experimental technique that comes with caveats (mostly performance issues).

When using Loadeer.js, we can target all lazy-loaded images with [data-lazyload] and refine this selection with [data-src] only to target the images that haven't been fully loaded yet.

img[data-lazyload][data-src] {
  filter: blur(150px);
  transform: scale(1.2);
}

We can then apply a transition to such properties.

/* Respect users choice for reduced motion */
@media (prefers-reduced-motion: no-preference) {
  img[data-lazyload] {
    transition: 1000ms cubic-bezier(0.86, 0.07, 0.07, 0.96);
    transition-property: filter, transform;
    /* Hint browser at change for better performance */
    will-change: filter, transform;
  }
}

As we are manually blurring the img element, whose src attribute is our generated SVG (so essentially the SVG is a child of our img element), we need to have a container that hides overflowing parts. For better performance, we also enforce GPU rendering by applying a transform declaration.

.img-container {
  overflow: hidden;
  /* Enforce GPU rendering */
  transform: translateZ(0);
}

The biggest caveat with this implementation is the transition of filter - users on lower-end devices will probably experience frame drops. Even using transform to enforce GPU rendering and using will-change as a last resort to hint at the transition does not always fix such issues. In the end, it heavily depends on what devices your end users are on, how big the painted image is on the site, and how many images your site features (that might all blur up at the same time).

So should you implement a "blur down" transition? Probably not. If you do, strongly consider the caveats. The best animation is the one that engages users, not the one that scares them.

Options

File Methods

Both $file->placeholder() and $file->placeholderUri() file methods support the following options passed as an associative array:

Option Type Default Description
ratio `float null` null The ratio of the image. If not set the original image's ratio will be used.
transparent `bool null` null Mark the image as transparent. If true, the generated blur will be cropped at the edges. If false, the generated blur will be extended at the edges. By default, the plugin will try to evaluate if the image contains an alpha channel.

Example:

$file->placeholderUri([
  'ratio' => 1.5,
  'transparent' => true
]);

Transparent Images

If you know that your image (e.g. a logo) is transparent, you can pass the transparent option to the placeholderUri() to circumvent artifacts at the edges of the generated blur:

<img src="<?= $image->placeholderUri(['transparent' => true]) ?>" />

When not explicitly specified, transparency is detected by evaluating the alpha channel of each pixel in the thumbnail image (by the given pixel target). If the image doesn't contain an alpha channel itself, an additional filter is added to the SVG placeholder to remove the alpha channel of the generated blur at the edges.

Image With Ratio

Pass the ratio option to the placeholderUri() method on the original image to generate a placeholder image with a specific ratio:

<?php $cropped = $original->crop(500, 400) ?>
<img
  src="<?= $original->placeholderUri(['ratio' => 5/4]) ?>"
  data-src="<?= $cropped->url() ?>"
  data-lazyload
  alt="<?= $original->alt() ?>"
/>

ℹ️ Kirby doesn't support file methods on cropped images, because the latter inherits the Kirby\Cms\FileVersion class.

Global

Option Default Description
pixel-target 60 Aim for a placeholder image of ~P pixels (w \* h = ~P).
kirbytag.srcset-preset null A preset passed to Kirby's srcset method when using the KirbyTag. If null (default), a src attribute will be rendered instead of srcset.
kirbytag.sizes auto Default for the data-sizes attribute if the KirbyTag works with srcset's.

All of the srcset options have to be wrapped in an array.

Options can be set in your config.php file:

return [
    'johannschopplich.blurry-placeholder' => [
        'pixel-target' => 60,
        'kirbytag' => [
            'srcset-preset' => 'article'
        ]
    ]
];

Placeholders in action

Note: GIF is slowed down so as to grasp visually how placeholders look before the image kicks in.

GIF showing plugin in action

Credits

License

MIT License © 2020-2023 Johann Schopplich

kirby-blurry-placeholder's People

Contributors

johannschopplich avatar luxuryluke avatar mayfinn avatar tobimori avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

kirby-blurry-placeholder's Issues

Division by zero Error (image inside Structure Field)

Hey, I´m getting the following error when using the plugin for images inside a structure field:

Whoops \ Exception \ ErrorException (E_WARNING):
Division by zero

it's in this line of site/plugins/kirby-blurry-placeholder/classes/KirbyExtended/BlurryPlaceholder.php:

$height = sqrt($pixelTarget / ($ratio ?? $file->ratio()));

What might be wrong here?

Unexpected end of file error during deployment

Hi there!

Thanks a lot for this plugin. During my local testing (using Lavarel Valet) it worked really nicely, but now when I try to deploy my site (on uberspace.de), I get an unexpected end of file syntax error, referring to the /classes/KirbyExtended/BlurryPlaceholder.php file. See screenshot.

20200925-232640_Screenshot_GoogleChrome

I am not really a php expert, upon first glance I don't see what's wrong (and also, it works locally, so I'm a bit confused).
Is there any quick fix for this issue?

Thanks a lot!

Crop & resize on `placeholderUri()`

I am using other means of resizing images automatically, e.g. crop but when using the placeholderuri function on the image, it will obviously output another ratio and when the lazyload happens the container obviously is shrinking/changing size.

I wonder if there's a method to also use the placeholder in the same ratio.

Placeholder image as poster for video?

Hi there!

Thanks a lot for the plugin!

I just tried to give my <video></video> element a poster attribute and tried to place a blurry placeholder into this. This does not work, the poster attribute gets added, but stays empty. Is this a general limitation of the poster attribute or is this a limitation of the plugin?

Thanks!

Placeholder from external image

Some of the images on my site are external. I have product pages which have url references to images on shopify cdn. Is there any chance to make this work with external images? Or should I somehow try to generate thumbs in place?

High-res image wont show up

Hi,

I have truobles getting the High-res image up. Blurry is working fine. I'll try fetching the images in a foreach.

<?php 
$projects = page('work')->children()->listed()->filterBy('onhome', true);
if(isset($limit)) $projects = $projects->limit($limit);
?>
<?php foreach($projects as $project): ?>
<img src="<?= $project->cover()->toFile()->placeholderUri() ?>" data-src="<?= $project->cover()->toFile()->url() ?>" data-lazyload data-sizes="auto">
<?php endforeach ?>

Blurry placeholder with WebP and `<picture>` tag

Hello there,

I'm struggling with getting blurry placeholders and webp images displayed at the same time. I either get blurry placeholders and jpegs loaded, or no placeholders but webp.

This is the snippet that I'm using thoughout my site:

<picture>
    <source
        type="image/webp"
        data-srcset="<?= $image->srcset('webp') ?>"
        sizes="100vw"
    >
    <img
        class="h-full w-full object-cover"
        style="object-position: <?php echo $image->focusPercentageX() ?>% <?php echo $image->focusPercentageY() ?>%;"
        
        src="<?= $image->placeholderUri() ?>"
        data-src="<?= $image->resize(400)->url() ?>"
        data-srcset="<?= $image->srcset() ?>"
        sizes="100vw"
        data-lazyload
        
        width="<?= $image->resize(1800)->width() ?>"
        height="<?= $image->resize(1800)->height() ?>"
        alt="<?= $image->alt() ?>"
    >
</picture>

Hope someone can help me with that.

Thank you for creating that plugin!

Hans

Get placeholder data via API/KQL

Is there any chance to use Kirby Query Language to get placeholder data?

I tried the following — It was a desperate shot in the dark and with no success :-)

...
photo: {
  query: "page.photos.toFiles.first",
  select: {
    url: true,
    placeholderUri: true,
    focus: true
  }
}

Maybe that's more of a KQL-Question on how to call fieldMethods in a query...

Publish useLazyload as npm package

I really like your updated version of lozad!

Right now, importing it from the plugin folder seems a bit verbose:

import { useLazyload } from '../site/plugins/kirby-blurry-placeholder/src/useLazyload'

Would be great to just have an npm package. Or is there a shorter way to import it?

Artifacts when using transparent images

First of all, thanks for the plugin – it’s awesome!

Unfortunately I am having some problems when using it for transparent PNG files, there are some heavy artifacts in the blurred version of the image:

image

Srcset conflict

Hi,

I can't figure out exactly where or why this is happening but when I use srcset with this plugin it stops working. It works great if I don't try to set the sizes. Would you be able to point me in the right direction?

My image tag looks like this
<img class="product-image" src="<?= $image->placeholderUri() ?>" data-src="<?= $image->url() ?>" data-lazyload alt="<?= $image->alt() ?>" data-sizes="auto" >

My config looks like this

    'kirby-extended.blurry-placeholder' => [
      'pixel-target' => 75,
      'srcset' => [
          'enable' => true,
          'sizes' => "760, 1400"
      ]
    ]

Previously I had srcset setup like this:

    'thumbs' => [
      'srcsets' => [
        'default' => [760, 1480, 2880],
        'exhibit' => [760, 1400],
        'product' => [740, 1400, 2000],
      ],
    ],

And was calling srcset in my image tag like this:
<img class="product-image" src="<?= $image->url() ?>" srcset="<?= $image->srcset('exhibit') ?>">

Thank you!
Emma

Make placeholders smoother

Hey there,

when using your plugin "as-is", I get this:

Screenshot 2023-03-25 at 19 24 40

From the banner image, I was under the impression to get softer gradients and overall smoother images, is there a way to "improve" the output quality by tweaking parameters? Or is it my images (strong light/shadow transitions etc)

Thanks!

WebP & AVIF support via `srcset`

Hey there!
Do you have any experience with different image formats being used, such as WebP, AVIF, .. ? I find it hard and was wondering, if your plugin could handle it properly 😁

Let me know if you know or don't know, you know ..
Cheers!
S1SYPHOS

Slow loading blurry placeholder image and big file size on live server

Having issues with blurry image loading speed on my live server. From the dev tools waterfall I can see the total loading time is 12.08s and the file size of resources ranges between 1-4mb. On my local environment this isn't an issue, the "data:image/svg+xml..." file sizes are about 1.2kb.

Screen Shot 2022-09-11 at 11 55 00 pm

Screen Shot 2022-09-12 at 12 02 18 am

This is severely affecting image loading times on my site, see live working link: https://jamesbraund.com/people

For reference, here are img tag snippets and config below:

<img
    src="<?= $image->placeholderUri() ?>"
    data-srcset="<?= $image->srcset('webp_portrait')?>"
    data-lazyload
    sizes="(max-width: 720px) 100vw, 50vw"
    >
'thumbs' => [
    'srcsets' => [
        'driver'    => 'im',
        'quality'   => 100,
        'bin'       => '/usr/local/bin/convert',
        'interlace' => true,
        'webp_landscape' => [
            '2400w'  => ['width' => 2400, 'format' => 'webp'],
            '1600w'  => ['width' => 1600, 'format' => 'webp'],
            '800w'  => ['width' => 800, 'format' => 'webp']
            ],
        'webp_portrait' => [
            '1800w'  => ['width' => 1800, 'format' => 'webp'],
            '1200w'  => ['width' => 1200, 'format' => 'webp'],
            '600w'  => ['width' => 600, 'format' => 'webp']
            ],
        ]
    ],
    'kirby-extended.blurry-placeholder' => [
        'pixel-target' => 60,
        'srcset' => [
            'enable' => true,
        ],

Any ideas what's the cause and/or any fixes?

Use `<noscript>` tag with default `<img>` element for browsers with disabled JS

Untested, but just came into my mind that you could do something like this to get rid of blurry placeholders if JS is disabled (roughly 1-2% of all browsers, so basically as irrelevant as IE)

<figure>
	  <img
	    src="<?= $image->placeholderUri() ?>"
	    data-src="<?= $image->url() ?>"
	    data-lazyload
	    alt="<?= $image->alt() ?>"
	  />
	  <noscript>
		    <img
		      src="<?= $image->url() ?>"
		      alt="<?= $image->alt() ?>"
		    />
	  </noscript>
</figure>

Probably needs absolute positioning on the parent or some kind of styling to remove the placeholder when the page is loaded without JS, as otherwise both images are shown, so something like this in the head should work:

<noscript>
	  <style>
			img[data-lazyload] {
				display: none; 
			}
	  </style>
</noscript>

With regard to this plugin, it should be at least mentioned in the README and optionally added to the bluryrimage KirbyTag and/or the image block snippet.

Handling static assets

I just figured out how to use this plugin with static files supplied with your Kirby "theme" and maybe we can find a way to extend the Class with methods and implement this into the plugin?

Use the asset helper to get a Kirby\Filesystem\Asset Object:

$asset = asset('/assets/leaves.png');

Then get the placeholderUri manually:

$placeholderUri = \KirbyExtended\BlurryPlaceholder::uri($elFile);

You will also need to switch the accepted parameter type in classes/KirbyExtended/BlurryPlaceholder to mixed.

    /**
     * Creates a blurry image placeholder
     *
     * @param \Kirby\Cms\File|\Kirby\Filesystem\Asset $file
     * @param float|null $ratio
     * @return string
     * @throws \Kirby\Exception\InvalidArgumentException
     */
    public static function image(mixed  $file, $ratio = null): string
    {
		[..]
	}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.