skip to Main Content

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 24×24 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.

2

Answers


  1. mix-blend-mode lighten is the right idea.

    However, outside the opaque part of the image there is nothing to ‘lighten’ against. Putting a background on the image that is white will ensure that on lighten white will be what shows.

    <style>
      .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;
        /* it's a css image sprite */
        z-index: 10;
        background-color: white;
      }
      
      .icon .glint {
        width: 24px;
        height: 24px;
        position: absolute;
        top: 0px;
        left: 0px;
        z-index: 11;
        mix-blend-mode: lighten;
      }
    </style>
    <div class="icon">
      <img class="type" src="https://omeo.one/images/icons.png">
      <img class="glint" src="https://omeo.one/images/glint.webp">
    </div>

    Note: I have not heard of z-order so have changed it to z-index, though it makes no difference in this example as the glint is positioned after and absolutely relative to the image.

    Login or Signup to reply.
  2. 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(--c, 1) - 1)) calc(-24px * (var(--r, 1) - 1));
    }
    
    .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(--c, 1) - 1)) calc(-24px * (var(--r, 1) - 1));
    }
    
    .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="--r:1; --c:1;"></div>
    <div class="icon" style="--r:1; --c:2;"></div>
    <div class="icon" style="--r:2; --c:3;"></div>
    <div class="icon" style="--r:3; --c:4;"></div>
    <div class="icon" style="--r:3; --c:6;"></div>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search