CSS background-size: cover + background-attachment: исправлено обрезание фоновых изображений.

У меня есть список фигур, содержащих фоновые изображения. Примерно так:

<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 установлено на cover и background-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


person Godwin    schedule 14.02.2014    source источник
comment
Только в демо он обрезан по горизонтали. Что еще ты имеешь в виду?   -  person Zach Saucier    schedule 15.02.2014
comment
@ZachSaucier, для меня он обрезан по горизонтали и вертикали в Chrome и Firefox. Взгляните на карты, если вы знаете, как должны выглядеть США, нет Новой Англии, Флориды или западных штатов.   -  person Godwin    schedule 15.02.2014
comment
(Если вы смотрите на него на относительно небольшой высоте)   -  person Godwin    schedule 15.02.2014


Ответы (9)


К сожалению, это просто артефакт того, как фиксированное позиционирование работает в CSS, и в чистом CSS нет никакого способа обойти это - вы должны использовать Javascript.

Причина, по которой это происходит, связана с комбинацией background-attachment: fixed и background-size: cover. Когда вы указываете background-attachment: fixed, это по существу заставляет background-image вести себя так, как если бы это было position: fixed изображение, что означает, что оно извлекается из потока страницы и контекста позиционирования и становится относительным к области просмотра, а не к элементу, для которого оно является фоновым изображением.

Таким образом, всякий раз, когда вы используете эти свойства вместе, значение cover вычисляется относительно размера области просмотра независимо от размера самого элемента, поэтому он работает так, как ожидалось, когда элемент имеет тот же размер, что и область просмотра, но обрезан. неожиданными способами, когда элемент меньше области просмотра.

Чтобы обойти это, вам в основном нужно использовать background-attachment: scroll и привязать прослушиватель событий к событию scroll в JS, который вручную обновляет background-position относительно того, как далеко окно было прокручено, чтобы имитировать фиксированное позиционирование, но при этом рассчитывать background-size: cover относительно элемента контейнера. а не область просмотра.

person Ennui    schedule 22.05.2014
comment
Есть ли какая-то конкретная причина, по которой в последнем абзаце вы предлагаете использовать калибровку CSS и позиционирование JS по сравнению с позиционированием CSS и размером JS? - person Will; 10.10.2014
comment
Не особо, в любом случае должно быть осуществимо, хотя я не пробовал, как вы упомянули. В основном потому, что я думаю, что позиционирование JS немного быстрее / проще для написания, чем определение размера JS в этом контексте. - person Ennui; 15.10.2014
comment
Есть более простой способ сделать это. Вместо использования background-position: fixed создайте ЭЛЕМЕНТ фона с position: fixed, а затем используйте background-size: cover с этим. Это покроет изображение в соответствии с размером элемента bg по сравнению с окном просмотра и при этом даст вам фиксированный фон. - person Matthew Dean; 25.11.2018

Для этого есть исправление jQuery: http://jsfiddle.net/QN9cH/1/ Я знаю, что это не оптимально, но, по крайней мере, работает :)

$(window).scroll(function() {
  var scrolledY = $(window).scrollTop();
  $('#container').css('background-position', 'left ' + ((scrolledY)) + 'px');
});
person Nick Noordijk    schedule 18.11.2014
comment
При прокрутке мыши он слишком ступенчатый. - person VVS; 11.03.2020

Ответ Ника Ноордейка направил меня на правильный путь, но мне нравится избегать скриптов, которые выполняют вычисления каждый раз, когда происходит событие прокрутки. Вот моя версия, которая выполняет расчет только при загрузке страницы или изменении размера экрана:

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, независимо от того, есть ли на вашем изображении избыток в любом направлении.

person squarecandy    schedule 13.08.2016
comment
Лучшее решение для адаптивной высоты в vh. - person VVS; 11.03.2020
comment
Отличное решение! - person Katarina Perović; 23.03.2020

background: url(<image>) center top no-repeat fixed;
background-size: auto <width size in %>;

Реального решения нет. Однако вы можете сделать размер изображения высотой auto вместо cover. И отрегулируйте размер ширины в% в вашем элементе, пока он не коснется границы.

