Grow your CSS skills. Land your dream job.

3D Inset Parallax Effect

Published by Chris Coyier

The following is a guest post by Joshua Bader. Joshua noticed that certain 3D effects on the web could benefit from adjusting perspective as the web page is scrolled. I'll let him explain.

People love to make flat things appear as if they're three-dimensional. There are two ways to pull off this effect in a 2D environment, shape and movement.

By give a flat shape the appearance of edges or sides, we can make buttons or other objects appear as if they are raised or sunken into the screen. This is seen even here on CSS-Tricks:

The movement effect is a bit trickier. In a three-dimensional world, objects closer to you move fast, while the ones further away move slow. The further away an object is, the slower it moves. This is called the parallax effect. The parallax effect has been used in all sorts of places for the past few years.

The first method, shape, works because it makes things appear to be popping out, or pushed away from you. The second method, movement, works because it makes things seem as if they are positioned in context with each other. But, what if we combined them. An object that looks sunken into the screen, like a shelf, and its edges shrink and grow, using the parallax effect, making the object appear as if it really is in 3D space.

First we need to add some markup on our page and give the items we want to affect the class of inset. Any block level elements will do. Here is a simple form with inputs that will have the effect applied.

<form>

  <label>
    First Name:
    <input class="inset" type="text" placeholder="Enter First Name" />
  </label>

  <label>
    Last Name:
    <input class="inset" type="text" placeholder="Enter Last Name" />
  </label>

  <label>
    Email Address:
    <input class="inset" type="text" placeholder="Enter Email Address" />
  </label>

  <label>
    Address:
    <input class="inset" type="text" placeholder="Enter Address" />
  </label>

  <label>
    Phone Number:
    <input class="inset" type="text" placeholder="Enter Phone Number" />
  </label>

</form>

Next we will need to style our inset elements. By giving the item slightly different color borders we can give the element the illusion of depth. This is because the joint between borders is an angle and with the different colors, it appears as if light is hitting different "sides" of a 3D area. This is the 'shape' method we mentioned earlier.

.inset {
  width: 100%;
  box-sizing: border-box;
  background: #f9f9f9;
  border-style: solid;
  border-width: 30px;
  border-top-color: #e5e5e5;
  border-right-color: #eee;
  border-bottom-color: #e5e5e5;
  border-left-color: #eee;
}

Now that the shape we want to manipulate is styled, we can use some simple jQuery to get the element's original border-width, window height, scroll position, target element's position on screen and convert that position to a percentage for the top edge and bottom edge.

var origBorderWidth = parseInt($('.inset').css('border-top-width')),
win = $(window),
windowHeight = win.height();

$('.inset').each(function() {
  var self = $(this),
    scrollHeight = win.scrollTop(),
    elementPosition = self.position(),
    positionPercentTop = Math.round((elementPosition.top - scrollHeight) / windowHeight * 100);
    positionPercentBottom = Math.round((elementPosition.top + self.outerHeight() - scrollHeight) / windowHeight * 100);
});

Once we have all of these numbers as variables, we can resize the top and bottom border-widths of each element based on its position on the screen.

var origBorderWidth = parseInt($('.inset').css('border-top-width')),
win = $(window),
windowHeight = win.height();

$('.inset').each(function() {
  var self = $(this),
    scrollHeight = win.scrollTop(),
    elementPosition = self.position(),
    positionPercentTop = Math.round((elementPosition.top - scrollHeight) / windowHeight * 100);
    positionPercentBottom = Math.round((elementPosition.top + self.outerHeight() - scrollHeight) / windowHeight * 100);

  self.css({
    'border-top-width' : origBorderWidth - (origBorderWidth * (positionPercentTop / 100)) + 'px',
    'border-bottom-width' : origBorderWidth * (positionPercentBottom / 100) + 'px'
  });
});

Now comes the fun part – recalculating each element's top and bottom border-widths while the page is scrolled. This will give us the desired parallax, 'movement' effect.

var origBorderWidth = parseInt($('.inset').css('border-top-width')),
  win = $(window);

function set3D() {
  var windowHeight = win.height();

  $('.inset').each(function() {
    var self = $(this),
      scrollHeight = win.scrollTop(),
      elementPosition = self.position(),
      positionPercentTop = Math.round((elementPosition.top - scrollHeight) / windowHeight * 100);
      positionPercentBottom = Math.round((elementPosition.top + self.outerHeight() - scrollHeight) / windowHeight * 100);

    self.css({
      'border-top-width' : origBorderWidth - (origBorderWidth * (positionPercentTop / 100)) + 'px',
      'border-bottom-width' : origBorderWidth * (positionPercentBottom / 100) + 'px'
    });
  });
};

win.on('load scroll resize', function() {
  set3D();
});

There you have it. To see the effect, keep you eye on a single element as it moves up and down the page when scrolled. You will see its top and bottom border-width change depending on its position on the page. Its subtle, but effective.

