缩放元素内的视频和z-index:一些div消失了

21
我在Chrome和Safari中遇到了奇怪的问题。我有一个缩放(transform: scale())容器,里面有一个视频和其他元素。在某些缩放比例下,具有高z-index的绝对定位元素会消失并且不再出现。
如何解决这个问题? 请注意,我不能给视频元素一个负的z-index,并且我需要使用overflow: hidden;示例 我制作了一个示例,可以将最外层容器放大和缩小。在特定的缩放值下,类名为.on-top(文本为"I should always be on top.")的元素会消失。当再次缩小时,它突然出现。
链接到示例:https://jsfiddle.net/iafiawik/Lcox1ecc/

enter image description here

结论

  • 看起来元素的大小很重要。我把它做得越大,消失前的比例值就越大。
  • 我也测试过直接在元素上使用CSS设置transform: scale(1.4),行为是相同的。

如果我:

  • 用div替换视频标签
  • .on-top的兄弟姐妹(即.below)中删除position: absolute;
  • .content中删除overflow: hidden;
  • .on-top移动到文档流中的视频标签之后

(但当然,由于项目特定原因,这些解决方法都对我不起作用。我也不能给视频元素一个负的z-index,而且我需要使用overflow: hidden;。)

社区提出的解决方法(感谢!)

  • 给视频标签一个负的z-index(我无法这样做,因为有时我会在视频后面放置元素)
  • 删除overflow: hidden;(我不能删除overflow: hidden;

浏览器

我在Chrome(Mac)和Safari(Mac)中看到了这个问题。


更新1

看起来这个错误报告基本上涵盖了我的问题。然而,它没有提供解决方法。

更新2

我通过提供自己的解决方案回答了自己的问题。

更新3

有很多答案都修改了视频的z-index或者给.on-top元素添加了translateZ。演示表明这两种方法都可以解决这个问题。但是,由于我的HTML结构是从可视化HTML编辑器输出的(长话短说...),我不知道哪些元素会在那里,或者它们是否应该在视频的前面、后面或旁边。因此,我正在寻找一种不需要更改缩放元素内部各个元素的解决方案。


3
显然,“scale”在Safari和Chrome中会破坏z-index信息。请参考:https://dev59.com/Q5Tfa4cB1Zd3GeqPSI3x?utm_medium=organic&utm_source=google_rich_qa&utm_campaign=google_rich_qa有一些解决方法,但看起来您已经找到了它们! - José A. Zapata
1
谢谢,José!我已经尝试了一些解决方法,但它们并没有帮助我。我必须在某个时候使用 overflow: hidden;,而这似乎是我的问题所在(当与 position: relative;position: absolute; 结合使用时)。 - iafiawik
1
看起来这是一个 WebKit/Blink(Safari/Chrome/Opera)的问题!我在 Firefox、Edge 和 IE11 上没有遇到这个问题。 - Baksteen
@Baksteen 我也是。我只能在Chrome和Safari中重现它。 - iafiawik
9个回答

9

看起来是Chrome的一个bug。注意当你缩放图片时,元素检查器仍然告诉你#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>


谢谢指出这个问题 :) 我创建了一个更新的演示,包含新的见解:https://jsfiddle.net/iafiawik/bpos8nw3/我添加了一个外部元素,它不会被缩放,但会随着内部元素的缩放而更新宽度和高度值。我将尝试在实际项目中使用并查看其效果。 - iafiawik
2
@iafiawik 请查看修改后的答案。 - Salman A
感谢您详细的回答,非常感激。我想避免操作单个元素以防止它们消失。据我所知,使您的解决方案起作用的是将200px的border-left添加到.on-top中。如果您在视频上方有限定数量的元素,那么这可能是一个足够好的解决方案,但对于我来说并非如此。我的HTML是从可视化编辑器输出的,因此当要呈现元素时,我不知道会有哪些元素。 - iafiawik
你好,我已经在我的解决方案中使用了你的答案(已发布为答案)。感谢你的帮助 :) - iafiawik

9

这绝对是适用于那些具有少量/可控元素的选项。不幸的是,我的HTML是从视觉编辑器输出的,因此当文档呈现时,我不知道会有哪些元素。当然,我可以通过某些类将translateZ添加到所有元素,但我发现这比我的当前解决方法更费力。无论如何,谢谢 :)! - iafiawik
1
啊,好的,我没意识到那是你的用例。如果要进行全局修复,只需使用 * { -webkit-transform: translateZ(0); }。这将应用规则到所有内容上。它不应该对你的布局产生任何影响,除了纠正显示错误。 - jmcgriz
这是一个繁重的操作吗? - iafiawik
1
如果您知道该位置将包含哪种类型的元素,最好具体列出它们,例如 h1、h2、div { },但是在适当的情况下适度使用 * 也可以,并不会影响您的加载时间或其他任何事情。 - jmcgriz
谢谢,我会尝试一下 :) 我还更新了我的问题,关于我的HTML结构。 - iafiawik
显示剩余3条评论

