parallax scrolling is a bit jumpy - javascript

I've a small bit of parallax on a site I'm working on, it's working almost fine but the foreground divs are a bit jumpy when I scroll down the page.
Near the top of the page I have a div called #top-banner, it has a fixed background image, sitting within this div are two more within a row, the fisrt div / column has an image of a model & the second div has just text.
Below the #top-banner div is a div with a background image of a waterline, the desired effext is to have the waterline to cover the #top-banner as the user scrolls down, to make it seem as if the model, text & background are being covered by water.
I've got it working by using jQuery to change the css bottom property to make it seem that the two columns divs are moving down the page beneath the waterline at a similar speed to the scroll when the user scrolls down the page. I've set the speeds/increments to be slightly different to create a parallax effect.
It's working pretty well but is a bit jumpy, I've also tried to use the jQuery animate function but that is even more jumpy.
HTML
<section id="top-banner">
<div class="row">
<div class="col-2 prlx-1">
<img src="model.png"/>
</div>
<div class="r-col-2 prlx-2">
<h3>Lorem Ipsum</h1>
<p>More Ipsum</p>
</div>
</section>
<section id="hp-water-line"></section>
CSS
#hp-top-banner {
background: url(bg.png);
height: 600px;
background-attachment: fixed;
background-origin: initial;
background-clip: initial;
background-size: cover;
overflow: hidden;
width: 100%;
position: relative;
}
#hp-water-line {
background: url(water-line.png) no-repeat transparent;
min-height: 92px;
margin: 0 auto;
width: 100%;
position: relative;
top: -15px;
background-size: cover;
}
JS
$(document).ready(function(){
function parallax(){
var prlx_effect_1= -((window.pageYOffset / 4) *2.25 );
$('.prlx-1').css({"position": "relative","bottom":prlx_effect_1, "transition": "0s ease-in-out"});
// jQ('.prlx-1').css({"position": "relative"});
// jQ('.prlx-1').animate({"bottom":prlx_effect_1},"fast");
var prlx_effect_2= -(window.pageYOffset / 5 );
$('.prlx-2').css({"position": "relative","bottom":prlx_effect_2, "transition": "0s ease-in-out"});
}
window.addEventListener("scroll", parallax, false);
});
Updated JS based on Prinzhorn Comment
var requestAnimationFrame = window.requestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.msRequestAnimationFrame ||
window.oRequestAnimationFrame;
function onScroll() {
requestAnimationFrame(parallax);
}
function parallax(){
var prlx_effect_1= +(window.pageYOffset *.7).toFixed(2); // .55 is a good speed but slow
var prlx_str_1 = "translate3d(0, "+prlx_effect_1+"px, 0)";
jQ('.prlx-1').css({
"transform":prlx_str_1,
"-ms-transform":prlx_str_1,
"-webkit-transform":prlx_str_1
});
var prlx_effect_2= +(window.pageYOffset * 1 ).toFixed(2); // .33 is a good speed but slow
var prlx_str_2 = "translate3d(0, "+prlx_effect_2+"px, 0)";
jQ('.prlx-2').css({
"transform":prlx_str_2,
"-ms-transform":prlx_str_2,
"-webkit-transform":prlx_str_2
});
requestAnimationFrame(parallax);
}
window.addEventListener("scroll", onScroll, false);

I used to build parallax sites in a similar way,by using jquery to adjust the background-position or margins, however, i read this article a few months back which really changed the way I approach them.
He suggest using CSS translateZ and perspective to move containers or imagery forward and backwards into the 3 dimensional space to create 'real' parralax. This creates more fluid animations, and also renders better on mobile devices. I also personally find this much easier to execute.
I.E.
.parallax {
perspective: 1px;
height: 100vh;
overflow-x: hidden;
overflow-y: auto;
}
.parallax__layer {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
}
.parallax__layer--base {
transform: translateZ(0);
}
.parallax__layer--back {
transform: translateZ(-1px);
}
The only issue is, that using real 3Dimensional layers, means you have to be smart with your Z-Index to make sure your layers are not overlapping at the wrong places.
The article has an excellent demo, which you can view the side profile of the 3D space to see how the layers are distributed in the z-axis. Just click the 'debug' button in the top left corner.
http://keithclark.co.uk/articles/pure-css-parallax-websites/

Related

Repeated 2-layer css parallax background in Firefox with css "transform" and "perspective" (background not cut off at content height)

