FIXED BACKGROUND EFFECT.

A simple template that takes advantage of the background-attachment CSS property to create a fixed background effect.

Sometimes you don’t need crazy javascript code to come up with creative and nice looking effects. Today’s snippet is all about a single CSS property: background-attachment. You can set the background to be fixed within the viewport (background-attachment: fixed;).

The new trick here is having the same element (in this case a phone) in the exact same position in all background images, so that while you scroll everything moves, but the phone.

The only assets you need are different images, with the same size and an element in common in the same position. Check out the preview video below to see how it looks like in action.

You can replace this iPhone image with other mockups from here.

Preview of Fixed Background Effect.

You might also like this.

How to Create a Fixed Background Effect.

Fixed Background Image

We need to create 3 files, one for CSS, HTML and other for JS. Make sure to attack Jquery CDN file or simply download it and add it into your <head> tag. There is one more JS file that you need to add for the styles. It’s called modernizer.

HTML.

The HTML structure is pretty basic: each section contains a .content element with the title and paragraph. Classes .img-1.img-2 etc are used to set up different background images in CSS. The .cd-vertical-nav is the arrow navigation (visible on bigger devices only). Data types have been used to identify in jQuery the sections/items of the slider.

CSS.

Couple of important things to bear in mind: iOS devices don’t like background-attachment: fixed; Therefore on small devices the fixed background effect won’t be visible. Also on small devices we don’t use CSS background-images yet, but we inject smaller photos of phones as ::after pseudo-elements of the .cd-content element.

Secondly, since we are using background-images, we can’t have 100% control on where the fixed element (phone in this case) will appear. This is hard to accept if you’re obsessed by pixel perfection, but I couldn’t find a solution for that.

JS.

Even though I do not like using Jquery nowadays, but I’ve still used it for the sake of experiment. I  used jQuery to implement a basic slider to navigate through the different sections (previous/next arrows and keyboard navigation). On window scroll, we update the arrows visibility (checkNavigation() function) and detect the section visible in the viewport (the .is-visible class is assigned using the checkVisibleSection() function). The nextSection() and prevSection() functions are used to navigate to the next/previous section.

Source Code.

HTML.

<section class="cd-fixed-background img-1" data-type="slider-item">
   <div class="cd-content">
      <h2>Title here</h2>
      <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Autem dolor beatae, laudantium eos fugiat, deserunt delectus quibusdam quae placeat, tempora ea? Nulla ducimus, magnam sunt repellendus modi, ad ipsam est.</p>
   </div>
</section>

<section class="cd-fixed-background img-2" data-type="slider-item">
   <!-- ... -->
</section>

<nav>
   <ul class="cd-vertical-nav">
      <li><a href="#0" class="cd-prev inactive">Next</a></li>
      <li><a href="#0" class="cd-next">Prev</a></li>
   </ul> <!-- cd-vertical-nav -->
</nav>

CSS.

html, body {
  height: 100%;
}

.cd-fixed-background {
  height: 100%;
  background-repeat: no-repeat;
  background-size: cover;
  background-position: center center;
  background-attachment: fixed;
}

.cd-fixed-background.img-1 {
  background-image: url("../img/img-1.jpg");
}

.cd-fixed-background.img-2 {
  background-image: url("../img/img-2.jpg");
}

.cd-fixed-background.img-3 {
  background-image: url("../img/img-3.jpg");
}

JS.

jQuery(document).ready(function($){
	var animating = false;

	//update arrows visibility and detect which section is visible in the viewport
	setSlider();
	$(window).on('scroll resize', function(){
		(!window.requestAnimationFrame) ? setSlider() : window.requestAnimationFrame(setSlider);
	});

	//move to next/previous section clicking on arrows
    $('.cd-vertical-nav .cd-prev').on('click', function(){
    	prevSection();
    });
    $('.cd-vertical-nav .cd-next').on('click', function(){
    	nextSection();
    });

    //move to next/previous using the keyboards
    $(document).keydown(function(event){
		if( event.which=='38' ) {
			prevSection();
			event.preventDefault();
		} else if( event.which=='40' ) {
			nextSection();
			event.preventDefault();
		}
	});

	//go to next section
	function nextSection() {
		if (!animating) {
			if ($('.is-visible[data-type="slider-item"]').next().length > 0) smoothScroll($('.is-visible[data-type="slider-item"]').next());
		}
	}

	//go to previous section
	function prevSection() {
		if (!animating) {
			var prevSection = $('.is-visible[data-type="slider-item"]');
			if(prevSection.length > 0 && $(window).scrollTop() != prevSection.offset().top) {
				smoothScroll(prevSection);
			} else if(prevSection.prev().length > 0 && $(window).scrollTop() == prevSection.offset().top) {
				smoothScroll(prevSection.prev('[data-type="slider-item"]'));
			}
		}
	}

	function setSlider() {
		checkNavigation();
		checkVisibleSection();
	}

	//update the visibility of the navigation arrows
	function checkNavigation() {
		( $(window).scrollTop() < $(window).height()/2 ) ? $('.cd-vertical-nav .cd-prev').addClass('inactive') : $('.cd-vertical-nav .cd-prev').removeClass('inactive');
		( $(window).scrollTop() > $(document).height() - 3*$(window).height()/2 ) ? $('.cd-vertical-nav .cd-next').addClass('inactive') : $('.cd-vertical-nav .cd-next').removeClass('inactive');
	}

	//detect which section is visible in the viewport
	function checkVisibleSection() {
		var scrollTop = $(window).scrollTop(),
			windowHeight = $(window).height();

		$('[data-type="slider-item"]').each(function(){
			var actualBlock = $(this),
				offset = scrollTop - actualBlock.offset().top;
			//add/remove .is-visible class if the section is in the viewport - it is used to navigate through the sections
			( offset >= 0 && offset < windowHeight ) ? actualBlock.addClass('is-visible') : actualBlock.removeClass('is-visible');
		});
	}

	function smoothScroll(target) {
		animating = true;
        $('body,html').animate({'scrollTop': target.offset().top}, 500, function(){ animating = false; });
	}
});

So this is how we will create it. Checkout the embed below.

CodeSandbox

Edit static
0 CommentsClose Comments

Leave a comment