4
经过大量研究和尝试多种方法后,我得出结论:没有解决方案可以修复我的问题。如果您能够控制消失元素的z-indexes,则有一些解决方案可以修复该问题,但由于HTML的结构未知(它是HTML编辑器的输出),因此我无法这样做。我正在寻找一种不需要更改缩放父级的各个子元素的解决方案,但到目前为止我还没有找到任何解决方案。

这份错误报告基本涵盖了我的问题,但没有提供解决方法。

我可以确认,这是因为该元素在缩放容器的原始宽度和高度之外:

元素在 scale(1.227) 可见(红色边框表示#scaled的原始大小):

enter image description here

...但不是在scale(1.228)的情况下:

enter image description here

因此,我的解决方案是在缩放元素外部添加另一个不进行缩放的包装元素,但根据其第一个子项的比例值更新其widthheight属性。该元素具有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>


请检查我的答案。这是我多年来在开发基于浏览器的游戏时遇到的一个奇怪的渲染故障,那一行代码应该可以解决它。很抱歉没有在你费力寻找解决方法之前就发现这个问题。 - jmcgriz

2
如果您可以稍微修改一下html,一种方法是将有问题的元素包装在与视频和容器大小相同且具有正确z-index的容器中。这样,您就会拥有相同大小和位置的清晰层,您可以将更复杂的元素定位到其中。例如:
   <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/


嗨,谢谢您的回答!这可能实际上是一种解决方法,适用于具有简单HTML结构的人。 :) 不幸的是,我的HTML是来自视觉编辑器的输出,因此当页面呈现时,我将不知道会有哪些元素。这也意味着我无法将每个单独的元素设置为“宽度:100%;高度:100%;”,因为这会搞乱指针事件和其他类似的事情。 - iafiawik

1

你好!感谢您的回复。我并不是要表现得不感激,但问题已经提到了去掉 overflow: hidden 就可以解决问题。所以很明显 overflow: hidden 是有其存在的理由的。关于其他建议,如果您能够提供一个工作示例,其中包含一个“节点之间”,它的功能与 overflow: hidden 相同,消除了错误行为但保持其他所有内容不变,那就太好了。 - jonahe
1
啊,对不起。我错过了这个。我匆忙进入了jsfiddle。 - Sandwell
@Sandwell 没问题,谢谢!你有使用额外节点的工作示例吗? - iafiawik
@iafiawik 我尝试了,但效果不太好。你能否考虑在视频标签上使用负 z-index,就像我更新的 jsfiddle 一样? - Sandwell
@Sandwell 我意识到这是少数看起来相当简单的解决方法之一,但是...我有其他元素有时会放在视频后面,给视频放一个负的z-index会带来一堆新问题。 - iafiawik
显示剩余2条评论

1
我非常喜欢Salman A的答案。 唯一想到的是,使用position: relative进行重写。 但我不知道这是否可行。

1
我同意,这是迄今为止最好的答案。不幸的是,在这个阶段重写为“position:relative;”不是一个选项 - 这将需要太多时间。 - iafiawik

0

1
是的,我知道(这个信息已经包含在我的问题中了)。我需要让它与 overflow: hidden; 一起正常工作。你有任何想法如何让它正常工作吗? - iafiawik

0

上周我遇到了和绝对定位元素和变换有关的类似问题...

不知道这个能否帮到你,但是这里有个链接。

CSS transform: translate移动position:fixed内部Div

最后我通过使用transform: translateX(none)而不是translateX(0)来修复了它。

确实非常奇怪的行为,但是链接中提供了一些更多的链接,以帮助更清楚地解决问题 - 就像它按照规范行为一样。


谢谢您的回复!您看过问题中链接的JSFiddle演示(最小化重现行为的示例)是否可以使用这个技巧来解决?我不确定如何将此方法应用于演示的具体情况。 - jonahe
1
你好!感谢您的输入。我不知道如何将其应用到我的示例(https://jsfiddle.net/iafiawik/Lcox1ecc/)。我的问题是z-index,而不是定位,但也许它们有某种关联?如果有,那么是什么关联呢? - iafiawik

0
可能有点晚了,但我还是想发一篇文章,以防有人会觉得有用。 在父容器元素下添加一个空的 div 元素,并使用变换动画即可避免元素消失。该动画本身并没有实质性的作用,但它可以强制浏览器使用硬件加速来渲染所有的元素。

<div class="emptydiv"></div>

    .emptydiv{
      transform:scale(1);
      animation:fix 3s infinite;
    }
    
    @keyframes fix{
      50%{
        transform:scale(1);
      }
    }

这是编辑过的原帖示例 https://jsfiddle.net/tfezqhp0/1 - Mr.Ri8x

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接