You are my last hope.
I decided to implement an update to the Page of my brothers store. One of the new features I wanted was a (simple^^) parallax background with two layers to create a kind of 3d-feeling while scrolling.
First I got it to work with a little bit of JS, adjusting the position on scroll events with a multiplicator. Then I noticed that the performance of the background is sticky, laggy, stuttering and doesn't really look well in Firefox. As far I could see this was because of the "Asynchronous Panning"-Feature of the browser.
Link to the JS-Version of the page update
So after a little time with the search engine of my choice I saw no option to disable or work around that feature and decided to start working on a CSS-only implementation on that site.
And guess which browser is not able to display everything as wanted? Firefox!
First I stuffed all my content into divs, so that - so my hope - a mutual parent div would enable me to use "height: 100%;" to scale the div's together. That didn't work as the the background was overflowing over my content. The problem was: Because I wanted the background images to repeat on the y-axis AND to move with a slower speed as the content I had to define a specific height of the background divs which is larger than the content height.
I even tried to set the height of the background divs with jQuery by
$(#background).height($(.main_content_container).height());
but the background always just turned out to be too large or too short.
After my idea with the parent div didn't work I started to work with the body and my content container itself to generate perspective. Could this have worked when i would've set all height to 100%? When I set height: 100%; I always got my viewport's height...
What I got now:
Creating the perspective and applying transform with body causing the overflow-y:
body {
overflow-y: auto;
overflow-x: hidden;
perspective: 1px;
width: 100%;
margin-left: 0px;
margin-top: 0px;
position: fixed;
height: 100vh;
transform-style: preserve-3d;
align-items: center;
align-content: center;
align-self: center;
text-align: left;
width: 100vw;
}
#background {
position: fixed;
bottom: 0;
left: 0;
transform: translateZ(-2px) scale(3);
width: 100vw;
background-size: 100vw;
background-image: url(websiteimage.png);
left: 0;
right: 0;
top: 0;
height: 500vh;
min-width: 100vw;
}
#background2 {
position: fixed;
bottom: 0;
left: 0;
transform: translateZ(-3px) scale(4);
background-image: url(websiteimage2.png);
background-size: 100vw;
left: 0;
right: 0;
top: 0;
height: 500vh;
min-width: 100vw;
opacity: 80%;
}
div.main_content_container {
transform: translateZ(0);
height: 100%;
background-color: transparent;
color: Silver;
max-width: 100vw;
width: 70%;
min-height: 100%;
}
In-vivo page (only startpage and only in dark mode is "working" at the moment)
Why does Chrome cut off the bottom of the background divs just as wanted and Firefox just create visible overflow?
Is there any chance to get one of my solutions to work fluent and formatted in Firefox?
I'm puzzling around for days now and thankful for every kind of idea/suggestion.
PS: This is my first post on StackOverflow. I hope I provided enough info and didn't break any rules as this site often helped me out of the hell of amateur webdesign.
PPS: I know my code is kind of a mess after all that puzzling but I'm playing around for days now
For all having the same problem:
I decided to try out several tweaks on my JS-implementation again and reached an improvement by adding
position: fixed;
background-attachment: fixed;
background-position: top;
to the background layers.
I also added a script by keith clark but I'm not sure if it takes any effect:
/*
Firefox super responsive scroll (c) Keith Clark - MIT Licensed
*/
(function(doc) {
console.log("Document executed")
var root = doc.documentElement,
scrollbarWidth, scrollEvent;
// Not ideal, but better than UA sniffing.
if ("MozAppearance" in root.style) {
// determine the vertical scrollbar width
scrollbarWidth = root.clientWidth;
root.style.overflow = "scroll";
scrollbarWidth -= root.clientWidth;
root.style.overflow = "";
// create a synthetic scroll event
scrollEvent = doc.createEvent("UIEvent")
scrollEvent.initEvent("scroll", true, true);
// event dispatcher
function scrollHandler() {
doc.dispatchEvent(scrollEvent)
}
// detect mouse events in the document scrollbar track
doc.addEventListener("mousedown", function(e) {
if (e.clientX > root.clientWidth - scrollbarWidth) {
doc.addEventListener("mousemove", scrollHandler, false);
doc.addEventListener("mouseup", function() {
doc.removeEventListener("mouseup", arguments.callee, false);
doc.removeEventListener("mousemove", scrollHandler, false);
}, false)
}
}, false)
// override mouse wheel behaviour.
doc.addEventListener("DOMMouseScroll", function(e) {
// Don't disable hot key behaviours
if (!e.ctrlKey && !e.shiftKey) {
root.scrollTop += e.detail * 16;
scrollHandler.call(this, e);
e.preventDefault()
}
}, false)
}
})(document);
Still no improvement on iOS Safari and mobile Firefox afaics.
Edit:
Thats the jQuery-function causing the effect here:
$(function() {
$(window).on('scroll', function() {
$('#background').css('background-position-y', $(window).scrollTop() * -.15);
});
});
$(function() {
$(window).on('scroll', function() {
$('#background2').css('background-position-y', $(window).scrollTop() * -.09);
});
});

