代码之家  ›  专栏  ›  技术社区  ›  Scott Marcus

在CSS动画期间防止背景图像调整大小,并消除闪烁

  •  8
  • Scott Marcus  · 技术社区  · 8 年前

    我有一个 <div> 我将其用作幻灯片图像容器。将在容器中显示的图像大小不同,并应用为背景图像,而不是 <img> 容器内的元素,以便:

    • 我可以根据需要通过媒体查询交换图像集
    • 背景可以设置动画以产生幻灯片效果

    我有 background-size:cover 和 background-clip:content-box 这样可以正确地将图像保持在容器的所需区域内,确保适当地剪掉多余的图像。

    第一个问题是,在CSS动画期间,当一个图像过渡到另一个图像时,新图像会在该关键帧的计时过程中调整大小。我想要的是,下一个图像只是转换到(在适当的大小已经),而没有看到它的大小。

    实际上,我甚至不知道为什么首先会出现交叉淡入淡出可视化,因为我没有这样做的说明(可能是内置的) -webkit- 动画?)。我本以为你会看到从一个图像到下一个图像的即时变化。实际上我喜欢交叉渐变,但不知道它是从哪里来的,这让我觉得它与尺寸调整问题有关。

    第二个问题是,在幻灯片放映的第一次迭代(仅)期间,图像会闪烁,并简要显示容器元素的白色背景色。因为这只发生在第一次迭代中,我认为这可能是图像初始下载时间的问题,这就是为什么它在后续迭代中消失的原因。然而,当我在幻灯片放映开始之前添加了一些JavaScript来预加载图像并发现相同的问题时,事实证明并非如此。此外,一旦你运行了该节目,你的缓存中就有了图像,当你刷新页面时(该页面只会从你的缓存中获取图像,是的,我正在通过从我自己的测试服务器获取文件来测试这一点,这样它们就会正确缓存),闪烁再次发生。

    我看过一些帖子,内容是关于使用各种CSS属性设置来消除闪烁 preserve3d 诸如此类,但这些都没有帮助。

    我在Windows 10上使用Chrome 62.0.3202.94桌面。

    #outerFrame {
      background-color: #22130c;
      box-shadow: inset 0 1px 1px 1px hsla(0,0%,100%,.2), 
                  inset 0 -2px 1px hsla(0,0%,0%,.4), 
                  0 5px 50px hsla(0,0%,0%,.25), 
                  0 20px 20px -15px hsla(0,0%,0%,.2), 
                  0 30px 20px -15px hsla(0,0%,0%,.15), 
                  0 40px 20px -15px hsla(0,0%,0%,.1);
      padding: 1.5em;
      overflow: auto;
      float: left;
      margin: 0 1em 1em 0;
    }
    
    #innerFrame {
      background-color: #fff5e5;
      box-shadow: 0 2px 1px hsla(0,0%,100%,.2), 
                  0 -1px 1px 1px hsla(0,0%,0%,.4), 
                  inset 0 2px 3px 1px hsla(0,0%,0%,.2), 
                  inset 0 4px 3px 1px hsla(0,0%,0%,.2), 
                  inset 0 6px 3px 1px hsla(0,0%,0%,.1);
      padding: 1.5em;
    }
    
    /* This is the relevant style: */
    #innermostFrame {
      padding: .75em;
      background-color: #fff;
      box-shadow: 0 -1px 0 2px hsla(0, 0%, 0%,.03);
      background-position: 50% 50%;
      background-repeat: no-repeat;
      background-size:cover;  
      background-clip: content-box;
      animation: cycle 8s ease-in-out infinite;
      
      width: 30vw;
      height:40vh;
      min-width: 200px;
      max-width: 900px;
    }
    
    @keyframes cycle {
      0%   { background-image: url("https://picsum.photos/200/300/?random");   }
      25%  { background-image: url("https://picsum.photos/640/480/?random");   }
      50%  { background-image: url("https://picsum.photos/1900/1200/?random"); }
      75%  { background-image: url("https://picsum.photos/480/200/?random");   }
      100% { background-image: url("https://picsum.photos/600/300/?random");   }
    }
    <div id="outerFrame">
      <div id="innerFrame">
        <div id="innermostFrame"></div>
      </div>
    </div>

    我意识到我可以使用其他各种技术(如JavaScript、堆栈)回避这些问题 <img> 要素和管理 opacity 等等),但我真的在努力理解是什么导致了这两个问题。

    1 回复  |  直到 5 年前
        1
  •  3
  •   J.T. Houtenbos    8 年前

    1、交叉淡入度:

    这是因为浏览器尝试在关键帧之间设置动画。如果我们看一下你的@关键帧周期,我们会看到你每25%制作一个关键帧。这意味着浏览器将从0%到25%设置动画。它将制作什么动画?改变的背景。它将如何为其设置动画?交叉衰减。此外,再次调整背景大小以覆盖div(使用的图片格式不同-如果使用相同的格式-这可能不会发生)。

    @keyframes cycle {
      0%   { background-image: url("https://picsum.photos/200/300/?random");   }
      25%  { background-image: url("https://picsum.photos/640/480/?random");   }
      50%  { background-image: url("https://picsum.photos/1900/1200/?random"); }
      75%  { background-image: url("https://picsum.photos/480/200/?random");   }
      100% { background-image: url("https://picsum.photos/600/300/?random");   }
    }
    

    解决方案A(无交叉褪色和白色背景)

    通过让动画几乎在瞬间发生,可以避免调整大小:

    0%   { background-image: url("https://picsum.photos/200/300/?random");   }
    24.99%   { background-image: url("https://picsum.photos/200/300/?random");   }
    25%  { background-image: url("https://picsum.photos/640/480/?random");   }
    

    关键帧0%中的背景与关键帧24.99%中的背景相同,因此不会设置动画。然后它在25%时发生变化(这看起来像是即时变化),请参阅下面的工作片段:

    #outerFrame {
      background-color: #22130c;
      box-shadow: inset 0 1px 1px 1px hsla(0,0%,100%,.2), 
                  inset 0 -2px 1px hsla(0,0%,0%,.4), 
                  0 5px 50px hsla(0,0%,0%,.25), 
                  0 20px 20px -15px hsla(0,0%,0%,.2), 
                  0 30px 20px -15px hsla(0,0%,0%,.15), 
                  0 40px 20px -15px hsla(0,0%,0%,.1);
      padding: 1.5em;
      overflow: auto;
      float: left;
      margin: 0 1em 1em 0;
    }
    
    
    #innerFrame {
      position: relative;
      background-color: #fff5e5;
      box-shadow: 0 2px 1px hsla(0,0%,100%,.2), 
                  0 -1px 1px 1px hsla(0,0%,0%,.4), 
                  inset 0 2px 3px 1px hsla(0,0%,0%,.2), 
                  inset 0 4px 3px 1px hsla(0,0%,0%,.2), 
                  inset 0 6px 3px 1px hsla(0,0%,0%,.1);
      padding: 1.5em;
    }
    
    /* This is the relevant style: */
    #innermostFrame{
      padding: .75em;
      background-color: #fff;
      box-shadow: 0 -1px 0 2px hsla(0, 0%, 0%,.03);
      background-position: 50% 50%;
      background-repeat: no-repeat;
      background-size:cover;  
      background-clip: content-box;
      animation: cycle 8s ease-in-out infinite;
      width: 30vw;
      height:40vh;
      min-width: 200px;
      max-width: 900px;
    }
    
    @keyframes cycle {
          0%   { background-image: url("https://picsum.photos/200/300/?random");   }
          24.99%   { background-image: url("https://picsum.photos/200/300/?random");   }
          
          25%  { background-image: url("https://picsum.photos/640/480/?random");   }
          49.99%  { background-image: url("https://picsum.photos/640/480/?random");   }
          
          50%  { background-image: url("https://picsum.photos/1900/1200/?random"); }
          74.99%  { background-image: url("https://picsum.photos/1900/1200/?random"); }
          
          75%  { background-image: url("https://picsum.photos/480/200/?random");   }
          99.99%  { background-image: url("https://picsum.photos/480/200/?random");   }
          
          100% { background-image: url("https://picsum.photos/200/300/?random");    }
    }
    <div id="outerFrame">
      <div id="innerFrame">
        <div id="innermostFrame"></div>
      </div>
    </div>

    注意! 最后一个关键帧(100%)应设置为与第一个关键帧(0%)相同的值,因为我们希望循环从停止的地方开始。实际上,这意味着在本例中,您只有4个不同的背景图像(如果您想有5个,请改为采取20%的步骤)。

    我们修复了调整背景大小的问题。作为一个副作用,我们还“修复”了交叉衰减。真正的缺点是我们在第一次播放动画时仍然有白色背景。

    2、白色背景

    我假设出现白色背景是因为需要先加载图像。它们仅在动画运行时加载。这就是为什么有白色背景的第一个运行槽。

    解决方案B(具有交叉淡入和无白色背景)

    我们可以使用二次div恢复交叉淡入淡出效果。但是我们必须在keframes中设置不透明度。我们可以在显示另一个div的同时在关键帧中设置背景图像,这样可以有更多的时间加载,并且不会出现白色背景。请参阅下面的代码段:

    #outerFrame {
      background-color: #22130c;
      box-shadow: inset 0 1px 1px 1px hsla(0,0%,100%,.2), 
                  inset 0 -2px 1px hsla(0,0%,0%,.4), 
                  0 5px 50px hsla(0,0%,0%,.25), 
                  0 20px 20px -15px hsla(0,0%,0%,.2), 
                  0 30px 20px -15px hsla(0,0%,0%,.15), 
                  0 40px 20px -15px hsla(0,0%,0%,.1);
      padding: 1.5em;
      overflow: auto;
      float: left;
      margin: 0 1em 1em 0;
    }
    
    
    #innerFrame {
      position: relative;
      background-color: #fff5e5;
      box-shadow: 0 2px 1px hsla(0,0%,100%,.2), 
                  0 -1px 1px 1px hsla(0,0%,0%,.4), 
                  inset 0 2px 3px 1px hsla(0,0%,0%,.2), 
                  inset 0 4px 3px 1px hsla(0,0%,0%,.2), 
                  inset 0 6px 3px 1px hsla(0,0%,0%,.1);
      padding: 1.5em;
    }
    
    /* This is the relevant style: */
    #innermostFrame,
    #innermostFrame2{
      padding: .75em;
      background-color: #fff;
      box-shadow: 0 -1px 0 2px hsla(0, 0%, 0%,.03);
      background-position: 50% 50%;
      background-repeat: no-repeat;
      background-size:cover;  
      background-clip: content-box;
      animation: cycle 8s ease-in-out infinite;
      width: 30vw;
      height:40vh;
      min-width: 200px;
      max-width: 900px;
    }
    #innermostFrame2{
      position: absolute;
      top: 1.5em; /* padding of innerFrame */
      animation: cycle2 8s infinite;
      background-image: url("https://picsum.photos/200/300/?random"); 
    }
    @keyframes cycle {
      0%   { background-image: url("https://picsum.photos/640/480/?random"); }
      50%  { background-image: url("https://picsum.photos/640/480/?random"); }
      51%  { background-image: url("https://picsum.photos/480/200/?random"); }
      100% { background-image: url("https://picsum.photos/480/200/?random"); }
    }
    @keyframes cycle2 {
      15%  { opacity: 1;}
      25%  { opacity: 0; background-image: url("https://picsum.photos/200/300/?random"); }
      
      26%  { background-image: url("https://picsum.photos/1900/1200/?random");}
      40%  { opacity: 0;}
      50%  { opacity: 1;}
      65%  { opacity: 1;}
      75%  { opacity: 0; background-image: url("https://picsum.photos/1900/1200/?random");}
      
      76%  { background-image: url("https://picsum.photos/200/300/?random");}
      90%  { opacity: 0;}
      100% { opacity: 1;}
    }
    <div id="outerFrame">
      <div id="innerFrame">
        <div id="innermostFrame"></div>
        <div id="innermostFrame2"></div>
      </div>
    </div>

    解决方案B说明:

    这个想法是让背景图像在显示之前加载。因此,有2个div和2个div @keyframe 产生平滑交替背景的动画。

    #innermostFrame2 获取与相同的宽度和高度 #innermostFrame 绝对定位(正上方)。 #最内层框架2 在动画持续时间的25%内显示和隐藏。以下内容可见:

             25%                25%               25%                25%
    [ #innermostFrame2 - #innermostFrame - #innermostFrame2 - #innermostFrame ]
    

    百分比:

    背景图像已设置为 #最内层框架2 自身-使其加载尽可能快。在 @keyframes cycle2

    #最内层框架 的背景现在可见(因为 #最内层框架2 是隐藏的)。我们希望在动画持续时间的25%中显示这一点。所以我们想要50% #最内层框架2 在新的背景下再次可见。因为我决定在动画持续时间的10%内进行交叉淡入淡出,我们从40%开始 opacity: 0; . 紧接着 #最内层框架2 完全显示(50%)我们改变了 #最内层框架 的背景,让它加载。这种情况发生在51% @keyframes cycle