Вы видите, что если вы измените размер окна, оно не будет обрезано. Вместо этого он всегда будет сохранять исходный формат размера изображения. С этим выше вы в основном "увеличиваете" его, но не "закрываете".

person Thielicious    schedule 12.09.2017

Размер фона: обложка; свойство действительно обрезает изображение, чтобы оно заполнило область и не оставило пустого места.

Размер фона: содержать; свойство определяет, какой размер больше, и масштабируется в соответствии с ним. Итак, если у вас есть блок размером 100 x 100 пикселей и фоновое изображение размером 200 x 150 пикселей, установка background-size как «содержит» приведет к масштабированию изображения до 100 x 75 пикселей. Однако в этом сценарии у вас будет пустое пространство, если соотношение сторон элемента отличается от изображения.

Вы также можете вручную контролировать, какая пропорция имеет приоритет, если вы знаете соотношение сторон изображения.

Итак, если вы знаете, что ваше изображение всегда 100x200 пикселей, это означает, что ширина всегда является малым размером, а высота - большим.

Теперь устанавливаем размер фона: 100% авто; гарантирует, что у вас не останется пустого места, но вы закончите обрезку. Если вы установите его на размер фона: авто 100%; это гарантирует, что не будет обрезки, и высота никогда не будет пуста (но ширина будет).

Если вы хотите обрезать и просто центрировать изображение, используйте background-position: 50% ;.

person kakashigr    schedule 17.02.2014
comment
Спасибо, но это не решает проблему, это просто объяснение того, как работает contain, но проблема, которую я пытаюсь решить, связана с cover. Проблема здесь в том, что cover обрезает как по горизонтали, так и по вертикали, когда изображение выше, чем область просмотра, я бы хотел, чтобы он обрезал одно или другое, но никогда и то и другое, и никогда ничего, как это делает contain. - person Godwin; 18.02.2014
comment
Вы можете использовать background-size: 100% auto; тогда. Это гарантирует, что ширина будет внутри элемента, а не обрезана и будет иметь 100% -ный размер. Если высота меньше высоты элемента, там будет пустое место. В противном случае он будет обрезан. - person kakashigr; 19.02.2014
comment
Я бы хотел, чтобы ширина и высота были всегда покрыты. - person Godwin; 19.02.2014
comment
Это может произойти двумя способами: 1. Если высота и ширина элемента такие же, как у изображения, установите размер фона на размер фона: 100% 100%; Однако, если они разные, ваше изображение будет иметь неправильное соотношение сторон. 2. Если элемент имеет разную ширину и высоту или один из них является переменным (изменяющимся), то вы не сможете заполнить область без обрезки. - person kakashigr; 21.02.2014
comment
Спасибо за попытку помочь, но я думаю, что вы неправильно поняли вопрос. Я хорошо знаю, как должен работать background-size, проблема в том, что когда также используется background-attachment, это, кажется, неожиданно меняет поведение. - person Godwin; 21.02.2014

Пример эффекта параллакса для отдельно расположенных элементов (не для полноэкранных элементов):

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);
        }
    });
});
person baduga    schedule 04.05.2018

Обновление в июле 2018 года: теперь есть однострочное исправление для этой проблемы

У меня была такая же проблема, пока я не прочитал эту статью , который научил меня, что я могу добавить эту строку в свой 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.

person Ryan    schedule 23.07.2018

Еще одно очень простое решение - использовать единицы vw и vh (которые, если вы не знали, имеют полностью сносная поддержка браузера в большинстве случаев). Например, вместо background-size: cover выполните background-size: auto 100vh.

Если вы не знакомы с vw и vh, это единицы области просмотра, и они соответствуют ширине области просмотра и высоте области просмотра. Значения соответствуют проценту от высоты или ширины области просмотра. Например, 50vw означает 50% ширины области просмотра.

Для наших целей мы можем просто указать, что фон составляет 100% высоты области просмотра.

Вот скрипка.

Если вам нужно приспособиться к разным соотношениям сторон, вы можете воспользоваться @media rules.

person Pete    schedule 24.07.2018
comment
Проблема все еще существует, даже когда я использую auto 100vh. - person Arad; 11.05.2019

Я сделал версию с Адаптивным фоновым изображением.

.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/

person Gino    schedule 30.11.2016