How to implement momentum zooming

I would like to implement zooming like is done on this site (click the menu -> demo) or this site. As you can see you can zoom in and out with momentum.
So, my question was, how is that possible. My initial though was something like this
<main>
<section class="scrolling"></section>
<section class="overlay">
<button class="demo">Hello</button>
</section>
A .scrolling layer with an invisible scrollbar. The overlay positioned fixed on top of the .scolling layer with pointer-events: none
.scrolling {
height: 1000vh;
position: relative;
}
.overlay {
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
pointer-events: none;
}
and a bit of js
main.addEventListener('scroll', (e) => {
const scale = .... magic magic ....
overlay.style.transform = 'scale(...)';
});
DEMO
As you can see in the demo if you scroll the overlay has momentum-zoom.
But there is one huge problem with my demo
the button is not clickabe
the button doesn't show a mouse-pointer if you move over it
Any suggestions what I can do to fix the above issues ?
And are there libraries that support this feature? For example, I found Impetus.js but it doesn't seem to do momentum based on the scrolling gesture.

Append divs from bottom with scroll - Chat application example

I'm looking to append divs from the bottom. At a certain point, the vertical scroll should kick in so you can view divs that were appended earlier on. I'm trying to replicate a typical chat application and how messages come from the bottom. Here's the codepen...
http://codepen.io/jareko999/pen/yaQmgk
Before I put the code, I'll explain a couple of workarounds I've tried thus far. The pen currently has the container absolutely positioned with a bottom of 0. The problem, which is a pain, is that once the height goes beyond the height of the viewport, it won't scroll. This is the problem with the absolute positioning workaround.
Another workaround I've tried is doing a height of 100vh and display of flex with justify-content flex-end so the columns start at the bottom. The problem with this is that the scroll will always start from the top. I believe the solution is a scroll function that I've created to scroll to the bottom every time a new div is added. Would this be the best method? The key here is that I want to be able to scroll up to the older divs but have the newer divs start from the bottom. Think of a typical chat application like slack or messages or similar.
HTML
<button onclick="myFunction()">Hey here's a box</button>
<div id="container">
</div>
CSS
body {
margin: 0;
width: 100%;
}
button {
position: fixed;
z-index: 10;
}
#container {
position: absolute;
bottom: 0;
width: 100%;
}
#box {
width: 100%;
background: tomato;
opacity: 0;
height: 100px;
transition: .2s;
}
#box:last-child {
opacity: 1;
height: 0;
animation: .2s height linear forwards;
}
#keyframes height {
to {
height: 100px;
}
}
#box:nth-last-child(2) {
opacity: .8;
}
#box:nth-last-child(3) {
opacity: .6;
}
#box:nth-last-child(4) {
opacity: .4;
}
#box:nth-last-child(5) {
opacity: .2;
}
JS
function myFunction() {
var box = document.createElement("div");
box.setAttribute("id", "box");
var container = document.getElementById('container');
container.appendChild(box);
// window.scrollTo(0, document.body.scrollHeight || document.documentElement.scrollHeight);
}
Is there a better solution than the function I've created to scroll to the bottom? Much appreciated.
Ok, so after messing around with some JS, I figured it out. I love when that happens...
Here's the codepen...
http://codepen.io/jareko999/pen/yaQmgk
I created a setInterval function for scrolling to the bottom.
var myVar = setInterval(function(){
window.scrollTo(0, document.body.scrollHeight || document.documentElement.scrollHeight);
}, .1);
However, since this interval runs every .1 seconds, I need to kill it in order to scroll around the divs above (like old chat messages), but I want the animation (of the new div coming in) to finish. So, I created a setTimeout function to kill the setInterval function at 200 ms.
setTimeout(function(){
clearInterval(myVar);
}, 200);

On scroll up use the animate() and show the div

