Gap created by scrollbar in CSS parallax scrolling

2019-08-31 15:39发布

问题:

I'm a big fan of Keith Clark's pure CSS parallax technique and have been using it with some success on one of my own sites. Recently, however, I noticed that the background parallax elements, even in his own demo, do not abut the left side of the screen, but leave a gap, the size of which depends on how far back in the 3D space they are rendered.

The culprit, as far as I can tell, is the scrollbar. This scrollbar is supposed to be there (inside the parent parallax div), but it creates a difference between the size of the parent container (in which the 3d space is rendered) and the size of the containers' parallax children, which leaves room for that gap. I see it in the latest versions of Chrome, Firefox, and Safari (though I could have sworn I didn't see it there even a year ago).

This can be sort of fixed by applying a width: 100vw rule to each parallax group or layer. But while this clears the gap, it places the elements out of alignment with the default center of the view (and so with any elements that do not also have the 100vw rule applied to them). I would also like a solution that holds to the 'pure CSS' principle as much as possible.

HTML taken from the Keith Clark example:

<div class="parallax">
  <div class="parallax__group">
    <div class="parallax__layer parallax__layer--base">
      <div>Base Layer</div>
    </div>
    <div class="parallax__layer parallax__layer--back">
      <div>Background Layer</div>
    </div>
  </div>
</div>

CSS:

.parallax { // parent, page-level container
    height: 500px;
    height: 100vh;
    overflow-x: hidden;
    overflow-y: auto;
    -webkit-perspective: 300px;
    perspective: 300px;
    -webkit-perspective-origin-x: 100%;
    perspective-origin-x: 100%;
}

.parallax__group {
    position: relative;
    height: 500px;
    height: 100vh;
    -webkit-transform-style: preserve-3d;
    transform-style: preserve-3d;
}

.parallax__layer {
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    -webkit-transform-origin-x: 100%;
    transform-origin-x: 100%;
}

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

.parallax__layer--back {
    -webkit-transform: translateZ(-300px) scale(2);
    transform: translateZ(-300px) scale(2);
    z-index: 3;
}

回答1:

I came up with some solutions and put them into an example website here: https://dawaltconley.github.io/parallax-gap-fix/.

The problem only seems to arise with certain Mac system scrollbars, which appear when a user plugs in a USB mouse (how I found them) or changes Show scroll bars to Always in their settings. This scrollbar, unlike the transparent trackpad one, changes the inner width of the parallax page element, but not the total size, in which the 3D space for parallax elements is rendered.

Changing the perspective-origin and transform-origin properties to left realigns the parallax elements without resizing anything. This would be the ideal solution, except that Safari seems to calculate the 3D space differently than most modern browsers, and needs those properties set to center to align everything correctly.

The best solution I found for all browsers is to add the following rules to the .parallax__group element:

.parallax__group {
    margin-left: calc(100% - 100vw);
    margin-right: calc(100% - 100vw);
    padding-left: calc(100vw - 100%);
    padding-right: calc(100vw - 100%);
}

The difference between 100vw and 100%, from any immediate child of the .parallax element, is the width of the scrollbar. Setting a negative right margin to that width expands the parallax elements to the same width as the page, removing the gap at the edge of the screen. Setting this to the left margin stretches the elements a bit more than necessary, but is needed to align the center of the 3D space with that of the rest of the page.

It should be possible to offset this bloat by applying similar rules to the .parallax__layout class, but Safari's inconsistencies make this difficult, as a non-bloated element would need to be aligned perfectly to avoid gaps in the layout.

Incidentally, this solution has the advantage that it does nothing when the problematic scrollbars are absent. In that case, there is no difference between 100vw and 100%, so the computed style is identical to Kieth Clark's.