Reputation: 4782
I'm trying to create a horizontal parallax. There are two elements: a list that have variable content, and a background that have the same width as the list. The parallax gives the sense of depth where the background moves behind the list.
Here is a code snippet, please check the CSS comments as follow-up:
document.querySelector('#transform').onchange = () => {
document.querySelector('.background').classList.toggle('background--transformed')
}
/*
its scrolls content and sets the value for the perspective
to enable the parallax effect
*/
.parallax {
perspective: 1px;
transform-style: preserve-3d;
overflow-y: hidden;
overflow-x: scroll;
}
/*
wrapper element for both list and background
used to make the background match the size of the list,
as the list grows it will expand the container and the
background will follow.
*/
.content {
position: relative;
display: inline-block;
}
/*
horizontal list
*/
.list {
display: inline-flex;
}
.list-item {
width: 20vw;
height: 80vh;
margin: 10vh 0;
background-color: red;
opacity: 0.9;
}
.list-item+.list-item {
margin-left: 1vw;
}
/*
background element that fill the entire width of it's parent
*/
.background {
position: absolute;
top: 0;
left: 0;
width: 100%;
background-color: black;
height: 100vh;
z-index: -1;
background: url(https://images.unsplash.com/photo-1499242165110-131f6ccd0c9a?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1500&q=80);
background-size: auto 100%;
}
/*
translates the element back into space, and scales it to the double
to match the original size. And this is what fails!
*/
.background--transformed {
transform: translateZ(-1px) scale(2);
}
.option {
position: fixed;
top: 0;
left: 0;
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/meyer-reset/2.0/reset.min.css" rel="stylesheet" />
<div class="parallax">
<div class="content">
<ul class="list">
<li class="list-item"></li>
<li class="list-item"></li>
<li class="list-item"></li>
<li class="list-item"></li>
<li class="list-item"></li>
<li class="list-item"></li>
<li class="list-item"></li>
<li class="list-item"></li>
<li class="list-item"></li>
<li class="list-item"></li>
</ul>
<div class="background"></div>
</div>
</div>
<div class="option">
<input type="checkbox" id="transform" name="transform">
<label for="transform">transform</label>
</div>
Notice that when the translate
is enabled (tick the checkbox on the top left) the background size grows bigger than the list (scroll to the right to see).
In theory, scaling to the double should "reset" the size, according to the values set for the perspective. But I'm surely missing something here...
Upvotes: 2
Views: 746
Reputation: 64254
Your problem is that the 2 elements (the one with the perspective and the one with the transform) have different sizes.
So the default origins, that are center, don't match.
Set perspective-origin and transform-origin to the same value (and arbitray one, more or less centered), and they will match:
document.querySelector('#transform').onchange = () => {
document.querySelector('.background').classList.toggle('background--transformed')
}
/*
its scrolls content and sets the value for the perspective
to enable the parallax effect
*/
.parallax {
perspective: 1px;
transform-style: preserve-3d;
overflow-y: hidden;
overflow-x: scroll;
perspective-origin: 300px 220px;
}
/*
wrapper element for both list and background
used to make the background match the size of the list,
as the list grows it will expand the container and the
background will follow.
*/
.content {
position: relative;
display: inline-block;
}
/*
horizontal list
*/
.list {
display: inline-flex;
}
.list-item {
width: 20vw;
height: 80vh;
margin: 10vh 0;
background-color: red;
opacity: 0.9;
}
.list-item+.list-item {
margin-left: 1vw;
}
/*
background element that fill the entire width of it's parent
*/
.background {
position: absolute;
top: 0;
left: 0;
width: 100%;
background-color: black;
height: 100vh;
z-index: -1;
background: url(https://images.unsplash.com/photo-1499242165110-131f6ccd0c9a?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1500&q=80);
background-size: auto 100%;
}
/*
translates the element back into space, and scales it to the double
to match the original size. And this is what fails!
*/
.background--transformed {
transform: translateZ(-1px) scale(2);
transform-origin: 300px 220px;
}
.option {
position: fixed;
top: 0;
left: 0;
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/meyer-reset/2.0/reset.min.css" rel="stylesheet" />
<div class="parallax">
<div class="content">
<ul class="list">
<li class="list-item"></li>
<li class="list-item"></li>
<li class="list-item"></li>
<li class="list-item"></li>
<li class="list-item"></li>
<li class="list-item"></li>
<li class="list-item"></li>
<li class="list-item"></li>
<li class="list-item"></li>
<li class="list-item"></li>
</ul>
<div class="background"></div>
</div>
</div>
<div class="option">
<input type="checkbox" id="transform" name="transform">
<label for="transform">transform</label>
</div>
Upvotes: 1