Ricky Stefano
Ricky Stefano

Reputation: 419

Background is shaking in transition

I have two div elements that the width can be controlled with click event on javascript. Whether on click event (toggling a className) or hover event, the background images of the its pseudo element seems to be shaking (confirmed on chrome, ie, edge, and safari).

I have looked up on SO and google but it seems to be a problem with changing the size of the background itself, which is what I did not do here.

section {
	width: 100%;
	max-width: 1920px;
	position: relative;
	min-height: 700px;
	overflow: hidden;
}

.container {
	width: 99%;
	height: 700px;
	position: absolute;
	transition: 1s linear width;
	overflow: hidden;
}

.left {
	top: 0;
	left: 0;
	background: transparent;
	transform: skew(20deg) translateX(-50%);
	border-radius: 0 20px 20px 0;
}

.left::after {
	content: "";
	position: absolute;
	top: 0;
	left: 50%;
	height: 100%;
	width: 100vw;
	transform: skew(-20deg);
	background-image: url(https://cdn.pixabay.com/photo/2018/12/04/22/38/road-3856796__340.jpg);
	background-size: 100% 100%;
	background-position: center;
	background-repeat: no-repeat;
}

.right {
	top: 0;
	right: 0;
	border-radius: 20px 0 0 20px;
	transform: skew(20deg) translateX(50%);
}

.right::after {
	content: "";
	position: absolute;
	top: 0;
	right: 50%;
	height: 100%;
	width: 100vw;
	transform: skew(-20deg);
	background-image: url(https://media.istockphoto.com/photos/taking-a-walk-in-the-woods-picture-id1130258547?s=2048x2048);
	background-size: 100% 100%;
	background-position: center;
	background-repeat: no-repeat;
}

.right:hover {
	width: 250%;
}
<section>
  <div class="container left"></div>
	<div class="container right"></div>
</section>

Upvotes: 8

Views: 3306

Answers (4)

Temani Afif
Temani Afif

Reputation: 272909

I have optimized the code and considered the use of left/right to define the size of the element then changed the width transition with a translation.

section {
  max-width: 1920px;
  position: relative;
  overflow: hidden;
  height: 500px;
}

.container {
  position: absolute;
  top: 0;
  bottom:0;
  transition:transform 1s linear;
  overflow: hidden;
  transform: skew(20deg);
}

.left {
  left: -60vw; /*to create the overflow*/
  right: calc(55% + 10px); /*10px distance between both element*/
  border-radius: 0 20px 20px 0;
}

.right {
  right: -80vw;
  left: 45%; /*100% - 55% (the right value of .left)*/
  border-radius: 20px 0 0 20px;
}

.left::after,
.right::after {
  content: "";
  position: absolute;
  top: 0;
  left: -40%;
  bottom: 0;
  right: -40%;
  transform: skew(-20deg);
  background-position: center;
  background-repeat: no-repeat;
  background-size: 100% 100%;
  transition:transform 1s linear;
}

.left::after {
  background-image: url(https://cdn.pixabay.com/photo/2018/12/04/22/38/road-3856796__340.jpg);
  transform-origin: bottom;
}

.right::after {
  transform-origin: top;
  background-image: url(https://media.istockphoto.com/photos/taking-a-walk-in-the-woods-picture-id1130258547?s=2048x2048);
}

.right:hover {
  transform: skew(20deg) translateX(-60vw);
}

.right:hover::after {
  transform: skew(-20deg) translateX(60vw);
}
<section>
  <div class="container left"></div>
  <div class="container right"></div>
</section>

Upvotes: 3

yunzen
yunzen

Reputation: 33439

The shaking stems from the centered background-position of the background-image.

Let's do this by hand:

Image a 200px wide div and a background-image with 200px width.
The image is set to centered background-position and no other background-settings.
The image will then be positioned from 0px through 200px of the div

Now imaging the same div with the same background-image and background-position.
The only difference is: the div is 201px wide, that is just 1 more then before.
The image will now be positioned from 0.5px through 200.5px of the div which will be rounded to 1px through 201px

Now one step further: imagine the width of the div to be 202px without changing any other properties.
The image will be positioned from 1px through 201px. That's exactly the same as before.

Now because your div is centered. The background starts to shake

(This is not always visible. It depends on the context width);

let buttons = document.querySelectorAll('button');
let bg = document.querySelector('#bg')

buttons.forEach((btn) => {
  btn.addEventListener('click', (e) => {
    bg.style.width = btn.getAttribute('w')
  })
})
#bg {
  display: block;
  
  width: 200px;
  height: 200px;
  background-color: gold;
  margin: 10px auto;
  background-image: url('https://picsum.photos/200/200?image=990');
  background-position: center;
  background-repeat: no-repeat;
}

body {
  text-align: center;
  width: 400px;
}
<button w="200px">200</button> 
<button w="201px">201</button>
<button w="202px">202</button>

<div id="bg"></div>

The solution would be: The left image should be positioned from the left by background-position: 0% 50%; and the right image should be positioned from the right by background-position: 100% 50%;

This snippet should be seen in full page view

div {
  height: 800px;
  width: 100vw;
  overflow: hidden;
  margin: 0 auto;
  position: relative;
}
div span {
  position: absolute;
  top: 0;
  bottom: 0;
  z-index: 0;
  -webkit-transform: skew(20deg);
          transform: skew(20deg);
  overflow: hidden;
  border-radius: 20px;
  transition-duration: .3s;
  transition-timing-function: ease-in-out;
  -webkit-animation: at-zb 1s both;
          animation: at-zb 1s both;
}
div span:hover {
  z-index: 200;
  transition-duration: 1s;
  -webkit-animation: at-z 1s both;
          animation: at-z 1s both;
}
div span a {
  position: absolute;
  -webkit-transform: skew(-20deg);
          transform: skew(-20deg);
  background-size: cover;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
}
div span:nth-child(1) {
  left: -160px;
  right: calc(100% / 2 + 10px);
  transition-property: right;
}
div span:nth-child(1) a {
  -webkit-transform-origin: 0% 100%;
          transform-origin: 0% 100%;
  background-image: url("https://picsum.photos/1920/800?image=992");
  background-position: 0% 50%;
  background-repeat: no-repeat;
}
div span:nth-child(1):hover {
  right: -160px;
}
div span:nth-child(2) {
  left: calc(100% / 2 + 10px);
  right: -160px;
  transition-property: left;
}
div span:nth-child(2) a {
  -webkit-transform-origin: 0% 0%;
          transform-origin: 0% 0%;
  background-image: url("https://picsum.photos/1920/800?image=990");
  background-position: 100% 50%;
  background-repeat: no-repeat;
}
div span:nth-child(2):hover {
  left: -160px;
}

@-webkit-keyframes at-z {
  0% {
    z-index: 1;
  }
  1%,99% {
    z-index: 9;
  }
  100% {
    z-index: 10;
  }
}

@keyframes at-z {
  0% {
    z-index: 1;
  }
  1%,99% {
    z-index: 9;
  }
  100% {
    z-index: 10;
  }
}
@-webkit-keyframes at-zb {
  0% {
    z-index: 10;
  }
  1%,99% {
    z-index: 2;
  }
  100% {
    z-index: 1;
  }
}
@keyframes at-zb {
  0% {
    z-index: 10;
  }
  1%,99% {
    z-index: 2;
  }
  100% {
    z-index: 1;
  }
}
body {
  margin: 0;
  padding: 0;
}
<div><span><a></a></span><span><a></a></span></div>

See this codepen: https://codepen.io/HerrSerker/pen/zbWpjB for a SCSS version

Upvotes: 1

jacobherrington
jacobherrington

Reputation: 463

The shaking is the result of using width: 100vw; on the element you are resizing. My best guess is that this is causing the height and width of the background image to be recalculated quite a few times during the transistion, so it appears to shake.

You could try using width: 100%; instead, but that will cause a different behavior in the way the image resizes.

Upvotes: 0

joshdoescode
joshdoescode

Reputation: 59

You could try making the '.right::after' and '.left::after' elements' width: "100%", instead of basing it on the view size. It seemed to work when done in the dev-tools.

Upvotes: 0

Related Questions