megclaypool
1/13/2020 - 4:54 PM

Cross Browser Blur Effect AND Getting Image Info from Drupal Media Library Images

[Cross Browser Blur Effect AND Getting Image Info from Drupal Media Library Images]

Ok, the stuff in the twig template was almost directly copied from Cross-browser blur-effect (Chrome, Firefox, Safari, IE10+).

Note that the way to get the equivalent of object-fit: cover with an image inside an svg is to make sure the svg viewbox matches the image dimensions, and that the svg has preserveAspectRatio="xMidYMid slice" set.

The toughest part of implementing this was getting the image info from Drupal (url was tough enough, but width and height were super challenging!!!). Sad but true -- I pretty much spent a whole day on it. I finally found my breakthrough in the code for "d8-responsive-image-programmatically.php"

This could also be implemented in WordPress, by adjusting the PHP backend code. Getting image url, width, and height is comparatively trivial in WP :P

/**
 * Implements hook_preprocess_HOOK() for Block document templates.
 */
function radicati_d8_preprocess_block(&$variables) {
  if (!empty($variables["elements"]["#id"]) && $variables["elements"]["#id"] == 'defaultsitemessage') {
    if (!empty($variables['elements']['content']['field_background_image'])) {

      // get the image uri from the entity reference
      $uri = $variables['content']['#block_content']->get('field_background_image')->entity->get('field_media_image')->entity->uri->value;

      // get the image object attached to that uri
      $image = \Drupal::service('image.factory')->get($uri);

      // verify that it's really an image, and if so store the needed info as an object in $variables
      if ($image->isValid()) {
        $variables['site_message_background_image'] = [
          'url' => file_create_url($uri),
          'width' => $image->getWidth(),
          'height' => $image->getHeight(),
        ];
      }
    }
  }
}
{# The regular card template stuff goes here...#}

{% block card_extra_content %}
    {# This is an alternative way of displaying the site message background image. It's a huge PITA and the only reason I'm doing this is that the regular image has a filter: blur applied, which IE doesn't support. #}
    {# This solution depends on JS, so if somebody is running IE with JS disabled, they just don't get a blurry background image. #}
    {# This isn't a responsive image, so it's loading the full-size original. However, it will only load if the ie-svg script is triggered, which copies the data-uri value to href and thus loads the image. #}
    {# In theory, all the devices that are running IE will be non-mobile, so this should be ok... #}
    <div {{ bem('ie-background-image-wrapper', null, 'card') }}>
      <svg {{ bem('ie-background-image', null, 'card', ['image--ie-replacement-svg']) }} preserveAspectRatio="xMidYMid slice" viewbox="0 0 {{ background_image.width }} {{ background_image.height }}">
        <defs>
          <filter id="blur">
            <feGaussianBlur stddeviation="3"/>
          </filter>
        </defs>
        <image data-uri="{{ background_image.url }}" width="{{ background_image.width }}" height="{{ background_image.height }}" filter="url(#blur)"></image>
      </svg>
    </div>
  {% endblock card_extra_content %}
(function($, Drupal) {
  "use strict";

    // find all the original images
    var originalImageWrapper = document.querySelectorAll(".image--original");

    // find all the svg replacement images
    var ieSvgImageWrapper = document.querySelectorAll(".image--ie-replacement-svg");

  // Detect Internet Explorer with Ducktyping
  // https://stackoverflow.com/a/31479176/6412747
  var isIE = /*@cc_on!@*/false || !!document.documentMode;

  // only run this if the browser is ie, and thus unable to display the original images properly
  if (isIE) {

    // go through all the images tagged to replace
    // set the original image to display none
    // also remove its source and sourceset so it stops downloading and wasting bandwidth
    for (var i = 0; i < originalImageWrapper.length; i++) {
      originalImageWrapper[i].style.display = "none";
      var originalImage = originalImageWrapper[i].querySelector('img');
      originalImage.src = '';
      originalImage.srcset = '';
    }

    // go through all the images tagged as replacements
    // set the replacement images (which default to display: none) to display: block
    // grab the url from the data-uri attribute and copy it to href so the image loads
    for (var i = 0; i < ieSvgImageWrapper.length; i++) {
      ieSvgImageWrapper[i].style.display = "block";
      var ieSvgImage = ieSvgImageWrapper[i].querySelector("image");
      var ieSvgImageUri = ieSvgImage.getAttribute('data-uri');
      ieSvgImage.setAttribute("href", ieSvgImageUri);
    }
  }
})(jQuery, Drupal);