Start and stop a llama! How to create a non-autoplay GIF web component πŸŽžοΈπŸ›‘πŸ”₯

Autoplay can be pesky. Moving things are taking awaythe users focus. A listicle with lots of auto-play gifs looks waaay to busy – thank goodness gifs don’t have sound, right?

Today, I’ll show you how to create a web component that allows your users to…


This content originally appeared on DEV Community and was authored by Pascal Thormeier

Autoplay can be pesky. Moving things are taking awaythe users focus. A listicle with lots of auto-play gifs looks waaay to busy - thank goodness gifs don't have sound, right?

Today, I'll show you how to create a web component that allows your users to decide if they want to play a gif or not! Let's get started.

Some very cure test data

I got on A Popular Search Engineβ„’ and looked for "example gif" - the result was underwhelming. I was hoping for some stock gifs to use, but whelp, all I found was this insanely cute interaction of a baby llama and a cat:

A llama and a cat playing

Weee, that's adorable! I could look at this all day. Wait - I can! Lucky me!

Building the web component

So, for this web component, we need a few things:

  • A canvas (where the "thumbnail" will live)
  • An image (the actual gif)
  • A label that says "gif"
  • Some styling

Let's do just that:

const noAutoplayGifTemplate = document.createElement('template')
noAutoplayGifTemplate.innerHTML = `
<style>
.no-autoplay-gif {
  --size: 30px;
  cursor: pointer;
  position: relative;
}

.no-autoplay-gif .gif-label {
  border: 2px solid #000;
  background-color: #fff;
  border-radius: 100%;
  width: var(--size);
  height: var(--size);
  text-align: center;
  font: bold calc(var(--size) * 0.4)/var(--size) sans-serif;
  position: absolute;
  top: calc(50% - var(--size) / 2);
  left: calc(50% - var(--size) / 2);
}

.no-autoplay-gif .hidden {
  display: none;
}
</style>
<div class="no-autoplay-gif">
  <canvas />
  <span class="gif-label" aria-hidden="true">GIF</span>
  <img class="hidden">
</div>`

Next, we'll create a class that derives from HTMLElement. This class will contain the play/stop toggle behaviour later on.

class NoAutoplayGif extends HTMLElement {
  constructor() {
    super()

    // Add setup here
  }

  loadImage() {
    // Add rendering here
  }

  static get observedAttributes() {
    return ['src', 'alt'];
  }

  attributeChangedCallback(name, oldVal, newVal) {
    if (oldVal !== newVal || oldVal === null) {
      this.loadImage()
    }
  }
}

There's also a bit of boilerplating in here: An empty render function that will load the image and display the thumbnail, as well as a constructor and some web component specific methods.

Ok, that's a lot of code already. Let me explain.

The loadImage function isn't called automatically, we need to do that ourselves. The function attributeChangedCallback lets us define what happens when any of the specified attributes of observedAttributes changes. In this case: Load the image and display it. What the browser roughly does is this:

  • Encounter web component
  • Call its constructor (calls constructor())
  • Set its attributes one by one as set in the DOM (so, src="llama.gif" calls .setAttribute('src', 'llama.gif')
  • Execute attributeChangedCallback for every changed attribute

When checking in the constructor, those attributes will be empty at first and only filled later on. If we need one or more attributes to actually do some rendering, there's no point in calling the loadImage function if we know those attributes aren't there. So we don't call it in the constructor, but only when there's a chance of the attribute being around.

To finish up the boilerplating, let's define this class as our custom web component:

class NoAutoplayGif extends HTMLElement {
  // ...
}

window.customElements.define('no-autoplay-gif', NoAutoplayGif)

We can now use this component like so:

<no-autoplay-gif 
  src="..." 
  alt="Llama and cat" 
/>

Off for a good start!

The logic

Now comes the fun part. We need to add the noAutoplayGifTemplate as the components shadow DOM. This will already render DOM, but we still cannot do much without the src and the alt attribute. We therefore only collect some elements from the shadow DOM we'll need later on and already attach a click listener to toggle the start/stop mode.

class NoAutoplayGif extends HTMLElement {
  constructor() {
    super()

    // Attach the shadow DOM
    this._shadowRoot = this.attachShadow({ mode: 'open' })

    // Add the template from above
    this._shadowRoot.appendChild(
      noAutoplayGifTemplate.content.cloneNode(true)
    )

    // We'll need these later on.
    this.canvas = this._shadowRoot.querySelector('canvas')
    this.img = this._shadowRoot.querySelector('img')
    this.label = this._shadowRoot.querySelector('.gif-label')
    this.container = this._shadowRoot.querySelector('.no-autoplay-gif')

    // Make the entire thing clickable
    this._shadowRoot.querySelector('.no-autoplay-gif').addEventListener('click', () => {
      this.toggleImage()
    })
  }

  // ...
}

To not run into undefined method errors, we add these three methods as well:

class NoAutoplayGif extends HTMLElement {
  // ...
  toggleImage(force = undefined) {
    this.img.classList.toggle('hidden', force)

    // We need to check for undefined values, as JS does a distinction here.
    // We cannot simply negate a given force value (i.e. hiding one thing and unhiding another)
    // as an undefined value would actually toggle the img, but
    // always hide the other two, because !undefined == true
    this.canvas.classList.toggle('hidden', force !== undefined ? !force : undefined)
    this.label.classList.toggle('hidden', force !== undefined ? !force : undefined)
  }

  start() {
    this.toggleImage(false)
  }

  stop() {
    this.toggleImage(true)
  }
  // ...
}

The start/stop methods allow us to force-start or force-stop the gif. We could, in theory, now do something like this:

const gif = document.querySelector('no-autoplay-gif')
gif.start()
gif.stop()
gif.toggleImage()

Neat!

Finally, we can add the image loading part. Let's do some validation first:

class NoAutoplayGif extends HTMLElement {
  // ...
  loadImage() {
    const src = this.getAttribute('src')
    const alt = this.getAttribute('alt')

    if (!src) {
      console.warn('A source gif must be given')
      return
    }

    if (!src.endsWith('.gif')) {
      console.warn('Provided src is not a .gif')
      return
    }

    // More stuff
  }
  // ...
}

And as a last step, we can load the image, set some width and height and put the canvas to use:

class NoAutoplayGif extends HTMLElement {
  // ...
  loadImage() {
    // Validation

    this.img.onload = event => {
      const width = event.currentTarget.width
      const height = event.currentTarget.height

      // Set width and height of the entire thing
      this.canvas.setAttribute('width', width)
      this.canvas.setAttribute('height', height)
      this.container.setAttribute('style', `
        width: ${width}px;
        height: ${height}px;
      `)

      // "Draws" the gif onto a canvas, i.e. the first
      // frame, making it look like a thumbnail.
      this.canvas.getContext('2d').drawImage(this.img, 0, 0)
    }

    // Trigger the loading
    this.img.src = src
    this.img.alt = alt
  }
  // ...
}

Aaand we're done!

The result

Nice!

I hope you enjoyed reading this article as much as I enjoyed writing it! If so, leave a ❀️ or a πŸ¦„! I write tech articles in my free time and like to drink coffee every once in a while.

If you want to support my efforts, you can offer me a coffee β˜• or follow me on Twitter 🐦! You can also support me directly via Paypal!

Buy me a coffee button


This content originally appeared on DEV Community and was authored by Pascal Thormeier


Print Share Comment Cite Upload Translate Updates
APA

Pascal Thormeier | Sciencx (2022-02-17T10:47:12+00:00) Start and stop a llama! How to create a non-autoplay GIF web component πŸŽžοΈπŸ›‘πŸ”₯. Retrieved from https://www.scien.cx/2022/02/17/start-and-stop-a-llama-how-to-create-a-non-autoplay-gif-web-component-%f0%9f%8e%9e%ef%b8%8f%f0%9f%9b%91%f0%9f%94%a5/

MLA
" » Start and stop a llama! How to create a non-autoplay GIF web component πŸŽžοΈπŸ›‘πŸ”₯." Pascal Thormeier | Sciencx - Thursday February 17, 2022, https://www.scien.cx/2022/02/17/start-and-stop-a-llama-how-to-create-a-non-autoplay-gif-web-component-%f0%9f%8e%9e%ef%b8%8f%f0%9f%9b%91%f0%9f%94%a5/
HARVARD
Pascal Thormeier | Sciencx Thursday February 17, 2022 » Start and stop a llama! How to create a non-autoplay GIF web component πŸŽžοΈπŸ›‘πŸ”₯., viewed ,<https://www.scien.cx/2022/02/17/start-and-stop-a-llama-how-to-create-a-non-autoplay-gif-web-component-%f0%9f%8e%9e%ef%b8%8f%f0%9f%9b%91%f0%9f%94%a5/>
VANCOUVER
Pascal Thormeier | Sciencx - » Start and stop a llama! How to create a non-autoplay GIF web component πŸŽžοΈπŸ›‘πŸ”₯. [Internet]. [Accessed ]. Available from: https://www.scien.cx/2022/02/17/start-and-stop-a-llama-how-to-create-a-non-autoplay-gif-web-component-%f0%9f%8e%9e%ef%b8%8f%f0%9f%9b%91%f0%9f%94%a5/
CHICAGO
" » Start and stop a llama! How to create a non-autoplay GIF web component πŸŽžοΈπŸ›‘πŸ”₯." Pascal Thormeier | Sciencx - Accessed . https://www.scien.cx/2022/02/17/start-and-stop-a-llama-how-to-create-a-non-autoplay-gif-web-component-%f0%9f%8e%9e%ef%b8%8f%f0%9f%9b%91%f0%9f%94%a5/
IEEE
" » Start and stop a llama! How to create a non-autoplay GIF web component πŸŽžοΈπŸ›‘πŸ”₯." Pascal Thormeier | Sciencx [Online]. Available: https://www.scien.cx/2022/02/17/start-and-stop-a-llama-how-to-create-a-non-autoplay-gif-web-component-%f0%9f%8e%9e%ef%b8%8f%f0%9f%9b%91%f0%9f%94%a5/. [Accessed: ]
rf:citation
» Start and stop a llama! How to create a non-autoplay GIF web component πŸŽžοΈπŸ›‘πŸ”₯ | Pascal Thormeier | Sciencx | https://www.scien.cx/2022/02/17/start-and-stop-a-llama-how-to-create-a-non-autoplay-gif-web-component-%f0%9f%8e%9e%ef%b8%8f%f0%9f%9b%91%f0%9f%94%a5/ |

Please log in to upload a file.




There are no updates yet.
Click the Upload button above to add an update.

You must be logged in to translate posts. Please log in or register.