(CSS) Make a background image scroll slower than everything else
Asked Answered
P

8

55

here is is my CSS code for the body:

body {
  padding: 0;
  margin: 0;
  background-image: url("../images/background.jpg");
  background-repeat: no-repeat;
  background-color: grey; 
  background-size: 100%;
}

What I want to do is make it so that the image scrolls slower than everything else on the page to make a simple parallax effect. I've looked online and all of the examples I've seen are much more complicated than what I want.

Paraplegia answered 24/3, 2015 at 18:19 Comment(2)
Not possible with CSS.Cromorne
this will help github.com/pederan/Parallax-ImageScroll but it is not possible with css like everyone saidEmblazonry
E
98

I stumbled upon this looking for more flexibility in my parallax speed that I have created with pure CSS and I just want to point out that all these people are wrong and it is possible with pure CSS It is also possible to control the height of your element better.

You will probably have to edit your DOM/HTML a bit to have some container elements, in your case you are applying the background to the body which will restrict you a lot and doesn't seem like a good idea.

http://keithclark.co.uk/articles/pure-css-parallax-websites/

Here is how you control the height with Viewport-percentage lenghts based on screen size:

https://stanhub.com/how-to-make-div-element-100-height-of-browser-window-using-css-only/

  .forefront-element {
    -webkit-transform: translateZ(999px) scale(.7);
    transform: translateZ(999px) scale(.7);
    z-index: 1;
  }

  .base-element {
    -webkit-transform: translateZ(0);
    transform: translateZ(0);
    z-index: 4;
  }

  .background-element {
    -webkit-transform: translateZ(-999px) scale(2);
    transform: translateZ(-999px) scale(2);
    z-index: 3;
  }

Layer speed is controlled by a combination of the perspective and the Z translation values. Elements with negative Z values will scroll slower than those with a positive value. The further the value is from 0 the more pronounced the parallax effect (i.e. translateZ(-10px) will scroll slower than translateZ(-1px)).

Here is a demo I found with a google search because I know there are a lot of non-believers out there, never say impossible:

http://keithclark.co.uk/articles/pure-css-parallax-websites/demo3/

Elbertine answered 10/6, 2016 at 12:10 Comment(4)
I wanted to clarify that this is still not a good solution if you're concerned about browser compatibility. On latest Firefox/Chrome of the time of this writing, there's is a white bar that appears to the left of each container. Also, on Chrome, the background elements appear very fuzzy. While the effect occurs, the visual appearance is still lacking in comparison to JS solutions, and therefore this pure CSS solution does not work.Reseat
Also this altering the scroll effect of the mobile devices. i:e iPad and iPhone won't continue scrolling after you release the touch which feels sticky. However unlike Jquery parallax techniques, this works in all browsers including safari.Diandiana
Scrolling issue is still there - any option to get rid of this "stickiness"?Brushwork
@Reseat I don’t think the white bar is due to a compatibility issue; I didn’t understand what you where talking about when you say that ‘the background elements appear very fuzzy’, because it’s not the case here; and as for browser compatibility, IE11 doesn’t support this method (but who is still using IE nowadays?), but browser compatibility for CSS 3D transforms is—I think—good enough. Browser compatibility wasn’t a bother in the question, but a CSS solution was asked.Mandrill
A
25

I know this is very late, but I wondered wether it would not be working easier than the codes above and invested 15 mins of my live.
Here is my code:

document.getElementById("body").onscroll = function myFunction() {  
    var scrolltotop = document.scrollingElement.scrollTop;
    var target = document.getElementById("main1");
    var xvalue = "center";
    var factor = 0.5;
    var yvalue = scrolltotop * factor;
    target.style.backgroundPosition = xvalue + " " + yvalue + "px";
  }
#main1 {
    background-image: url(https://images.unsplash.com/photo-1506104489822-562ca25152fe?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9);
    background-position: top center;
    background-attachment:scroll;
    background-size: cover;
    height: 80vh;
    width: 100%;
}
#placeholdersoyoucanscroll{
    height:100vh
}
<body id="body">
      <main>
        <div id="main1">
        </div>
        <div id="placeholdersoyoucanscroll">
        </div>
    </main>
</body>
Animalist answered 10/6, 2019 at 12:7 Comment(4)
Works perfect and is so much simpler than dealing with transforms.Metallophone
Perfect answer! Works like a charm. If you want to change the background image scrolling direction, replace this line target.style.backgroundPosition = xvalue + " " + yvalue + "px"; with this code target.style.backgroundPosition = xvalue + " -" + yvalue + "px";Forgave
Thanks Tim! Changing background-attachment: scroll to background-attachment: fixed, fixes a small lag and makes it even better.Saucier
"I know this is very late" - not to worry, this isn't Reddit. We don't punish people for contributing relevant, useful knowledge.Latchstring
L
11

you can use something simple like here: html:

Motion

css:

body {

  min-height:4000px;
  background-image: url("http://www.socialgalleryplugin.com/wp-content/uploads/2012/12/social-gallery-example-source-unknown-025.jpg");
}

h1 {margin-top:300px;}

js:

(function(){

  var parallax = document.querySelectorAll("body"),
      speed = 0.5;

  window.onscroll = function(){
    [].slice.call(parallax).forEach(function(el,i){

      var windowYOffset = window.pageYOffset,
          elBackgrounPos = "50% " + (windowYOffset * speed) + "px";

      el.style.backgroundPosition = elBackgrounPos;

    });
  };

})();

Here is jsfiddle

