Why can I not scroll past my parallax without moving the mouse first?
First thing first, I would suggest adding a position: relative;
in the .parallax
in order to remove the double scroll.
The issue you're experiencing is a browser related scrolling issue and not a problem with your code (it does work on Firefox, but not on Chrome for example). So there's no "fix".
But there's clearly other ways to go about using parallax to prevent the issue from even happening like scroll each parallax manually without relying on browser's scroll.
Or you can simulate a click when reaching the top or the bottom to be able to overcome the issue :
$('div').on('scroll', chk_scroll);function chk_scroll(e) { var elem = $(e.currentTarget); if (elem[0].scrollHeight - elem.scrollTop() == elem.outerHeight()) { $("#parallax").trigger("click"); } else if (elem.scrollTop() == 0) { $("#parallax").trigger("click"); }}
As you have two scrollbars, the behaviour you described is intended. You can't change this behaviour. What you can do is to alter your solution so that it conforms to the intended behaviour (i.e. make one scrollbar appear only instead of two).
You're using perspective
and translateZ
to create the parallax effect. This means that the container of the "parallax-ed" items must have the perspective
property and the items themselves must be a direct descendent of the container. In the solution below, we're setting the perspective
attribute to the body
element. This means that the parallax only occurs when the body
element is scrolled. Without configuring the overflow
of the html
element, the scroll will appear on the html
element; thus, it is essential to set the html
element's overflow
attribute to hidden
. This will cause the scrollbar to appear on the body
element.
My answer is very similar to Dennis Ranish's answer. However, I use background-image
and background-position: center bottom
to make it so that the image is automatically centered regardless of its width (in your solution, you used margin-left: -1500px
to do this). Using this solution, you need not know the intrinsic size (original size) of the image and the image will always be centered even if you change the image later on. This is more flexible than using fixed units. Coincidentally, this requires me to use div
s and this works in your favour (because you mentioned a problem of using img
directly in Vue [do take a look further to see what the underlying problem exactly is; it might not simply be caused by the div
]).
Furthermore, I used top: 100%
and position: relative
to adjust it so that the content is located directly below the image. Here's the solution.
* { box-sizing: border-box; margin: 0;}html { overflow: hidden;}body { overflow-x: hidden; background-color: #fedcc8; height: 100vh; -webkit-perspective: 100px; perspective: 100px;}div[class^="parallax__layer"] { position: absolute; bottom: 0; left: 0; width:100vw; height:100vh; background-repeat: no-repeat; background-position-y: bottom; background-position-x: center;}h1 { font-family: Helvetica; color: #fff; text-align: center;}.parallax__content { background: #2d112b; top: 100%; position: relative; padding: 200px 100px;}.parallax__layer--zero { background: url('https://github.com/samdbeckham/blog/blob/master/dev/_assets/images/articles/firewatch/layer_0.png?raw=true'); -webkit-transform: translateZ(-300px) scale(4); transform: translateZ(-300px) scale(4);}.parallax__layer--one { background: url('https://github.com/samdbeckham/blog/blob/master/dev/_assets/images/articles/firewatch/layer_1.png?raw=true'); -webkit-transform: translateZ(-250px) scale(3.5); transform: translateZ(-250px) scale(3.5);}.parallax__layer--two { background: url('https://github.com/samdbeckham/blog/blob/master/dev/_assets/images/articles/firewatch/layer_2.png?raw=true'); -webkit-transform: translateZ(-200px) scale(3); transform: translateZ(-200px) scale(3);}.parallax__layer--three { background: url('https://github.com/samdbeckham/blog/blob/master/dev/_assets/images/articles/firewatch/layer_3.png?raw=true'); -webkit-transform: translateZ(-150px) scale(2.5); transform: translateZ(-150px) scale(2.5);}.parallax__layer--four { background: url('https://github.com/samdbeckham/blog/blob/master/dev/_assets/images/articles/firewatch/layer_4.png?raw=true'); -webkit-transform: translateZ(-100px) scale(2); transform: translateZ(-100px) scale(2);}.parallax__layer--five { background: url('https://github.com/samdbeckham/blog/blob/master/dev/_assets/images/articles/firewatch/layer_5.png?raw=true'); -webkit-transform: translateZ(-50px) scale(1.5); transform: translateZ(-50px) scale(1.5);}.parallax__layer--six { background-image: url('https://github.com/samdbeckham/blog/blob/master/dev/_assets/images/articles/firewatch/layer_6.png?raw=true'); -webkit-transform: translateZ(0px) scale(1); transform: translateZ(0px) scale(1);}
<div class='parallax__layer--zero'></div><div class='parallax__layer--one'></div><div class='parallax__layer--two'></div><div class='parallax__layer--three'></div><div class='parallax__layer--four'></div><div class='parallax__layer--five'></div><div class='parallax__layer--six'></div><div class='parallax__content'> <h1>Hello y'all, this parallax works!</h1></div>
What you are experiencing is not a problem. Your example has 2 different elements that have a scroll bar, one with-in the other. For-example if you look at one of the code snippets on this page you will experience the same behavior. This is intended. When you reach the bottom of an element you typically don't want to start scrolling the main page all of a sudden. Chrome for example waits for you to do another action like move the mouse or just pause for long enough, then it lets you scroll the parent element.
For the parallax effect you do not want multiple scroll-able elements; you only need one. Additionally you do not need all those extra divs. Here is how I would do it...
html { overflow: hidden;}body { margin: 0; background-color: #fedcc8; height: 100vh; overflow-x: hidden; -webkit-perspective: 100px; perspective: 100px;}img { z-index: -1; position: absolute; left: calc(-1500px + 50vw); border-top: calc(100vh - 455px) solid transparent; bottom: 0;}.content { background: #2d112b; min-height: calc(100vh - 25px); margin-top: calc(100vh - 2px); padding: 25px;}.text-box { padding: 15px 25px; border-radius: 15px; background: #fedcc8; max-width: 800px; margin: 100px auto 0; font: italic small-caps bold 16px/2 cursive}.parallax-0 { -webkit-transform: translateZ(-300px) scale(4); transform: translateZ(-300px) scale(4);}.parallax-1 { -webkit-transform: translateZ(-250px) scale(3.5); transform: translateZ(-250px) scale(3.5);}.parallax-2 { -webkit-transform: translateZ(-200px) scale(3); transform: translateZ(-200px) scale(3);}.parallax-3 { -webkit-transform: translateZ(-150px) scale(2.5); transform: translateZ(-150px) scale(2.5);}.parallax-4 { -webkit-transform: translateZ(-100px) scale(2); transform: translateZ(-100px) scale(2);}.parallax-5 { -webkit-transform: translateZ(-50px) scale(1.5); transform: translateZ(-50px) scale(1.5);}.parallax-6 { -webkit-transform: translateZ(0px) scale(1); transform: translateZ(0px) scale(1);}
<img class='parallax-0' src='https://github.com/samdbeckham/blog/blob/master/dev/_assets/images/articles/firewatch/layer_0.png?raw=true'/><img class='parallax-1' src='https://github.com/samdbeckham/blog/blob/master/dev/_assets/images/articles/firewatch/layer_1.png?raw=true'/><img class='parallax-2' src='https://github.com/samdbeckham/blog/blob/master/dev/_assets/images/articles/firewatch/layer_2.png?raw=true' /><img class='parallax-3' src='https://github.com/samdbeckham/blog/blob/master/dev/_assets/images/articles/firewatch/layer_3.png?raw=true'/><img class='parallax-4' src='https://github.com/samdbeckham/blog/blob/master/dev/_assets/images/articles/firewatch/layer_4.png?raw=true'/><img class='parallax-5' src='https://github.com/samdbeckham/blog/blob/master/dev/_assets/images/articles/firewatch/layer_5.png?raw=true'/><img class='parallax-6' src='https://github.com/samdbeckham/blog/blob/master/dev/_assets/images/articles/firewatch/layer_6.png?raw=true'/><div class='content'> <div class='text-box'> Lorem ipsum dolor sit amet, consectetur adipi scin elit. Etiam vulputate augue vel felis gravida porta. Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consectetur adipi scin elit. Etiam vulputate augue vel felis gravida porta. Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consectetur adipi scin elit. Etiam vulputate augue vel felis gravida porta. Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consectetur adipi porta. Lorem ipsum dolor sit amet. </div></div>
First thing is set the body's perspective so that there is parallax. Also it is important to set the html overflow to hidden because by default page scroll bars belong the the html element. Then for the elements that get the parallax effect it is important to make sure that get moved/scaled correctly to properly align. By default all the transformations happen based off the center point. You choose to put the images inside divs that are the size of the screen and align the images to the bottom, which works well. For simplicity in the example the images are given a top border to be the size of the screen but still be at the bottom. They are also centered, z-index set to -1 to be behind the rest of the page, and their bottom is set to 0 to handle very wide screens correctly (when the border is negative it does nothing). Finally there is a div which contains the rest of the page. It has a top margin of the screens height to allow room for the parallax mountains (I subtracted 2px because I notices a weird gap between it and the mountains on mobile).
If you want to have the mountains start somewhere higher up you can put them inside of divs like you have and align them at any height or if you don't want to add divs you can do it like this...
/*__img__*/border-top: calc(45vh - 455px / 2) solid transparent;border-bottom: calc(55vh - 455px / 2) solid transparent;/*__.content__*/margin-top: min(100vh - 2px, 100vh + 455px / 2 - 2px);
The important thing to take away from this example is that you have to have the scrolling only happen in one element. Hope this helps.