【问题标题】:How to Animate a Skeleton Screen with a CSS Mask如何使用 CSS 蒙版为骨架屏幕设置动画
【发布时间】:2021-01-02 22:33:02
【问题描述】:

我正在尝试为看起来像this one 的骨架添加一个微妙的闪光动画。我目前有一个看起来像这样的屏幕(参见CodePen):

我正在尝试编写一个可以像这样接受 SVG 的骨架组件:

<div class="skeleton" aria-busy="true">
  <svg width="233" height="68" viewBox="0 0 233 68" xmlns="http://www.w3.org/2000/svg">
    <g opacity="0.8">
      <rect x="79" y="32" width="154" height="11" rx="2" fill="black" fill-opacity="0.07"/>
      <rect width="179" height="20" rx="2" fill="black" fill-opacity="0.07"/> 
      <rect x="79" y="52" width="84" height="11" rx="2" fill="black" fill-opacity="0.07"/>
      <rect y="26" width="67" height="42" rx="2" fill="black" fill-opacity="0.07"/>
    </g>
  </svg>
</div>

这是我用来为 SVG 上方的微光设置动画的 CSS:

.skeleton {
  overflow: hidden;
  position: relative;
}

.skeleton::before {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  bottom: 0;
  right: 0;
  background: linear-gradient(to right, rgb(243, 242, 241) 0%, rgb(237, 235, 233) 50%, rgb(243, 242, 241) 100%) 0px 0px / 90% 100% no-repeat rgb(243, 242, 241);
  transform: translateX(-100%);
  
  animation-name: skeleton-animation;
  animation-duration: 2s;
  animation-timing-function: ease-in-out;
  animation-direction: normal;
  animation-iteration-count: infinite;
}

@keyframes skeleton-animation {
  0% {
    transform: translateX(-100%);
  }
  100% {
    transform: translateX(100%);
  }
}

我正在尝试弄清楚如何使用here 所述的某种遮罩,以便动画微光只发生在 SVG 上。

【问题讨论】:

标签: html css svg css-animations


【解决方案1】:

一种方法是使用SVG 作为mask-image 并更新线性渐变background-position

示例(使用您自己的渐变颜色进行更新)。:

.skeleton {
  overflow: hidden;
  position: relative;
  width: 233px;
  height: 68px;
  background: linear-gradient(to right, rgb(143, 142, 141) 0%, rgb(237, 235, 233) 50%, rgb(143, 142, 141) 100%) 0px 0px / 100% 100% rgb(243, 242, 241);
  -webkit-mask-image: url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjMzIiBoZWlnaHQ9IjY4IiB2aWV3Qm94PSIwIDAgMjMzIDY4IiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPg0KICAgIDxnIG9wYWNpdHk9IjAuOCI+DQogICAgICA8cmVjdCB4PSI3OSIgeT0iMzIiIHdpZHRoPSIxNTQiIGhlaWdodD0iMTEiIHJ4PSIyIiBmaWxsPSJibGFjayIgZmlsbC1vcGFjaXR5PSIxIi8+DQogICAgICA8cmVjdCB3aWR0aD0iMTc5IiBoZWlnaHQ9IjIwIiByeD0iMiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMSIvPiANCiAgICAgIDxyZWN0IHg9Ijc5IiB5PSI1MiIgd2lkdGg9Ijg0IiBoZWlnaHQ9IjExIiByeD0iMiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMSIvPg0KICAgICAgPHJlY3QgeT0iMjYiIHdpZHRoPSI2NyIgaGVpZ2h0PSI0MiIgcng9IjIiIGZpbGw9ImJsYWNrIiBmaWxsLW9wYWNpdHk9IjEiLz4NCiAgICA8L2c+DQogIDwvc3ZnPg==");
  margin: 1em;
  animation: linearAnim 2s infinite linear
}

@keyframes linearAnim {
  0% {
    background-position: 0px 0px;
  }
  100% {
    background-position: 230px 0px;
  }
}

