web-dev-qa-db-fra.com

Vidéo et z-index dans l'élément scaled: certaines divs disparaissent

J'ai un comportement quelque peu étrange dans Chrome et Safari. J'ai un conteneur mis à l'échelle (transform: scale()) avec une vidéo et d'autres éléments à l'intérieur. À certaines échelles, les éléments de positionnement absolu avec un indice z élevé disparaissent et ne reviennent plus.

Comment puis-je résoudre ce problème? Notez que je ne peux pas attribuer d'indice z négatif à l'élément vidéo et que je dois utiliser overflow: hidden;.

Exemple

J'ai créé un exemple permettant de monter et de descendre le conteneur le plus à l'extérieur. À une valeur d'échelle spécifik, l'élément avec la classe .on-top (et le texte "I should always be on top.") disparaît. Lorsque vous réduisez à nouveau, il apparaît soudainement.

Lien vers exmaple: https://jsfiddle.net/iafiawik/Lcox1ecc/

 enter image description here

Conclusions

  • Il semble que la taille de l'élément compte. Plus je le fais grand, plus la valeur d'échelle est grande avant qu'elle ne disparaisse.
  • J'ai également essayé de définir transform: scale(1.4) avec CSS directement sur l'élément et le comportement est identique.

Le problème n'existe pas si I:

  • Remplacer la balise video par un div
  • Supprimer position: absolute; de la fratrie en .on-top (c'est-à-dire .below)
  • Supprimer overflow: hidden; de .content
  • Si je déplace .on-top pour qu'il soit placé après la balise video dans le flux de documents

(Mais bien sûr, aucune de ces solutions de contournement ne fonctionne pour moi en réalité pour des raisons spécifiques au projet. Je ne peux pas non plus donner à l'élément vidéo un index z négatif et I need d'utiliser overflow: hidden;.)

Solutions de contournement suggérées par la communauté (merci!)

  • Attribuez à la balise vidéo un index z négatif (vous ne pouvez pas le faire car des éléments sont parfois placés derrière la vidéo)
  • Supprimer overflow: hidden; (je ne peux pas supprimer overflow: hidden;)

Navigateurs

J'ai vu ce problème dans Chrome (Mac) et Safari (Mac).


Mise à jour 1

On dirait que ce rapport de bogue couvre assez bien mon problème. Cependant, cela ne résout pas le problème.

Mise à jour 2

J'ai répondu à ma propre question en apportant ma solution à ce problème.

Mise à jour 3

Il y a beaucoup de réponses qui entrent dans ce qui modifie le z-index de la vidéo ou ajoute translateZ à l'élément .on-top. Les démos ont montré que ces deux approches résolvent le problème. Cependant, comme ma structure HTML est la sortie d’un éditeur HTML visuel (histoire longue ...), je ne sais pas quels éléments seront présents ni s’ils devraient être devant, au-dessous ou à côté d’une vidéo . Par conséquent, je recherche une solution ne nécessitant pas de modification d'éléments individuels contenus dans l'élément mis à l'échelle.

18
iafiawik

Après avoir passé beaucoup de temps à chercher ce problème et à essayer différentes approches, j'en suis venu à la conclusion qu'aucune solution ne résout mon problème. Il existe des solutions qui résolvent le problème si vous êtes capable de contrôler les z-index des éléments qui disparaissent, mais je suis incapable de le faire car la structure du code HTML n'est pas connue (c'est la sortie de l'éditeur HTML ). Je cherchais une solution qui ne nécessiterait pas de modifications pour les enfants d'un parent échelonné, mais je n'en ai trouvé aucune jusqu'à présent.

Ce rapport de bogue traite assez bien de mon problème, mais il ne fournit pas de solution.

Je peux confirmer que cela se produit car l'élément est en dehors de la largeur et de la hauteur d'origine des conteneurs mis à l'échelle:

L'élément est visible à scale(1.227) (la bordure rouge indique la taille originale de #scaled):

 enter image description here

... mais pas à scale(1.228):

 enter image description here

Ma solution est donc d'ajouter un autre élément d'habillage en dehors de l'élément mis à l'échelle qui n'est pas mis à l'échelle mais d'obtenir ses propriétés width et height mises à jour conformément à ses premières valeurs d'échelle enfant. Cet élément a overflow: hidden; et empêche les éléments d'être visibles.

 enter image description here

Ce n’est pas une solution parfaite, car il est possible que le décalage entre l’élément mis à l’échelle et l’élément enveloppant le plus à l’extérieur (problèmes d’arrondis) soit minime, mais c’est ce que je peux faire de mieux compte tenu des circonstances. 

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>

3
iafiawik

Cela ressemble à un bug dans Chrome. Notez que lorsque vous redimensionnez l'image, l'inspecteur d'élément vous répète que la taille de #scaled est de 1024x768:

 Chrome Scale

Où comme dans Firefox:

 Firefox Scale

Apparemment, Chrome utilise une taille incorrecte pour conclure que .on-top estcomplètementextérieur .content et le cache en raison du débordement masqué (cela ne devrait pas être le cas, mais apparemment, il tente d'optimiser éloignez tout élément qui apparaît au-dessus d’une vidéo) Exemples:

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)

Malheureusement, je n'ai pas pu trouver de solution élégante. Idéalement, vous devriez réviser votre balisage HTML et CSS, en alignant éventuellement l’élément supérieur sur Edge gauche. En dernier recours, vous pouvez déplacer les éléments plus vers la gauche en utilisant une bordure transparente:

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>

8
Salman A

Ici vous allez: https://jsfiddle.net/Lcox1ecc/423/

Vous devez juste ajouter -webkit-transform: translateZ(0); à la classe .on-top.

Bon codage!

4
jmcgriz

Une approche, si vous pouvez modifier un peu votre html, consiste à envelopper vos éléments problématiques dans un conteneur de la même taille que la vidéo et le conteneur, avec le z-index approprié. De cette façon, vous obtenez des couches claires de la même taille et des mêmes positions, dans lesquelles vous pouvez positionner des éléments plus complexes. Comme ceci par exemple:

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

2
Julien Grégoire

J'ai fait cette solution de contournement en mettant z-index:-1; sur video

https://jsfiddle.net/Lcox1ecc/312/

1
Sandwell

J'aime beaucoup la réponse de Salman A . La seule chose qui me vient à l’esprit, serait de réécrire avec position: relative. Mais je ne sais pas si c’est une option.

1
Domenik Reitzner

Je suis tombé sur quelque chose de similaire à la semaine dernière en positionnant des éléments absolus et en transformant ... 

Je ne sais pas si cela va vous aider, mais voici un lien.

Transformation CSS: traduire les mouvements de position: fixe div interne

Finalement, je l'ai corrigé en utilisant une transformation: translateX (none) vs translateX (0).

Super étrange comportement à coup sûr, mais le lien donne quelques liens supplémentaires pour aider à rendre les choses plus claires - comme dans son comportement par spécification. 

0
Sten Muchow

Cela se produit parce que le débordement est caché.Voici le lien de travail

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

.content {
overflow:visible;
}
0
Pramod Pandey