htmlcssmix-blend-mode

Apply dynamic glint effect img to other partially transparent img using css


I want to apply an animating glinting effect to certain images on my web page. The images are all randomly shaped and have transparent parts. I want the glint to only be applied to the opaque parts of these images. I have prepared the glint itself in an animated .webp file; it contains moving wavy half-transparent streaks of colors. It is complex enough that I do not want to create it with css (if that is even possible).

My attempt at this was to stack two images on top of each other with position: absolute;. The lower image is the one to be glinted and the upper one the glint itself. And then apply mix-blend-mode: lighten; (or overlay, or whatever turns out best) to the glint image. This works, but the glint doesn't contain itself to only the opaque parts of the image.

Now I wonder if this effect can be realized with plain css at all? The alternative is to prepare pre-glinted versions of all relevant images, but due to file size and maintenance concerns I'd rather not do that.

The code I have so far is:

.icon {
    position: relative;
    display: inline-block;
    background-color: #000080;
}
.icon .type {
    width: 24px;
    height: 24px;
    position: absolute;
    top: 0px;
    left: 0px;
    object-fit: none;
    object-position: 0px 0px; /* its a css image sprite */
    z-order: 10;
}
.icon .glint {
    width: 24px;
    height: 24px;
    position: absolute;
    top: 0px;
    left: 0px;
    z-order: 11;
    mix-blend-mode: overlay;
}
<div class="icon">
    <img class="type" src="https://omeo.one/images/icons.png">
    <img class="glint" src="https://omeo.one/images/glint.webp">
</div>

The icons are just regular 24x24 images arranged in an image sprite (link; the lower half is what I use now as 'glinted' version, but I want to make it animated). I haven't yet finalized the glint image, but I've uploaded a proof of concept (link). I first tried to upload the glint webp image file but StackOverflow refused to take it? To see it in context, my site (OMEO) now uses a static pre-glinted image, next to the item drop downs. For the curious: I want to replicate the Minecraft glinting effect for enchanted items.


Solution

  • If you want the glint to "contain itself to only the opaque parts of the image" then mask is the answer. I also changed the imgs to pseudo elements to tidy up the HTML code a bit.

    .icon {
      position: relative;
      display: inline-block;
      background-color: #fff;
      width: 24px;
      height: 24px;
      overflow: hidden;
    }
    
    .icon::before {
      content: '';
      display: block;
      position: absolute;
      inset: 0;
      background-image: url('https://omeo.one/images/icons.png');
      background-position: calc(-24px * (var(--index, 1) - 1)) 0px;
    }
    
    .icon::after {
      content: '';
      display: block;
      position: absolute;
      inset: 0;
      background-image: url('https://omeo.one/images/glint.webp');
      mix-blend-mode: lighten;
      mask-image: url(https://omeo.one/images/icons.png);
      mask-position: calc(-24px * (var(--index, 1) - 1)) 0px;
    }
    
    .icon:nth-child(2) {
      background: #800;
    }
    
    .icon:nth-child(3) {
      background: #080;
    }
    
    .icon:nth-child(4) {
      background: #008;
    }
    
    .icon:nth-child(5) {
      background: #888;
    }
    <div class="icon" style="--index:1;"></div>
    <div class="icon" style="--index:2;"></div>
    <div class="icon" style="--index:3;"></div>
    <div class="icon" style="--index:4;"></div>
    <div class="icon" style="--index:5;"></div>