Luciusluck answered 8/10, 2017 at 21:12 Comment(1)
Would there be a possibility to leave the background's horizontal placement intact? I'm using this for a hero image that I need to nudge around with background-position depending on the screen size. Otherwise, great solution!Aeriela
M
4

If you want to apply a high background image, use this JS:

(function () {
        var body = document.body,
                e = document.documentElement,
                scrollPercent;
        $(window).unbind("scroll").scroll(function () {
            scrollPercent = 100 * $(window).scrollTop() / ($(document).height() - $(window).height());
            body.style.backgroundPosition = "0px " + scrollPercent + "%";
        });
})();
Monogyny answered 6/4, 2016 at 19:23 Comment(2)
this is quite simple and works nicely. also unlikepure css solutions it isnt blurry and terrible looking. +1Sidney
I would upvote this answer if this included a raw javascript approach that "did not require" attaching jquery to the project.Rhythmics
R
2

I also was looking for a modern and intuitive approach to adjusting my background scroll speed. You should try out the Simple Parallax Scrolling jQuery Plug-in, inspired by Spotify.com.

Once you have it attached to your project, along with a big image to play with, this is one of many ways to set it up.

The HTML | setup your containers and basic parallax attributes on element.

<main>
<section>Some Content Above it</section>
 <div class="parallax-container" data-parallax="scroll" data-position="top" style="height: 80vh;"></div>
<section>Some Content Below it</section>
</main>

The jQuery | Here, you can play with additional parameters based on the docs

function parallaxjs() {
    $('.parallax-container').parallax({
        imageSrc: '/<path-to-your-image>/<your-big-image>.jpg',
        naturalWidth: 1071, //your image width value
        naturalHeight: 500, //your image height value
        bleed: 0,
    });
}

(function () {
  parallaxjs();
})($);
Rhythmics answered 4/2, 2020 at 20:30 Comment(0)
N
0

Agree that it isn't possible just with css, because you have to compute image and document height ratio. I also like this effect, that's why created simple function that does just that. Here is function and its call on scroll event:

$(window).on('scroll', function() {
	smoothBackgroundScroll("relative/image/url");
});

function smoothBackgroundScroll(imgsrc) {
	function loadImageHeight(url, width) {
		var img = new Image();
		img.src = url;
		if (width) {
			img.width = width;
		}
		return img.height;
	}

	var dh, wh, ih, st, posy, backh, backw;
	if (!this._smoothBackgroundScroll) {
		var bcksize = $(document.body).css('background-size');
		var bmatch = /(\w+)\s*(\w+)/.exec(bcksize);
		if (!bmatch || bmatch.length < 3) {
			backh = loadImageHeight(imgsrc)
		} else {
			backh = parseInt(bmatch[2]);
			if (isNaN(backh)) {
				backw = parseInt(bmatch[1]);
				backh = loadImageHeight(imgsrc, parseInt(backw));
			}
		}
		this._smoothBackgroundScroll = {
			dh: $(document).height()
			, wh: $(window).height()
			, ih: backh
		}
	}
	dh = this._smoothBackgroundScroll.dh;
	wh = this._smoothBackgroundScroll.wh
	ih = this._smoothBackgroundScroll.ih;
	st = $(document).scrollTop();
	posy = (dh - ih) * st / (dh - wh);
	document.body.style.backgroundPosition = 'center ' + posy + 'px';
}

You can find it here along with example and visual explanation what's really going on with image and document:

Smooth background image scrolling

Neoterize answered 19/7, 2015 at 13:54 Comment(1)
Not true, it's been possible with CSS for years. See hereProximal
U
0

I realize that this is an old question, however, I recently stumbled upon this problem myself and spent a lot of time trying to find the best working code. Everything I found was either too complicated or didn't work without lagging a lot, especially in Chrome. As pointed out by others, the problem cannot be solved by pure CSS, but I made my own simple AngularJS directive to solve the problem:

app.directive("paraBack", ['$window', function ($window) {
  return function(scope, element, attrs) {
    element.css("background-image", "url("+attrs.paraBack+")"); // Apply the background image with CSS
    element.css("background-attachment", "fixed"); // Disable background scrolling

    var max = Infinity;

    var image = new Image(); // Create a JavaScript image so that the code below can be run when the background is loaded
    image.src = attrs.paraBack;
    image.onload = function () {
      max = image.height - window.innerHeight; // Stop scrolling after the bottom of the picture is reached
      var xOffset = -(image.width/2-window.innerWidth/2);
      element.css("background-position-x", xOffset+'px'); // Horizontally align the background
    }

    var scrollHandler = function () {
      var offset = Math.floor(this.pageYOffset*0.1); // Set background to scroll at 10% of scrolling speed
      if (offset<max) {
        element.css('background-position-y', '-'+offset+'px'); // If not the bottom of the image is reached, move the background (scroll)
      }
    };

    angular.element($window).on('scroll', scrollHandler); // Set the defined scrollHandler function to be ran when user scroll

    scope.$on('$destroy', function () {
      angular.element($window).off('scroll', scrollHandler); // Unbind the function when the scope is destroyed
    });
  };
}]);

It can be used in the html like this:

<body para-back="url/to/image">

If you want to see an example of what it looks like, you can visit this page.

Uticas answered 12/6, 2016 at 17:39 Comment(0)
I
-3

Best way is to do it with jQuery. There are a lot of sites about this, for example:

Ithaman answered 24/3, 2015 at 18:39 Comment(1)
Not true, it's been possible with CSS for years. See hereProximal

© 2022 - 2024 — McMap. All rights reserved.