Check out this Pen!
Editors note: pretty neat eh? Perhaps with even more tech we could create a demo that did eye-tracking and adjusted the perspectives depending on where your eyes are in relation to the screen. Here's another example that adjusts perspective based on the mouse.

Comments

  1. Really awesome technique there. I never really thought about using a parallax effect like that before. Maybe as a more “fun” use of it would be a pair of eyes on a painting following the user :P Creeepy!

  2. Ian

    Really cool. And it looks OK in IE! :-) Thanks

  3. Pretty darn cool.

  4. Vlad Malik

    Tacky for my taste.

  5. Yeah looks great!! Tested in IE, looks great also! Cheers for the post

  6. Looks great!
    But I don’t see where it could be used in a real life client site.

  7. Hendra

    Cool! Though I think, for people who don’t get the effect, they’ll be like “Whoa, the textboxes are screwed up everytime I scroll!”

  8. Alexander

    Wonderful effect! I’m not a big fan of those 3D elements around this site and I prefer flat elements in my design, but this code would look awesome in some kind of 3D bookshelf.
    I just have one question – in your code you “grab” the window element twice without caching it – wouldn’t it be better to cache it outside the function block? Or isn’t this a big performance issue, grabbing it every time (and twice)?

  9. Badoup

    I really don’t like it, usually I always let my eyes looking at the same place on the webpage while I scroll, and I am fairly sure most of people do that. So the effect is not noticeable, it even feel weird and buggy :/

    Btw, eye tracking would be useless, even if you move your eyes the parralax doesn’t change. You’d have to do head tracking instead. Like that 3rd video: http://johnnylee.net/projects/wii/

  10. @scarfacedeb
    Hey, its common sense, you can use it on your clients site to make it more valuable, elegant design and more stylish.

  11. Jonny Sooter

    Hey Chris the new outlook email think doesn’t wrap text and makes it really hard to read your feed. Could you include a css line for that? Cus leave it to Microsoft and it’ll never happen.

  12. Ian

    In the “Editors Note”, the example mouse perspective works on codepin but not when I copy paste to separate html, css, and script.js files. Using the same browser (chrome). Also tried using the prefixed -webkit-perspective and -webkit-transform:rotate but the page just loads as a bunch of white squares so it’s not even showing the initial perspective:80px though it is loading in styles under dev tools. Looking in dev tools I see -webkit-perspective-origin changing as I move the mouse but the page it’s self is not changing.

    Anyone know why it would work in codepin but not in a actual html page?

    Thanks

  13. Lovely idea even if I think the indepth effect is a bit too strong to be visually appealing.

    Very lightweight code anyway, this is nice. :)

  14. This is pretty awesome. Thank you! But, I will have to say, you are relying on JavaScript to style elements on a page.

  15. As someone who tried to accomplish this effect in CSS alone, I can appreicate the need for JavaScript here. Kudos!

  16. bob

    Cute gimmick.

  17. Geewhiz256

    Hehe…this is awesome…thanks alot

  18. This is really great and amazing and especially for those people who are not wizards at creating these kinds of effects.

  19. I took me 10 minutes of intense concentration to figure out what the big deal is here – but now I get it. I can be quite obtuse but make up for it in sarcasm. The effect is almost too subtle here but I see where this could be used to do some cool webnastics. Off to CodePen. Thank you, Chris.

    +brian

  20. It was pretty subtle, had to scroll couple of times to notice it (or better, to really LOOK at it ;) ) and to me, anything subtle is a nice add-on to the skillset, might come in handy one day, might not. but the more examples of parallax effects, the better we’ll understand the dynamics.

  21. futekov

    There’s a small bug – the page height changes depending on your position in it.

    To see it scroll to the very bottom and then drag the scrollbar handle up – you’ll notice that the handle decreases in size and gets out of sync with your mouse.

    Turns out that when an inset element is off the screen and one of the borders is 0px, the other (bottom or top) continues to grow without limit. On larger pages this will be a serious issue.

  22. Pretty awesome, but using JavaScript making it bit tedious.

  23. Lennin Ruiz

    hi, do i need to put all of the code in my web page? html, js and css? thanks

  24. Super cool! I’m not sure if I’m a fan of the inset content with the headers “outside” of the inset. Looks a little weird. I could definitely see some design concepts implementing this in cool ways. I’m thinking smooth auto-scrolling from Point A to Point B of the page. The effect looks way awesome if you scrub with the scroll bar handle instead of the mouse wheel.

  25. subhojit777
    Permalink to comment#

    Thanks for sharing!! The good thing is it will also work in IE8

  26. Tried and tested on IE and FF. Works great. This is really awesome. Thanks

This comment thread is closed. If you have important information to share, you can always contact me.

*May or may not contain any actual "CSS" or "Tricks".