Unexpected behavior with CSS background-image transitions effects

Currently, I am experimenting with a hover swipe effect using div and background image. The background image utilizes the outer div's max-width and an inner div with a padding-bottom percentage to maintain the aspect ratio.

However, there are several aspects of this setup that I find puzzling:

  1. I was surprised to find that setting my background position to %200 only shifted the image a few pixels. It took a massive 7100% adjustment to fully reveal the repeated image.
  2. Any padding-bottom value exceeding 54% causes the transition and functionality to break.
  3. The '7100%' fix works well on Chrome, Firefox, and Opera but not on Safari.

Here is the fiddle for reference: https://jsfiddle.net/riegersn/a3mnr9um

.container {
      max-width: 480px;
    }

.project {
  margin-bottom: 15px;
  cursor: pointer;
  overflow: hidden;
  width: 100%;
  background-position: center center;
  background-repeat: repeat-x;
  background-size: contain;
  transition: all 0.2s ease-out;
  padding-bottom: 54%;
  &:hover {
    background-position: 7100% 50%;
  }
}

Answer №1

Setting the Scene

Your fiddle has been tweaked for demonstration purposes, maintaining the image proportions and relevant CSS properties. Let's start by examining what we achieve with our setup:

.container {
  max-width: 480px;
  background: orange;
}

.project {
  width: 100%;
  background-image: url("https://i.sstatic.net/Wurh4.png");
  background-position: center center;
  background-repeat: no-repeat;
  background-size: contain;
  padding-bottom: 54%;
}
<div class="container">
  <div class="project">
  </div>
</div>

Image width:      504px
Image height:     276px
Container width:  480px
Container height: 259.2px // FireFox 53

Due to background-size: contain, our background-image has the following dimensions:

BG image height:  259.2px
BG image width:   473.3217...px

The height equals the container height (this is how contain works here). The width is then calculated as: (259.2 / 276) * 504.

Difference D:     480px - 473.3217...px = 6.6782...px

This represents the gap in the snippet above. Due to background-position-x: center, you see ~3px on the left and another ~3px gap on the right.


Understanding background-position and percentages

In essence: background-position-x: 50% aligns the center of the background-image with the center of the container. So background-position-x: 100% will position the image's right side at the container's right side.

.container {
  width: 700px;
  height: 300px;
  background: orange;
  background-image: url("https://i.sstatic.net/Wurh4.png");
  background-position: 90% 50%;
  background-repeat: no-repeat;
}
<div class="container">
</div>

The calculated difference D corresponds to the 100% in background-position-x: 100% in pixels.


7100% - not a Random Number

Difference D:     6.6782...px
Factor F:         480px / D = 71.8749...

To move the background-image completely out of view, background-position-x: 7174% would be required. Question 1 should now be clarified. In short, setting 100% in background-position holds significant significance.

When I set the padding-bottom to anything over 54%, the transition and everything stops working.

Indeed, this occurs when the background-image width surpasses the container width.

Answer №2

When dealing with using percentages in the context of background images, things can get a bit complex.

Using X% to move a background image means aligning the X% point of the image with the X% point of the container. For instance, 50% will align the middle of the image with the middle of the container. On the other hand, 100% will align the last pixel of the image with the last pixel of the container, and so forth.


Src: https://css-tricks.com/almanac/properties/b/background-position/)


For further understanding of this concept, you might find this resource helpful: how-does-css-computation-for-background-percentages-work


It is worth considering using a pseudo element instead, as it allows for the use of transform, which performs better for animations compared to background-position.

With a pseudo element, you can also execute actions like zooming, rotating, skewing, animating back and forth, etc., functionalities that are not achievable solely with the background-image property.

In the example below, a pseudo element has been utilized. It has twice the width of the project, positioned halfway to the left (left: -100%, where 100% refers to its parent's width). When hovered over, it moves back to position 0 using transform: translateX(50%), translating it half its own width to the right.

.container {
  max-width: 480px;
}

.project {
  position: relative;
  margin-bottom: 15px;
  cursor: pointer;
  overflow: hidden;
  width: 100%;
  padding-bottom: 54%;
  background-size: 0;
}
.project::before {
  content: '';
  position: absolute;
  left: -100%; top: 0;
  width: 200%; height: 100%;
  background-position: left center;
  background-repeat: repeat-x;
  background-size: contain;
  background-image: inherit;
  transition: transform 0.5s ease-out;
}
.project:hover::before {
  transform: translateX(50%);
}
<div class="container">
  <div class="project" style="background-image:url('https://www.dropbox.com/s/fpha4d0uyu71whe/pandora_thumb.png?dl=1')">
  </div>
</div>

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Is there a way to make the fixed table header scroll along with the table body data?

I am facing an issue with my table where I have multiple columns. I have managed to fix the table header successfully, however, when I scroll horizontally through the table body columns, the header remains fixed and does not move accordingly. How can I res ...

Exploring the Power of JQuery Load and CSS styling

Currently, I am dynamically loading text into a div. However, I am encountering an issue where the content below this div is constantly shifting depending on the loaded text. Is there a way to restrict the space allocated to the div with the dynamic conte ...

CSS Grid having trouble with wrapping elements

Greetings to all, As a newcomer to the world of web development, I am currently exploring the wonders of the CSS grid tool. However, I have encountered a roadblock: My intention is for the cards to automatically flow one by one into the next row while ma ...

Ways to conceal Bootstrap-designed elements behind a fullscreen overlay

I'm attempting to create a transparent layer to conceal the entire content of my page behind it. It works fine without the Bootstrap columns, but as soon as I add the columns for responsive design, it stops working (or maybe I'm just not understa ...

What is the best way to apply a class to a container when a component in AngularJS receives focus or is clicked?

I am trying to apply a class to the container when either the input field is focused or when the dropdown component receives focus or is clicked. The class should be removed when the focus is lost. The requirement is for the input container to have the &a ...

Is it feasible to create a doughnut chart with curved edges?

My goal is to create a doughnut chart, but my search for reliable CSS/SVG/Canvas solutions has not been successful. https://i.sstatic.net/Rq6Lx.jpg I want each segment to have fully rounded corners, which presents a unique challenge. ...

The iPhone screen is unresponsive, causing the webpage to zoom in to the top left corner repeatedly

I've been wracking my brain trying to solve this issue, searching high and low on this site and others for a solution. I've attempted various configurations with the viewport meta tag, removing the fb-root div, ensuring there is no height=100%, b ...

Is it achievable to use multi-level :not selectors in CSS / JS querySelector?

I have come across numerous tutorials explaining the use of multiple :not selectors on a single element. However, I have yet to find any examples of using :not selectors on different levels within a single CSS / JS querySelector path. For instance, conside ...

Designing a carousel-style menu list with navigation buttons for moving forward and backward

I'm running into some trouble while attempting to create a carousel. Firstly, the issue I am facing is that when you continuously click on the Next button, the function keeps working even after reaching the last item. I'm not sure how to make th ...

The CSS3 :nth-child(odd) Selector is not working for either columns or rows in the selection

I am facing an issue with a table where I have used tr:nth-child(odd) to alternate row background colors. HTML /*-----------#602 SAO Styles-----------------------------*/ /*---------SAO Global------------*/ .sao-pricing-table table { border-collaps ...

Footer rises with bootstrap zooming

Hey everyone! I'm a beginner in the world of web development and currently working on creating my own website. I've run into an issue with my footer - I want it to stay fixed at the bottom even when zoomed in, but for some reason, when I zoom in, ...

Navbar-toggle divider shade

I am having trouble changing the color of the line break on the Twitter Bootstrap 3 navbar-toggle function. Upon viewing this image, you can see that there is a line break using my background color. https://i.sstatic.net/qgVAm.jpg I have tried several o ...

Optimizing image centering in Next JS as screen size expands

I've been struggling to work with NextJS's Image component. My goal is to set up a banner image that only covers a specific amount of space on the screen, regardless of screen size. I have managed to achieve this by using max-height: 30vh and ov ...

What is the solution for resolving the "cannot resolve" issue in CSS for an Angular project?

During a small project I was working on, I decided to add a background image to another image using CSS. Here is the code snippet: .child2 img { width: 200px; height: 200px; border-radius: 50%; background-image: url('./assets/images/imageb ...

Guide on transferring href parameter from the parent Vue component to the child component

Hey there! I've been working on creating a Vue page, breaking it down into components, and populating it with content from json. It all seems pretty straightforward, but I've hit a snag. Why is the href parameter not showing up in the right place ...

What is the best way to enable a DOM element's height to be resized?

I have a widget displayed in the corner of my browser that I would like to make resizable by dragging the header upwards to increase its height. The widget's position remains fixed with its bottom, left, and right properties unchanged, but I need the ...

Update the appearance of an element in real-time using VUEJS

I am trying to use VueJS to dynamically change the position of a div. In the data function, I have a variable called x that I want to assign to the top property. However, the code I wrote doesn't seem to be working. Here is what it looks like: <tem ...

Is it possible to update the CSS file of an external SVG file in real-time?

Is there a way for an SVG image to reference another CSS file? A webpage contains an SVG file. A button allows users to switch between classic colors and high contrast mode on the entire webpage, including the SVG image. Attempt w.css (white backgrou ...

Is there a way to determine if a container is overflowing at the top?

Currently, I am using Puppeteer to scrape Slack. My goal is to confirm whether I have scrolled to the very top of the channel feed. The issue arises because the channel feed does not actually scroll, making it impossible for me to utilize the method outli ...

Ensure all columns have the same height as the shortest column

While many solutions exist on Stack Overflow for making columns equal height based on the largest content, I am specifically interested in making all columns equal height based on the smallest column. https://i.sstatic.net/Xlcyh.png In this scenario, my ...