【问题标题】:CSS background-size: cover + background-attachment: fixed clipping background imagesCSS background-size:cover + background-attachment:固定裁剪背景图片
【发布时间】:2014-03-14 05:19:55
【问题描述】:

我有一个包含背景图像的数字列表。类似于以下内容:

<ul>
  <li>
    <figure style="background-image: url(...);"></figure>
  </li>
  <li>
    <figure style="background-image: url(...);"></figure>
  </li>
  <li>
    <figure style="background-image: url(...);"></figure>
  </li>
</ul>

每张图片的background-size 设置为coverbackground-attachment 设置为fixed

figure {
  width: 100%;
  height: 100%;
  background-size: cover;
  background-attachment: fixed;
}

当每个图形占据整个视口时,这可以正常工作,但如果有任何类型的偏移,背景图像就会被剪裁。

据我所知,这是设计使然 (https://developer.mozilla.org/en-US/docs/Web/CSS/background-size#Values)。

我希望图像可以垂直或水平剪辑,但不能同时进行,并以图形本身的大小居中。

我知道有 javascript 解决方案,但有没有办法使用 CSS 来做到这一点?

这是一个工作示例:http://codepen.io/Godwin/pen/KepiJ

【问题讨论】:

  • 仅在演示中被水平剪辑。你还有什么意思?
  • @ZachSaucier,对我来说,它在 Chrome 和 Firefox 中被水平和垂直剪辑。看看地图,如果你知道美国应该是什么样子,就没有新英格兰、佛罗里达或西部各州。
  • (如果您在相对较小的高度观看)

标签: css background-image background-attachment background-size


【解决方案1】:

不幸的是,这只是 CSS 中固定定位工作原理的产物,在纯 CSS 中无法绕过它 - 您必须使用 Javascript。

发生这种情况的原因是background-attachment: fixedbackground-size: cover 的组合。当您指定background-attachment: fixed 时,它实际上会导致background-image 表现得好像它是position: fixed 图像,这意味着它已从页面流和定位上下文中取出并变得相对于视口而不是相对于背景的元素图片。

因此,无论何时一起使用这些属性,cover 的值都是相对于视口的大小计算的,而与元素本身的大小无关,这就是为什么当元素的大小与视口,但当元素小于视口时会以意想不到的方式裁剪。

要解决这个问题,您基本上需要使用 background-attachment: scroll 并将事件侦听器绑定到 JS 中的 scroll 事件,该事件手动更新 background-position 相对于窗口滚动的距离以模拟固定定位但仍然相对于容器元素而不是视口计算background-size: cover

【讨论】:

  • 在上一段中您建议使用 CSS 大小调整和 JS 定位与 CSS 定位和 JS 大小调整有什么具体原因吗?
  • 不是特别是,任何一种方式都应该是可行的,尽管我没有按照你提到的方式尝试过。主要是因为我认为在这种情况下,JS 定位比 JS 大小写得更快/更容易。
  • 有一种更简单的方法可以做到这一点。不要使用背景位置:固定,而是使用位置:固定的背景元素,然后使用背景尺寸:覆盖。这将根据 bg 元素与视口的大小覆盖图像,并且仍然为您提供固定的背景。
【解决方案2】:

对此有一个 jQuery 修复:http://jsfiddle.net/QN9cH/1/ 我知道这不是最佳的,但至少它有效:)

$(window).scroll(function() {
  var scrolledY = $(window).scrollTop();
  $('#container').css('background-position', 'left ' + ((scrolledY)) + 'px');
});

【讨论】:

  • 鼠标滚动时它太步了。
【解决方案3】:

Nick Noordijk 的回答让我走上了正轨,但我喜欢避免在每次滚动事件发生时执行计算的脚本。这是我的版本,它仅在页面加载或屏幕尺寸更改时执行计算:

html:

<div class="fake-img"></div>

css:

.fake-img {
    display: block;
    height: 280px;  /* set the height here */
    width: 100%;
    background-image: url('http://example.com/path/to/image.jpg');
    background-repeat: no-repeat;
    background-position: center 68px;
    background-size: auto 50%; /* make a "best guess" here as a default */
    background-attachment: fixed;
    position: relative;
}

jQuery:

$(window).on('resize load orientationchange', function(){
    responsive_calc();
});

var responsive_calc = function(){

    // get the viewport height
    var h = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight;

    // get the element height
    var bannerHeight = $('.fake-img').height();

    // get the integer percentage value difference between them
    var bgHeightPercent = Math.ceil(bannerHeight/h*100);

    // set background-size height to match the element instead of the viewport
    $('.fake-img').css('background-size', 'auto ' + bgHeightPercent + '%');
}

请注意,这实际上只适用于横向“横幅”图像 - 使用 background-size: auto nn% 并没有 background-size: cover 的相同优势,无论您的图像在任一方向上是否有多余。

【讨论】:

  • vh中自适应高度的最佳解决方案。
  • 很好的解决方案!
【解决方案4】:
background: url(<image>) center top no-repeat fixed;
background-size: auto <width size in %>;

没有真正的解决方案。但是,您可以将图像的尺寸高度设为auto 而不是cover。并在元素中以 % 为单位调整宽度大小,直到它咬住边框。

您会看到,如果您调整窗口大小,它就不会被剪裁。相反,它将始终保持原始图像大小格式。有了上面的内容,您基本上可以“放大”它,但不会“覆盖”它。

【讨论】:

    【解决方案5】:

    背景尺寸:封面;属性确实在裁剪图像以使其填充该区域并且没有任何空白空间。

    背景尺寸:包含;属性正在确定哪个维度更大并根据该维度进行缩放。因此,如果您有一个 100px x 100px 的块和 200x150px 的背景图像,将 background-size 设置为 contains 会将图像缩放为 100x75px。但是,在这种情况下,如果元素的纵横比与图像的不同,您将有空白空间。

    假设您知道图像的纵横比,您还可以手动控制哪个比例具有优先权。

    因此,如果您知道您的图像始终为 100x200 像素,这意味着宽度始终是小尺寸,而高度始终是大尺寸。

    现在设置背景大小:100% 自动;将确保您不会得到空白空间,但最终会被剪裁。如果将其设置为 background-size: auto 100%;它将确保不会发生剪裁,并且高度永远不会有空白空间(但宽度会)。

    如果您确实想要剪裁并将图像居中,请使用 background-position: 50%;。

    【讨论】:

    • 谢谢,但这并不能解决问题,这只是对contain 工作原理的解释,但我要解决的问题是cover。这里的问题是cover 在图像高于视口时会在水平和垂直方向上进行裁剪,我希望它可以裁剪一个或另一个,但永远不会像contain 那样同时裁剪。
    • 你可以使用 background-size: 100% auto;然后。这将确保元素内部的宽度是多少,而不是被剪裁和 100% 的大小。如果高度小于元素的高度,它将在那里有空白空间。如果没有,它将被剪裁。
    • 我希望始终覆盖宽度和高度。
    • 这可能有两种情况: 1. 如果元素的高度和宽度与图片相同,则将 background-size 设置为 background-size: 100% 100%;但是,如果它们不同,您的图像将具有错误的纵横比。 2. 如果元素有不同的宽度和高度,或者其中一个是可变的(变化的),那么你不能在没有裁剪的情况下填充区域。
    • 感谢您提供帮助,但我认为您可能误解了这个问题。我非常清楚 background-size 应该如何工作,问题是当 background-attachment 也被使用时,它似乎意外地改变了行为。
    【解决方案6】:

    单独排列元素的视差效果示例(不适用于全屏元素):

    html:

    <div style="background-image: url('/path/to/image.jpg')">Content</div>
    

    css:

    #element_to_scroll {
        background-repeat: no-repeat;
        background-size: cover; 
        background-position-x: center; 
        background-position-y: 0;   
    }
    

    js:

    $(window).scroll(function() {
        var realImageWidth = 1280;
        var realImageHeight = 1024;
        var viewportBottom = $(window).scrollTop() + $(window).height();
        $('#element_to_scroll').each(function(){
            var scrollAmountPx = realImageHeight/(realImageWidth/$(this).outerWidth())-$(this).outerHeight();
            var elementOffsetFromBottom = viewportBottom-$(this).offset().top;
            var scrollAreaHeight = $(this).outerHeight() + $(window).height();
            if(elementOffsetFromBottom>0 && elementOffsetFromBottom<scrollAreaHeight) {
                var backgroundPositionOffset = Math.ceil(scrollAmountPx/scrollAreaHeight*elementOffsetFromBottom);
                //$(this).css('background-position-y',"-"+backgroundPositionOffset+"px");
                $(this).clearQueue().animate({'background-position-y':"-"+backgroundPositionOffset+"px"},50);
            }
        });
    });
    

    【讨论】:

      【解决方案7】:

      2018 年 7 月更新:现在有针对此问题的 1 行修复

      在阅读 this article 之前,我遇到了同样的问题,它告诉我可以将这一行添加到我的 CSS 中以使其工作:

      will-change: transform;

      成功了!

      现在我的 CSS 看起来像这样:

      .myClass{
          position: relative;
          background: hsla(300, 100%, 90%, 0.1);
          overflow: hidden;
          &::before {
              background-image: url(/img/bg.jpg);
              background-size: cover;
              background-size: cover !important;
              -webkit-background-size: cover !important;
              background-repeat: repeat repeat !important;
              background-attachment: scroll !important;//"fixed" is the desired effect, but mobile browsers find it too "expensive" and disabled it, so use "scroll"
              background-position: 30% 50%;
              @media(min-width: $screen-sm-min){
                  background-attachment: fixed !important;
                  background-position: 30% 20%;
                  will-change: transform;
              }//768
              content: "";
              display: block;
              position: absolute;
              top: 0;
              left: 0;
              width: 100%;
              height: 100%;
              opacity: 0.1;
          }
      }
      

      它适用于 Windows Chrome、Edge 和 Safari。但不是 Firefox。

      【讨论】:

        【解决方案8】:

        另一个非常简单的解决方案是使用vwvh 单位(如果您不知道,大多数情况下有totally passable browser support)。例如,不要使用background-size: cover,而是使用background-size: auto 100vh

        如果您不熟悉vwvh,它们是视口单位,它们对应于视口宽度和视口高度。这些值对应于视口高度或宽度的百分比。例如,50vw 表示视口宽度的 50%。

        出于我们的目的,我们可以简单地将背景设置为视口高度的 100%。

        Here's a fiddle.

        如果您需要适应不同的纵横比,您可以利用aspect-ratio @media rules

        【讨论】:

        • 即使我使用auto 100vh,问题仍然存在。
        【解决方案9】:

        我做了一个带有响应式背景图片的版本。

        .flexslider .slides > li {
          background-position: center center;
          background-repeat: no-repeat;
          -webkit-background-size: cover;
          -moz-background-size: cover;
          -o-background-size: cover;
          background-size: cover;
        }
        #slider,
        .alink {
          min-height: 300px
        }
        .alink {
          display: block
        }
        

        http://jsfiddle.net/onigetoc/grpku1gk/

        【讨论】: