0

I have some content on a page that serves as a global background. I put a sprite (div with background-image: url(...), changing frames by modifying background-position) on top of that using position: absolute. The sprite is a PNG with alpha-channel.

Now I'm trying to add some tint to that image (greenish or blueish or other).

I've studied the similar questions and apparently the only possible solutions are:

  1. Create a div on top of the sprite with the desired color as its background-color, desired tint strength as opacity and the original sprite image as mask-image (and setting the mask-type: alpha). While it should work on paper, it doesn't in practice - this new div is just invisible :(
  2. Use mix-blend-mode for the overlaying colored div and specify something like multiply or overlay. It produces perfect results as long as the global background is black. If it's something else - it gets included in the calculations and the overlay div modifies it as well, producing a tinted rectangle instead of tinted sprite...
  3. Use SVG filter as described in an answer here: https://stackoverflow.com/a/30949302/306470 . I didn't try this one yet, but it feels unnecessary complicated for this task. I'm concerned about the performance here too, will it slow down things a lot if there will be multiple tinted sprites on the screen at the same time? If anyone had experience with it - please comment here :)
  4. Prepare a tinted version of the sprite using an invisible canvas. Sounds even more complicated, has a disadvantage of having to spend time to prepare the tinted version of the sprite, but should work as fast as the original sprite once it's prepared? Implementation should be pretty complicated though. Pure CSS solution would be much better...

Am I missing something? Are there any other options? Or should I go with #3 or #4?

mephisto123
  • 1,400
  • 13
  • 38
  • Try `filter: hue-rotate(Xdeg)` changing the value of X to meet your requirements – Akshay Nov 14 '20 at 14:27
  • Thank you! I've considered that but it will work only for more or less single-hue images and I should know that hue before applying the filter. – mephisto123 Nov 14 '20 at 14:37
  • If your images aren't dynamic, you could find the desired hue-rotate for each image, add them to an object and then use js to dynmically apply the filter. But it may not be applicable if you have lots of images or if the images are user generated – Akshay Nov 14 '20 at 15:03
  • Images are not user-generated but I will potentially have many of them and manually finding a correct hue is not really an option. But thank you for the suggestion anyway! – mephisto123 Nov 14 '20 at 15:07
  • You could use JS to get the position of the image/s getBoundingClientRect() then set the position of an overlay element set to position absolute with a higher zindex then using the rect.top, rect.left settings of your images set those to the overlay so they position on top. Get the width & height of the images using this.width & this.height and set the overlay elements to those widths and heights. Set the backgroundColor of your overlay element to desired RGB with an opacity (0, 255, 0, 0.5) => greenish overlay or randomize the RGB with the opacity set to 0.5 so it acts like a filter. – dale landry Nov 14 '20 at 18:27

2 Answers2

0

Here is a working example of the outlined comment I left, hope it helps. I use a created div element to overlay on top of the image. Get the image elements position using boundingClientRect and this.width/this.height inside a for loop looping over the image elements. Set the overlay elements position to that of the image element being looped over and randomize a color using function with rgb setting alpha to 0.5.

let fgrp = document.getElementById("group");
let images = document.querySelectorAll(".imgs");
//function to randomize the RGB overlay color
function random() {
  var o = Math.round,
    r = Math.random,
    s = 255;
  return o(r() * s);
}
//function to randomize a margin for each image to show the overlay will snap to the image 
function randomWidth() {
  var n = Math.round,
    ran = Math.random,
    max = 400;
  return n(ran() * max);
}
// loop through the img elements and create an overlay div element for each img element
for (let i = 0; i < images.length; i++) {
  // load the images and get their wisth and height
  images[i].onload = function() {
    let width = this.width;
    let height = this.height;
    this.style.marginLeft = randomWidth() + "px";
    // create the overlay element
    let overlay = document.createElement("DIV");
    // append the overlay element
    fgrp.append(overlay);
    // get the image elements top, left positions using `getBoundingClientRect()`
    let rect = this.getBoundingClientRect();
    // set the css for the overlay using the images height, width, left and top positions
    // set position to absolute incase scrolling page, zindex to 2 
    overlay.style.cssText = "width: " + this.offsetWidth + "px; height: " + this.offsetHeight + "px; background-color: rgba(" + random() + ", " + random() + ", " + random() + ", 0.5); left: " + rect.left + "px; top: " + rect.top + "px; position: absolute; display: block; z-index: 2; cursor pointer;";
  }
}
img {
  margin: 50px 0;
  display: block;
}
<div id="group">
  <image src="https://artbreeder.b-cdn.net/imgs/275c7c05efca3a40e3178208.jpeg?width=256" class="imgs"></image>
  <image src="https://artbreeder.b-cdn.net/imgs/275c7c05efca3a40e3178208.jpeg?width=256" class="imgs"></image>
  <image src="https://artbreeder.b-cdn.net/imgs/275c7c05efca3a40e3178208.jpeg?width=256" class="imgs"></image>
</div>
dale landry
  • 7,831
  • 2
  • 16
  • 28
  • 1
    It will not work for images with transparency: it will add a tint to the whole rect including the background under the image that's visible in the transparent parts of the image. – mephisto123 Nov 15 '20 at 16:18
0

Following the guide below, it is possible to at a colorful tint over a div/image using only CSS, like so:

<html>

<head>
    <style>
        .hero-image {
            position: relative;
            width: 100%;
        }

        .hero-image:after {
            position: absolute;
            height: 100%;
            width: 100%;
            background-color: rgba(0, 0, 0, .5);
            top: 0;
            left: 0;
            display: block;
            content: "";
        }
    </style>
</head>

<body>
    <div class="hero-image">
        <img src="https://cdn.jpegmini.com/user/images/slider_puffin_before_mobile.jpg" alt="after tint" />
    </div>
    <div>
        <img src="https://cdn.jpegmini.com/user/images/slider_puffin_before_mobile.jpg" alt="before tint" />
    </div>
</body>

</html>

Since you are trying to place it on top of an absolute position image, as the guide says, add a z-index of 1 (for example) to the :after chunk.

Edit: It might need some tweaking on the width's percentage!

Source: https://ideabasekent.com/wiki/adding-image-overlay-tint-using-css

hkZeneus
  • 1
  • 2