/* demo purpose only */
.skeleton + .skeleton {

  background: linear-gradient(to right, rgba(143, 142, 141,0.75) 0%, rgba(237, 235, 233, 0.75) 50%, rgba(143, 142, 141, 0.75) 100%) 0px 0px / 100% 100% rgba(243, 242, 241, 0.5);
  }
  .skeleton.blue {
  background-color:blue;
  }
  .skeleton.red {
  background-color:red;
  }
  .skeleton.yellow {
  background-color:yellow;
  }
  .skeleton.green {
  background-color:green;
  }
  .flex {display:flex;align-items:center;justify-content:center;box-sizing:border-box;padding-top:0.6em;font-size:10px;color:#fff8;}
body {background:#bee; display:grid;grid-template-columns:repeat(auto-fill, 300px);}
<div class="skeleton" aria-busy="true"></div>
<div class="skeleton blue" aria-busy="true"></div>
<div class="skeleton red" aria-busy="true"></div>
<div class="skeleton yellow" aria-busy="true"></div>
<div class="skeleton green flex" aria-busy="true">On its way ...</div>

这里有一个类似的问题,方法不同CSS animation, only render overlay on specific elements


编辑

如果要求将您的 SVG 放在 HTMl 中,您可以给它一个 ID 并即时执行编码部分

// https://www.w3docs.com/snippets/javascript/how-to-encode-and-decode-strings-with-base64-in-javascript.html

let svg = document.getElementById("mask").outerHTML
let sk =document.querySelector(".skeleton");
let encodedString = btoa(svg);
sk.setAttribute("style", " -webkit-mask-image: url('data:image/svg+xml;base64," + encodedString + "')");
.skeleton {
  overflow: hidden;
  position: relative;
  width: 233px;
  height: 68px;
  background: linear-gradient( to right, rgb(143, 142, 141) 0%, rgb(237, 235, 233) 50%, rgb(143, 142, 141) 100%) 0 0 /  200% 100%   rgb(243, 242, 241);
  margin: 1em;
  animation: linearAnim 1.25s infinite linear;
}

@keyframes linearAnim {
 
  100% {
    background-position: -200% 0;
  }
}

#mask {
  display: none;
}
<div class="skeleton" aria-busy="true">
<svg id="mask" width="233" height="68" viewBox="0 0 233 68" xmlns="http://www.w3.org/2000/svg">
    <g opacity="0.5">
      <rect x="79" y="32" width="154" height="11" rx="2" fill="black" fill-opacity="0.5"/>
      <rect width="179" height="20" rx="2" fill="black" fill-opacity="0.5"/> 
      <rect x="79" y="52" width="84" height="11" rx="2" fill="black" fill-opacity="0.5"/>
      <rect y="26" width="67" height="42" rx="2" fill="black" fill-opacity="0.5"/>
    </g>
  </svg>
</div>

【讨论】:

  • 由于我正在尝试构建一个组件,我希望我的组件的用户通过插槽传递任意 SVG 文件,例如&lt;skeleton&gt;&lt;svg&gt;...&lt;/svg&gt;&lt;/skeleton&gt; 而不是在 CSS 中硬编码。
  • @MuhammadRehanSaeed 你可以试试-webkit-mask-image: url("#SVG_ID");},但你会发现最好使用来自clipPath SVG developer.mozilla.org/fr/docs/Web/CSS/clip-path 的clip-path。从那里,bg 位置动画的工作原理相同。您将不得不重新考虑 SVG 构建其形状的方式
  • 您的编辑与我正在寻找的非常接近。唯一的问题是动画background-position 很慢,而且您还必须在动画中设置特定的宽度。有没有办法解决这些问题?
  • @MuhammadRehanSaeed 动画时间可以根据您的需要重新设置,背景大小和背景位置也可以用来调整它。我修改了第二个sn-p。对于尺寸,您的 svg 为 width="233" height="68",即面具的尺寸 :)
【解决方案2】:

如果您不需要 使用 SVG,那么这在 H​​TML 中会更容易一些。但这里是使用 SVG 的方法。

.shimmer-rect {
  animation-name: skeleton-animation;
  animation-duration: 2s;
  animation-timing-function: linear;
  animation-direction: normal;
  animation-iteration-count: infinite;
}