I have two navigation in my website. Both the navigation bars are fixed. Basically when I scroll up, I would like to use the animate() and show both the navigation bar in the page. How do I get the scroll up event and use that to animate the divs, like the Google search widget. I would really appreciate your help. Thank you.
html:
<div id="navbar_header">
some link
</div>
<div id="main_content">
<p>Some content...</p>
</div>
<div id="navbar_footer">
some link
</div>
css:
#navbar_header {
background: #22313F;
position: fixed;
top: 0px;
left: 0px;
width: 100%;
height: 40px;
}
#navbar_footer {
background: #22313F;
position: fixed;
bottom: 0px;
left: 0px;
width: 100%;
height: 40px;
}
Normally using the window for the scroll event should be sufficient, as it's big enough and the one element, that's being scrolled. If jQuery is loaded correctly, you could try something like this:
$(document).ready(function(){
var lastTopPosition = 0;
$(window).scroll(function(){
var topPosition = $(window).scrollTop();
if (topPosition > lastTopPosition ){
$("#navbar_header").stop(true).animate({'top':'-40px'}, 200);
$("#navbar_footer").stop(true).animate({'bottom':'-40px'}, 200);
} else {
$("#navbar_header").stop(true).animate({'top':'0px'}, 200);
$("#navbar_footer").stop(true).animate({'bottom':'0px'}, 200);
}
lastTopPosition = topPosition;
}
});
This piece of code gets the current position from the top everytime you scroll. If the distance gets bigger (scroll down) the two bars fadeout. If it's getting smaller (scroll up) it fades in. You can replace the FadeOut/In methods here with you animate() call too. A check, if the elements are displayed would be good here too, but I guess you can figure that one out ;-)
If I understood this right, something along the lines of:
$("#main_content").scroll(function(){
$('#navbar_header').show(300);
$('#navbar_footer').show(300);
});
Where show(300) will basically do a 300ms showing animation of your divs.

background image shaky on div resize

I want to use HTML to create an "opening" effect of one on top of another one.
After some research i figured out a way (see JSFiddle).
I now have the problem that the background image moves a little bit when the circle is resizing.
Can anyone help me figure out how to get the background image to stand still.
The image in the circle needs to keep same zoom level when opening.
The circle needs to be centered and the bottom half needs to be out of the window.
Circle css is this:
.circle {
width: 0px;
height: 0px;
z-index: 10;
position: absolute;
border-radius: 50%;
overflow: hidden;
margin: 0 auto;
left: 50%;
-moz-transform: translate(-50%, 50%);
-webkit-transform: translate(-50%, 50%);
bottom: 0;
-moz-transition: all 1.5s;
-webkit-transition: all 1.5s;
}
JSFiddle: http://jsfiddle.net/GmvUQ/2/
Update,
Let me explain a little more. i notice that my question is not clear enough.
I have a few screenshot for the effect i want to create:
1st frame:
2nd frame
The entire effect is already working but when the transition is in progress (The circle with the image is getting bigger or smaller) the image inside the circle moves a little bit.
This is probably because of the calculations that need to be done by Javascript / CSS positioning.
I would like some help how to let this image stand entirely still during resize transition.
Thanks!
DEMO: http://jsfiddle.net/GmvUQ/5/
Updated HTML
<div>
<div class="buttons">
<button onclick="changeboleto(0)">Click here</button>
<button onclick="changeboleto(500)">Click here</button>
<button onclick="changeboleto(1000)">Click here</button>
</div>
<div class="circle girl">
</div>
<div class="circle lamborghini">
</div>
</div>
Note that I've removed the nested </div> elements within each .circle. Instead I've added an extra class for each, which sets the background-image (and some positioning for them, if necessary).
Updated CSS
.circle {
width: 250px;
height: 250px;
z-index: 10;
position: absolute;
border-radius: 50%;
overflow: hidden;
margin: 0 auto;
background-repeat: no-repeat;
background-size: cover;
background-origin: content-box;
background-position: center center;
}
.lamborghini {
background-image: url(http://www.hdwallpapers.in/walls/2013_wheelsandmore_lamborghini_aventador-wide.jpg);
}
.girl {
background-image: url(http://www.hdwallpapers.in/walls/colorful_background_girl-normal5.4.jpg);
top: 50%;
}
.buttons {
position: relative;
z-index: 5;
}
I've moved most of the CSS in to the .circle class as it is common to both image sets. Pay special attention to the values for the background-* attributes.
Updated JQuery
function changeboleto(pix) {
circleHeight = pix;
circleWidth = pix;
$('.circle').animate({
'width' : circleWidth,
'height': circleHeight
}, 1500, 'linear');
//css('width', circleWidth).css('height', circleHeight);
changeCircleBackgroundToWindow();
}
function changeCircleBackgroundToWindow() {
windowWidth = $(window).width();
windowHeight = $(window).height();
$(".circle > div").animate({
'width' : windowWidth,
'height': windowHeight
}, 1500, 'linear');
$(".circle > div").animate({
'width' : windowWidth,
'height': windowHeight
}, 1500, 'linear');
//$(".circle-background").css("width", windowWidth).css("height", windowHeight);
//$(".circle-background2").css("width", windowWidth).css("height", windowHeight);
}
Rather than mix JQuery and CSS transitions I've lumped all the animation together in the JQuery.
I've used the animate() function and specified the easing method. The default easing is swing but I've used linear as this progresses the animation at a constant pace.
Edit
The solution above includes CSS that allows the image to scale with the animation. However you are requesting that the image stays at the same "zoom level" throughout.
To achieve this simply remove a line from the CSS, namely this one:
.circle {
...
background-size: cover;
...
}
I know this is 5 years too late, but I found this thread via a search engine and thought I'd provide my own thoughts.
This effect can also be achieved with clip-path, which is a bit more forgiving than jquery's animate (which can still result in image shakiness if you're animating certain/enough properties).
clip-path has the additional benefit of not needing javascript at all if you're doing, say, hovers rather than button clicks. It also results in a simpler HTML file.
I've made an updated version of the original question's jsfiddle, http://jsfiddle.net/GmvUQ/13/ which demonstrates doing this with clip-path. It's still using jquery to handle the button clicks, but the "magic" all happens via CSS transitions, rather than javascript animations.
JQuery:
function changeboleto(pix) {
...
$('.circle-background').css('clip-path', 'circle(' + pix/2 + 'px at 50% 100%)');
}
CSS (including original CSS from original fiddle):
.circle-background {
position: absolute;
z-index: 10;
clip-path: circle(0% at 50% 100%);
background:url(http://www.hdwallpapers.in/walls/colorful_background_girl-normal5.4.jpg);
background-size: cover;
-webkit-transition: all 1.5s;
-moz-transition: all 1.5s;
bottom: 0%;
left: 50%;
-moz-transform: translateX(-50%);
-webkit-transform: translateX(-50%);
}
What this does is simply cause the CSS to transition on the clip-path property, animating the circle expansion. Because the image itself never moves, just the boundaries between which it displays, it never shakes.
Full screen demo
JSFiddle: http://jsfiddle.net/7bP7Z/4/ (Click around to see things grow)
Okay, so now that the question has more clarification I have revisited the drawing board and have come up with a better solution.
HTML
<div class="circle">
<div class="circle-overlay"></div>
<img src="http://www.hdwallpapers.in/walls/2013_wheelsandmore_lamborghini_aventador-wide.jpg" />
</div>
<div class="circle">
<div class="circle-overlay"></div>
<img src="http://www.hdwallpapers.in/walls/colorful_background_girl-normal5.4.jpg" />
</div>
Note the changes to the structure:
A containing element
An "overlay" element
An </img>
CSS
.circle {
position: relative;
overflow: hidden;
}
.circle-overlay {
position: absolute;
left: 50%;
margin-left: -150px;
bottom: -150px;
border-radius: 50%;
width: 300px;
height: 300px;
box-shadow: 0px 0px 0px 3000px white;
}
Nice and simple CSS!
The majority of the code is used to position our .circle-overlay class. This class provides a transparent circle (using border-radius) and utilises one of my favourite new CSS features - box-shadow - to apply a solid, white "outline" of an arbitrarily large value that covers the image below it. Have a play with the colour and size (adjust the 300px value) of the box-shadow to see how this works.
JQuery
$('.circle').click(function() {
var c = $(this).children('.circle-overlay');
var w = c.width() + 100;
c.animate({
'width' : w,
'height': w,
'bottom': (w*-0.5),
'margin-left': (w*-0.5)
}, 500, 'linear');
});
Once again, keeping things nice and simple!
The above JQuery performs a very simple task. It increases the size of the circle-overlay whilst maintaining its bottom, centre positioning on every click.
This should be a very smooth animation and the image should not "judder" or "shake" as the image is not being manipulated.

Categories

Resources