Видео и z-индекс внутри масштабированного элемента: некоторые divs исчезают

У меня несколько странное поведение в Chrome и Safari. У меня есть масштабированный (transform: scale()) контейнер с видео и другими элементами внутри него. При некоторых масштабирования абсолютные позиционированные элементы с высоким z-индексом исчезают и не возвращаются снова.

Как я могу это исправить? Обратите внимание: я не могу дать элементу видео отрицательный z-индекс, и мне нужно использовать overflow: hidden; ,

пример

Я привел пример, который масштабирует внешний контейнер вверх и вниз. В значении масштаба .on-top элемент с классом .on-top (и текст "I should always be on top.") исчезает. При уменьшении масштаба снова появляется неожиданно.

Ссылка на exmaple: https://jsfiddle.net/iafiawik/Lcox1ecc/

enter image description here

Выводы

  • Кажется, что размер элемента имеет значение. Чем больше я это делаю, тем больше значение шкалы перед ее исчезновением.
  • Я также проверил, чтобы установить transform: scale(1.4) с CSS непосредственно на элемент, и поведение такое же.

Проблема не существует, если я:

  • Замените тег видео на div
  • Удалить position: absolute; от братьев и сестер до .on-top (то есть, .below)
  • Удалить overflow: hidden; от .content
  • Если я перемещаюсь. .on-top поэтому он помещается после тега видео в поток документа

(Но, конечно, ни один из этих обходных решений для меня не работает на самом деле из-за особых причин проекта. Я также не могу дать элементу видео отрицательный z-индекс, и мне нужно использовать overflow: hidden;)

Предлагаемые обходные пути сообщества (спасибо!)

  • Дайте тегу видео отрицательный индекс z (не могу этого сделать, потому что иногда у меня есть элементы, размещенные за видео)
  • Удалить overflow: hidden; (Я не могу удалить overflow: hidden;)

Браузеры

Я видел эту проблему в Chrome (Mac) и Safari (Mac).


Обновление 1

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

Обновление 2

Я ответил на свой вопрос, предоставив свое решение этой проблемы.

Обновление 3

Существует много ответов, которые либо изменяют z-index видео, либо добавляют translateZ к .on-top. Демонстрации показали, что оба этих подхода устраняют проблему. Однако, поскольку моя структура HTML - это результат визуального редактора HTML (длинный рассказ...), я не знаю, какие элементы будут там или если они должны быть впереди, ниже или рядом с видео. Поэтому я ищу решение, которое не требует изменений для отдельных элементов, находящихся внутри масштабированного элемента.

Ответы

Ответ 1

Проведя много времени, исследуя эту проблему и пытаясь найти множество разных подходов, я пришел к выводу, что никакое решение не устраняет мою проблему. Существуют решения, которые устраняют проблему, если вы можете управлять z-индексами исчезающих элементов, но я не могу этого сделать, поскольку структура HTML не известна (это результат редактора HTML). Я искал решение, которое не требовало бы изменения отдельных дочерних элементов для масштабируемого родителя, но я пока не нашел его.

Этот отчет об ошибках в значительной степени затрагивает мою проблему, но это не дает для этого исправления.

Я могу подтвердить, что это происходит из-за того, что элемент за пределами масштабированных контейнеров имеет оригинальную ширину и высоту:

Элемент отображается в scale(1.227) (красная граница указывает исходный размер #scaled):

enter image description here

... но не в scale(1.228):

enter image description here

Поэтому мое решение состоит в том, чтобы добавить еще один элемент упаковки за пределы масштабированного элемента, который не масштабируется, но обновлять его свойства width и height соответствии со своими значениями первого дочернего масштаба. Этот элемент имеет overflow: hidden; и предотвращает видимость элементов.

enter image description here

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

var goalScale = 140;
var startScale = 100;
var currentScale = 100;
var shouldScaleUp = true;
var container = document.getElementById("scaled");
var scaledContainer = document.getElementById("resized-container");
var scaleInfo = document.getElementById("scale-info");

function step() {
  var contentWidth = 1024;
  var contentHeight = 768;
  container.style.transform = "scale(" + (currentScale / 100) + ")";

  scaledContainer.style.width = contentWidth * ((currentScale / 100)) + "px";
  scaledContainer.style.height = contentHeight * ((currentScale / 100)) + "px";

  scaleInfo.innerText = "Scale: " + (currentScale / 100);

  if (currentScale === goalScale) {
    shouldScaleUp = false;
  }
  if (currentScale === startScale) {
    shouldScaleUp = true;
  }

  if (shouldScaleUp) {
    currentScale += 0.5;
  } else {
    currentScale -= 0.5;
  }

  window.requestAnimationFrame(step);
}

window.requestAnimationFrame(step);
#resized-container {
  position: fixed;
  width: 1024px;
  height: 768px;
  overflow: hidden;
  border: 10px solid red;
  top: 200px;
  left: 200px;
}

#scaled {
  background: #cccccc;
  width: 1024px;
  height: 768px;
  position: absolute;
  transform-origin: left top;
}

.content {
  height: 100%;
  position: relative;
  margin-left: auto;
  margin-right: auto;
  background: rgba(34, 34, 56, 0.2);
}

.below {
  position: absolute;
  width: 300px;
  height: 300px;
  right: 0px;
  top: 100px;
  background: purple;
  z-index: 1;
  opacity: 0.8;
}

.below-2 {
  z-index: 3;
  right: 100px;
}

.below-3 {
  z-index: 4;
  right: 400px;
}

.on-top {
  position: absolute;
  width: 50px;
  right: -30px;
  top: 150px;
  background: pink;
  z-index: 5;
  padding: 20px;
}

.on-top h1 {
  font-size: 20px;
}

#video {
  position: absolute;
  z-index: 4;
  width: 1024px;
  height: 768px;
  background: rgba(0, 0, 0, 0.4);
}
<div id="resized-container">
  <div id="scaled">
    <div id="scale-info">

    </div>
    <div class="content">
      <h2 class="below below-1">
        I have z-index 1
      </h2>

      <div class="on-top">
        <h1>
          I should always be on top.<br /> I have z-index 5
        </h1>
      </div>

      <h2 class="below below-2">
        I have z-index 3
      </h2>
      <video id="video" src="https://www.w3schools.com/html/mov_bbb.mp4"></video>
      <h2 class="below below-3">
        I have z-index 4
      </h2>
    </div>
  </div>
</div>

Ответ 2

Это похоже на ошибку в Chrome. Обратите внимание, что при масштабировании изображения инспектор элементов сообщает вам, что размер #scaled равен 1024x768:

Chrome Scale

Где, как в Firefox:

Firefox Scale

Теперь, по-видимому, Chrome использует неправильный размер, чтобы заключить, что .on-top полностью выходит за пределы .content и скрывает его из-за скрытого переполнения (он не должен этого делать, но, по-видимому, пытается оптимизировать любой элемент, отображающий над видео). Примеры:

Scale: 1.225
Parent width: 1254.40
Child left: 1254.40 - (100 + 90) * 1.225 = 1021.65
Result: less than 1024 (partially inside)

Scale: 1.230
Parent width: 1259.52
Child left: 1259.52 - (100 + 90) * 1.230 = 1025.82
Result: greater than 1024 (completely outside)

К сожалению, я не мог найти элегантное решение. В идеале вам следует пересмотреть HTML-разметку и CSS, возможно, выровняйте верхний элемент с левым краем. В крайнем случае вы можете перемещать элементы ближе к левому, используя прозрачную рамку:

var goalScale = 140;
var startScale = 100;
var currentScale = 100;
var shouldScaleUp = true;
var container = document.getElementById("scaled");
var scaleInfo = document.getElementById("scale-info");

function step() {
  container.style.transform = "scale(" + (currentScale / 100) + ")";
  scaleInfo.innerText = "Scale: " + (currentScale / 100);
  if (currentScale === goalScale) {
    shouldScaleUp = false;
  }
  if (currentScale === startScale) {
    shouldScaleUp = true;
  }
  if (shouldScaleUp) {
    currentScale += 0.5;
  } else {
    currentScale -= 0.5;
  }
  window.requestAnimationFrame(step);
}
window.requestAnimationFrame(step);
.scale-info {
  position: fixed;
  left: 0;
  top: 0;
}

#scaled {
  background: #cccccc;
  width: 1024px;
  height: 768px;
  position: fixed;
  left: 200px;
  top: 200px;
}

.content {
  height: 100%;
  overflow: hidden;
  position: relative;
  margin-left: auto;
  margin-right: auto;
  background: rgba(34, 34, 56, 0.2);
}

.below {
  position: absolute;
  width: 300px;
  height: 300px;
  right: 0px;
  top: 100px;
  background: purple;
  z-index: 1;
  opacity: 0.8;
}

.below-2 {
  z-index: 3;
  right: 100px;
}

.below-3 {
  z-index: 4;
  right: 400px;
}

.on-top {
  position: absolute;
  width: 50px;
  right: 100px;
  top: 150px;
  background: pink;
  z-index: 5;
  padding: 20px;
  /* a 200px border moves the element towards left */
  border-left: 200px solid transparent;
  background-clip: padding-box;
}

.on-top h1 {
  font-size: 20px;
}

#video {
  position: absolute;
  z-index: 4;
  width: 1024px;
  height: 768px;
  background: rgba(0, 0, 0, 0.4);
}
<div id="scale-info"></div>

<div id="scaled">
  <div class="content">
    <h2 class="below below-1"> I have z-index 1</h2>
    <div class="on-top">
      <h1> I should always be on top.<br> I have z-index 5</h1>
    </div>
    <h2 class="below below-2"> I have z-index 3</h2> <video id="video" src="https://www.w3schools.com/html/mov_bbb.mp4"></video>
    <h2 class="below below-3"> I have z-index 4</h2>
  </div>
</div>

Ответ 3

Здесь вы найдете: https://jsfiddle.net/Lcox1ecc/423/

Вам просто нужно добавить -webkit-transform: translateZ(0); в .on-top.

Счастливое кодирование!

Ответ 4

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

   <div id="top-container">
     <div class="on-top">
      <h1>
        I should always be on top.<br /> I have z-index 5
      </h1>
    </div>
   </div>

  #top-container {
    width: 100%;
    height: 100%;
    position: absolute;
    z-index: 5;
  }

https://jsfiddle.net/06oykj8o/4/

Ответ 6

Мне очень нравится ответ от Салмана А. Единственное, что приходит на ум, будет переписываться с position: relative. Но я не знаю, если это вариант.

Ответ 7

Я наткнулся на что-то похожее на эту последнюю неделю с позиционированием абсолютных элементов и преобразований...

Я не знаю, поможет ли это вам, но вот ссылка.

CSS-преобразование: перевод перемещений: фиксированный внутренний Div

В конце концов я исправил его с помощью преобразования: translateX (none) vs translateX (0).

Супер странное поведение наверняка, но ссылка дает еще несколько ссылок, чтобы помочь сделать вещи более ясными - как и в своем поведении в спецификации.

Ответ 8

Это происходит из-за переполнения скрыто. Здесь работает ссылка

https://jsfiddle.net/Lcox1ecc/322/

.content {
overflow:visible;
}