@keyframes skeleton-animation {
  0% {
    transform: translateX(0px);
  }
  100% {
    transform: translateX(2000px);
  }
}

.skeleton {
  margin-bottom: 2em;
}
<!-- Include this once somewhere in your HTML -->
<svg width="0" height="0" style="position: absolute">
  <defs>
    <linearGradient id="shimmer" gradientUnits="userSpaceOnUse" x2="2000" spreadMethod="repeat">
      <stop offset="15%" stop-color="rgb(237,235,233)"/>
      <stop offset="25%" stop-color="rgb(243,242,241)"/>
      <stop offset="35%" stop-color="rgb(237,235,233)"/>
    </linearGradient>
    <mask id="shimmer-mask">
      <rect x="79" y="32" width="154" height="11" rx="2" fill="white"/>
      <rect width="179" height="20" rx="2" fill="white"/> 
      <rect x="79" y="52" width="84" height="11" rx="2" fill="white"/>
      <rect y="26" width="67" height="42" rx="2" fill="white"/>
    </mask>
    <g id="skel" mask="url(#shimmer-mask)">
      <rect class="shimmer-rect" x="-2000" width="3000" height="100%" fill="url(#shimmer)"/>
    </g>
  </defs>
</svg>


<div class="skeleton" aria-busy="true">
  <svg width="233" height="68" viewBox="0 0 233 68"> <use xlink:href="#skel"/> </svg>
</div>

<div class="skeleton" aria-busy="true">
  <svg width="233" height="68" viewBox="0 0 233 68"> <use xlink:href="#skel"/> </svg>
</div>

<div class="skeleton" aria-busy="true">
  <svg width="233" height="68" viewBox="0 0 233 68"> <use xlink:href="#skel"/> </svg>
</div>

【讨论】:

  • 我正在尝试构建一个可重用的 Vue/React 组件,例如&lt;skeleton&gt;&lt;svg&gt;...&lt;/svg&gt;&lt;/skeleton&gt;。是否可以将一些微光逻辑上移到 CSS 中并单独保留 SVG?
  • 如果你的意思是&lt;linearGradient&gt;,那么不,不能移动到CSS。而且我认为没有任何浏览器支持 SVG 元素上的 CSS 渐变。但是&lt;linearGradient&gt; 只需要在页面上出现一次。只需将其放在页面中的某个位置 &lt;svg width="0" height="0"&gt;&lt;/svg&gt; 即可。它会稍微减少 HTML 的数量。
  • 其实大部分SVG只需要在页面上出现一次。您可以为骨架的每个实例使用一个最小的小 SVG。请参阅我的更新答案。
【解决方案3】:

创建 CSS 骨架微光的简单方法:

创建一个简单的类,然后将其应用于 HTML 表格,然后您可以使用 HTML 来塑造/调整骨架,如下所示:

@keyframes preloadAnimation {
    from {background-position: 100% 0}
    to {background-position: -80% 0}
}

.shimmer{background: linear-gradient(to right, #dedede 8%, #f3f3f3 18%, #dedede 33%);
     background-size: 200% 50px;
     animation: preloadAnimation 2s infinite;
     border-collapse:collapse;
     width:100%
   }

.shimmer td{background:transparent;border:2px solid white;height:18px}
.shimmer td.white{background-color:white}
<table class=shimmer>
          <tr><td style='height:30px'></td><td colspan=2 class=white></td></tr>
          <tr><td colspan=3 style='height:12px' class=white></td></tr>
          <tr><td colspan=2></td><td class=white></td></tr>
          <tr><td colspan=3 style='height:12px' class=white></td></tr>
          <tr><td></td><td></td><td></td></tr>
          <tr><td></td><td></td><td></td></tr>
          <tr><td colspan=3 style='height:12px' class=white></td></tr>
          <tr><td style='height:30px'></td><td colspan=2 class=white></td></tr>
        </table>

【讨论】:

    猜你喜欢
    • 2016-01-16
    • 1970-01-01
    • 2018-01-03
    • 2012-12-06
    • 2014-12-13
    • 2014-04-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多