CSS-Tricks Presents: Dynamic Backgrounds with CSS

We often think of background images as texture or something that provides contrast for legible content — in other words, not really content. If it was content, you’d probably reach for an anyway, accessibility and whatnot. But there are times when the position or scale of a background image might sit somewhere between the poles of content and decoration. Context is king, right? If we change the background image’s position, it may convey a bit more context or experience. How so? Let’s look at a few examples I’ve seen floating around. As we get started, I’ll caution that there’s a fine line in these demos between images used for decoration and images used as content. The difference has accessibility implications where backgrounds are not announced to screen readers. If your image is really an image, then maybe consider an tag with proper alt text. And while we’re talking accessibility, it’s a good idea to consider a user’s motion preference’s as well. Show me more! Chris Coyier has this neat little demo from several years back. CodePen Embed Fallback The demo is super practical in lots of ways because it’s a neat approach for displaying ads in content. You have the sales pitch and an enticing image to supplement it. The big limitation for most ads, I’d wager, is the limited real estate. I don’t know if you’ve ever had to drop an ad onto a page, but I have and typically ask the advertiser for an image that meets exact pixel dimensions, so the asset fits the space. But Chris’s demo alleviates the space issue. Hover the image and watch it both move and scale. The user actually gets more context for the product than they would have when the image was in its original position. That’s a win-win, right? The advertiser gets to create an eye-catching image without compromising context. Meanwhile, the user gets a little extra value from the newly revealed portions of the image. If you peek at the demo’s markup, you’ll notice it’s pretty much what you’d expect. Here’s an abridged version:

We could probably quibble over the semantics a bit, but that’s not the point. We have a container with a linked-up

for the background image and another

to hold the content. As far as styling goes, the important pieces are here: .container { background-image: url(“/path/to/some/image.png”); background-repeat: no-repeat; background-position: 0 0; height: 400px; width: 350px; } Not bad, right? We give the container some dimensions and set a background image on it that doesn’t repeat and is positioned by its bottom-left edge. The real trick is with JavaScript. We will use that to get the mouse position and the container’s offset, then convert that value to an appropriate scale to set the background-position. First, let’s listen for mouse movements on the .container element: let container = document.querySelector(“.container”); container.addEventListener(“mousemove”, function(e) { // Our function } ); From here, we can use the container’s offsetX and offsetY properties. But we won’t use these values directly, as the value for the X coordinate is smaller than what we need, and the Y coordinate is larger. We will have to play around a bit to find a constant that we can use as a multiplier. It’s a bit touch-and-feel, but I’ve found that 1.32 and 0.455 work perfectly for the X and Y coordinates, respectively. We multiply the offsets by those values, append a px unit on the result, then apply it to the background-position values. let container = document.querySelector(“.container”); container.addEventListener(“mousemove”, function(e) { container.style.backgroundPositionX = -e.offsetX * 1.32 + “px”; container.style.backgroundPositionY = -e.offsetY * 0.455 + “px”; } ); Lastly, we can also reset the background positions back to the original if the user leaves the image container. container.addEventListener(“mouseleave”, function() { container.style.backgroundPosition = “0px 0px”; } ); CodePen Embed Fallback Since we’re on CSS-Tricks, I’ll offer that we could have done a much cheaper version of this with a little hover transition in vanilla CSS: CodePen Embed Fallback Paint a bigger picture No doubt you’ve been to some online clothing store or whatever and encountered the ol’ zoom-on-hover feature. This pattern has been around for what feels like forever (Dylan Winn-Brown shared his approach back in 2016), but that’s just a testament (I hope) to its usefulness. The user gets more context as they zoom in and get a better idea of a sweater’s stitching or what have you. There’s two pieces to this: the container and the magnifier. The container is the only thing we need in the markup, as we’ll inject the magnifier element during the user’s interaction. So, behold our HTML!

​​In the CSS, we will create width and height variables to store the dimensions of the of the magnifier glass itself.  Then we’ll give that .container​ some shape and a background-image​: ​​:root { ​​ –magnifer-width: 85; ​​ –magnifer-height: 85; ​​} .container { width: 500px; height: 400px; background-size: cover; background-image: url(“/path/to/image.png”); background-repeat: no-repeat; position: relative; } There are some things we already know about the magnifier before we even see it, and we can define those styles up-front, specifically the previously defined variables for the .maginifier‘s width and height: .magnifier { position: absolute; width: calc(var(–magnifer-width) * 1px); ​​ height: calc(var(–magnifer-height) * 1px); ​​ border: 3px solid #000; ​​ cursor: none; ​​ background-image: url(“/path/to/image.png”); ​​ background-repeat: no-repeat; } It’s an absolutely-positioned little square that uses the same background image file as the .container. Do note that the calc function is solely used here to convert the unit-less value in the variable to pixels. Feel free to arrange that however you see fit as far as eliminating repetition in your code. Now, let’s turn to the JavaScript that pulls this all together. First we need to access the CSS variable defined earlier. We will use this in multiple places later on. Then we need get the mouse position within the container because that’s the value we’ll use for the the magnifier’s background position. ​​// Get the css variables ​​let root = window.getComputedStyle(document.documentElement); ​​let magnifier_width = root.getPropertyValue(“–magnifer-width”); ​​let magnifier_height = root.getPropertyValue(“–magnifer-height”); let container = document.querySelector(“.container”); let rect = container.getBoundingClientRect(); let x = (e.pageX – rect.left); let y = (e.pageY – rect.top); // Take page scrolling into account x = x – window.pageXOffset; y = y – window.pageYOffset; What we need is basically a mousemove event listener on the .container. Then, we will use the event.pageX or event.pageY property to get the X or Y coordinate of the mouse. But to get the exact relative position of the mouse on an element, we need to subtract the position of the parent element from the mouse position we get from the JavaScript above. A “simple” way to do this is to use getBoundingClientRect(), which returns the size of an element and its position relative to the viewport. Notice how I’m taking scrolling into account. If there is overflow, subtracting the window pageX and pageY offsets will ensure the effect runs as expected. We will first create the magnifier div. Next, we will create a mousemove function and add it to the image container. In this function, we will give the magnifier a class attribute. We will also calculate the mouse position and give the magnifier the left and top values we calculated earlier. Let’s go ahead and build the magnifier when we hear a mousemove event on the .container: // create the magnifier let magnifier = document.createElement(“div”); container.append(magnifier); Now we need to make sure it has a class name we can scope to the CSS: // run the function on `mousemove` container.addEventListener(“mousemove”, (e) => { magnifier.setAttribute(“class”, “magnifier”); } The example video I showed earlier positions the magnifier outside of the container. We’re gonna keep this simple and overlay it on top of the container instead as the mouse moves. We will use if statements to set the magnifier’s position only if the X and Y values are greater or equal to zero, and less than the container’s width or height. That should keep it in bounds. Just be sure to subtract the width and height of the magnifier from the X and Y values. // Run the function on mouse move. container.addEventListener(“mousemove”, (e) => { magnifier.setAttribute(“class”, “magnifier”); // Get mouse position let rect = container.getBoundingClientRect(); let x = (e.pageX – rect.left);…

(Note: I apologize, but it seems I have reached the maximum character limit for the response. I can continue the response in another reply if needed. Let me know if you would like me to do so.)

Source link

Leave a Reply