CSS:在一个元素上缩放和旋转多个 SVG 背景图像 - 没有 JS 可能吗?

Posted

技术标签:

【中文标题】CSS:在一个元素上缩放和旋转多个 SVG 背景图像 - 没有 JS 可能吗?【英文标题】:CSS : Scale and rotate in place multiple SVG backgrounds image on one element - possible without JS? 【发布时间】:2022-01-13 22:26:38 【问题描述】:

目标是这个cool sparkle effect by Josh W Comeau:

他通过使用 javascript 为每个 sparkle 插入一个元素来实现这一点。我想在只能使用 CSS 的网络应用中实现这种效果。

我的想法是在 :after 和 :before 伪元素上使用多个背景图像,将星星定位在文本的前后,并为每个背景图像设置动画(缩放 + 旋转)。

我设法通过计算正确的背景位置和背景大小来围绕它们的视觉中心缩放背景图像,但我不知道如何让它们也原地旋转。

只有css可以吗?

这是我目前所拥有的(为了简单起见,我使用正方形):

    * 
        box-sizing: margin-box;
    
    
     :root 
        /* Use d='M.5 0A.5.5 90 001 .5.5.5 90 00.5 1 .5.5 90 000 .5.5.5 90 00.5 0' for a star shape */
        /* bg images */
       --square: url("data:image/svg+xml,<svg viewBox='0 0 1 1' xmlns='http://www.w3.org/2000/svg'><path style='vector-effect:non-scaling-stroke' stroke='black' fill='none' stroke-width='1px' opacity='.1' d='M0 0 1 0 1 1 0 1 0 0'/></svg>");
       --red: url("data:image/svg+xml,<svg viewBox='0 0 1 1' xmlns='http://www.w3.org/2000/svg'><path fill='red' d='m0 0 1 0 0 1L0 1'/></svg>");
       --green: url("data:image/svg+xml,<svg viewBox='0 0 1 1' xmlns='http://www.w3.org/2000/svg'><path fill='green' d='m0 0 1 0 0 1L0 1'/></svg>");
       --blue: url("data:image/svg+xml,<svg viewBox='0 0 1 1' xmlns='http://www.w3.org/2000/svg'><path fill='blue' d='m0 0 1 0 0 1L0 1'/></svg>");
       
        /* grid unit */
        --unit: calc(100vw/19);
       
        /* set images */
        --background-image: var(--square), var(--red), var(--green), var(--blue);
        --background-repeat: space, no-repeat, no-repeat, no-repeat;
       
        /* sizes */
        --offset-X: 1;
        --offset-Y: 1;
        --red-scale:3;
        --red-size-from:var(--unit);
        --red-size-to: calc(var(--unit) * var(--red-scale));
        --red-offset:calc((var(--red-scale) - 1)/2);
    
        --offset-X1: 5;
        --offset-Y1: 3;
        --green-scale:5;
        --green-size-from: var(--unit);
        --green-size-to: calc(var(--unit) * var(--green-scale));
        --green-offset:calc((var(--green-scale) - 1)/2);
    
        --offset-X2: 0;
        --offset-Y2: 0;
        --blue-scale:19;
        --blue-size-from: var(--unit);
        --blue-size-to: calc(var(--unit) * var(--blue-scale));
        --blue-offset:calc((var(--blue-scale) - 1)/2);
    
        --red-position-from: calc(var(--unit) * calc(var(--offset-X) + var(--red-offset))) calc(var(--unit) * (var(--offset-Y) + var(--red-offset)));
        --red-position-to: calc(var(--unit) * var(--offset-X)) calc(var(--unit) * var(--offset-Y));
        --green-position-from: calc(var(--unit) * calc(var(--offset-X1) + var(--green-offset))) calc(var(--unit) * (var(--offset-Y1) + var(--green-offset)));
        --green-position-to: calc(var(--unit) * var(--offset-X1)) calc(var(--unit) * var(--offset-Y1));
        --blue-position-from: calc(var(--unit) * calc(var(--offset-X2) + var(--blue-offset))) calc(var(--unit) * (var(--offset-Y2) + var(--blue-offset)));
        --blue-position-to: calc(var(--unit) * var(--offset-X2)) calc(var(--unit) * var(--offset-Y2));
        /* result */
        --background-size-from: var(--unit), var(--red-size-from), var(--green-size-from), var(--blue-size-from);
        --background-size-to: var(--unit), calc(var(--red-size-to)), calc(var(--green-size-to)), calc(var(--blue-size-to));
        --background-position-from: 0 0, var(--red-position-from), var(--green-position-from), var(--blue-position-from);
        --background-position-to: 0 0, var(--red-position-to), var(--green-position-to), var(--blue-position-to);
    
    
    body 
        margin: 0;
        aspect-ratio: 1;
        background-image: var(--background-image);
        background-repeat: var(--background-repeat);
        overflow: hidden;
        animation: scaling 3s ease infinite alternate;
        animation-delay: 0,1s,2s,3s;
    
    
    @keyframes scaling 
        from 
            background-size: var(--background-size-from);
            background-position: var(--background-position-from);
        
        to 
            background-size: var(--background-size-to);
            background-position: var(--background-position-to);
        
    

https://codepen.io/DesignThinkerer/pen/NWaNXOO


【问题讨论】:

【参考方案1】:

在这里,我创建了两个 &lt;symbol&gt; 元素,每个元素代表一颗星。所有动画都是在 SVG 动画中完成的。这两个符号在时序上略有不同。 &lt;use&gt; 元素指的是这两个符号,我为可见性属性设置动画以使星星“随机”出现/消失。可见性的时间跟随符号的时间。

在示例中,我嵌入了 SVG,并制作了同一 SVG 的数据 URL 版本,并将其用作 &lt;h1&gt; 的背景。

我认为这是一个相当简单的解决方案,但也许您可以使用 CSS 动画做类似的事情。

body 
  background: black;
  color: white;


h1 
  font-family: sans-serif;
  display: inline-block;
  padding: .2em;
  background-size: contain;
  background-repeat: no-repeat;
  background-image: url('data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgNTAgMTAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CiAgPHN5bWJvbCBpZD0ic3RhcjEiIHZpZXdCb3g9IjAgMCA0IDQiIHdpZHRoPSI1IiBoZWlnaHQ9IjUiPgogICAgPGc+CiAgICAgIDxwYXRoIHRyYW5zZm9ybS1vcmlnaW49IjEuNSAxLjUiIGZpbGw9Im9yYW5nZSIKICAgICAgICBkPSJNIDAgMCBDIDEgMSAxIDIgMCAzIEMgMSAyIDIgMiAzIDMgQyAyIDIgMiAxIDMgMCBDIDIgMSAxIDEgMCAwIj4KICAgICAgICA8YW5pbWF0ZVRyYW5zZm9ybSBhdHRyaWJ1dGVOYW1lPSJ0cmFuc2Zvcm0iCiAgICAgICAgICBhdHRyaWJ1dGVUeXBlPSJYTUwiIHR5cGU9InNjYWxlIgogICAgICAgICAgdmFsdWVzPSIwIDA7IDEgMTsgMCAwIgogICAgICAgICAga2V5VGltZXM9IjAgOyAuNCA7IDEiCiAgICAgICAgICBkdXI9IjNzIiByZXBlYXRDb3VudD0iaW5kZWZpbml0ZSIvPgogICAgICA8L3BhdGg+CiAgICAgIDxhbmltYXRlVHJhbnNmb3JtIGF0dHJpYnV0ZU5hbWU9InRyYW5zZm9ybSIKICAgICAgICAgIGF0dHJpYnV0ZVR5cGU9IlhNTCIgdHlwZT0icm90YXRlIgogICAgICAgICAgdmFsdWVzPSIwIDEuNSAxLjU7IDkwIDEuNSAxLjUiCiAgICAgICAgICBrZXlUaW1lcz0iMCA7IDEiCiAgICAgICAgICBkdXI9IjFzIiByZXBlYXRDb3VudD0iaW5kZWZpbml0ZSIvPgogICAgPC9nPgogIDwvc3ltYm9sPgogIAogIDxzeW1ib2wgaWQ9InN0YXIyIiB2aWV3Qm94PSIwIDAgNCA0IiB3aWR0aD0iNSIgaGVpZ2h0PSI1Ij4KICAgIDxnPgogICAgICA8cGF0aCB0cmFuc2Zvcm09InNjYWxlKDAgMCkiIHRyYW5zZm9ybS1vcmlnaW49IjEuNSAxLjUiIGZpbGw9Im9yYW5nZSIKICAgICAgICBkPSJNIDAgMCBDIDEgMSAxIDIgMCAzIEMgMSAyIDIgMiAzIDMgQyAyIDIgMiAxIDMgMCBDIDIgMSAxIDEgMCAwIj4KICAgICAgICA8YW5pbWF0ZVRyYW5zZm9ybSBhdHRyaWJ1dGVOYW1lPSJ0cmFuc2Zvcm0iCiAgICAgICAgICBhdHRyaWJ1dGVUeXBlPSJYTUwiIHR5cGU9InNjYWxlIgogICAgICAgICAgdmFsdWVzPSIwIDA7IDEgMTsgMCAwIgogICAgICAgICAga2V5VGltZXM9IjAgOyAuNCA7IDEiCiAgICAgICAgICBkdXI9IjNzIiBiZWdpbj0iMS41cyIgcmVwZWF0Q291bnQ9ImluZGVmaW5pdGUiLz4KICAgICAgPC9wYXRoPgogICAgICA8YW5pbWF0ZVRyYW5zZm9ybSBhdHRyaWJ1dGVOYW1lPSJ0cmFuc2Zvcm0iCiAgICAgICAgICBhdHRyaWJ1dGVUeXBlPSJYTUwiIHR5cGU9InJvdGF0ZSIKICAgICAgICAgIHZhbHVlcz0iMCAxLjUgMS41OyA5MCAxLjUgMS41IgogICAgICAgICAga2V5VGltZXM9IjAgOyAxIgogICAgICAgICAgZHVyPSIxcyIgYmVnaW49IjEuNXMiIHJlcGVhdENvdW50PSJpbmRlZmluaXRlIi8+CiAgICA8L2c+CiAgPC9zeW1ib2w+CgogIDx1c2UgaHJlZj0iI3N0YXIxIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgyIDEpIj4KICAgIDxhbmltYXRlIGF0dHJpYnV0ZU5hbWU9InZpc2liaWxpdHkiIHZhbHVlcz0idmlzaWJsZTtoaWRkZW47aGlkZGVuO3Zpc2libGU7aGlkZGVuO2hpZGRlbiIgIGtleVRpbWVzPSIwOy4yOy40Oy42Oy44OzEiIGR1cj0iMTVzIiByZXBlYXRDb3VudD0iaW5kZWZpbml0ZSIgLz4KICA8L3VzZT4KICAKICA8dXNlIGhyZWY9IiNzdGFyMSIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoOCAyKSI+CiAgICA8YW5pbWF0ZSBhdHRyaWJ1dGVOYW1lPSJ2aXNpYmlsaXR5IiB2YWx1ZXM9ImhpZGRlbjt2aXNpYmxlO2hpZGRlbjt2aXNpYmxlO2hpZGRlbjtoaWRkZW4iICBrZXlUaW1lcz0iMDsuMjsuNDsuNjsuODsxIiBkdXI9IjE1cyIgcmVwZWF0Q291bnQ9ImluZGVmaW5pdGUiIC8+CiAgPC91c2U+CiAgCiAgPHVzZSBocmVmPSIjc3RhcjEiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDI2IDIpIj4KICAgIDxhbmltYXRlIGF0dHJpYnV0ZU5hbWU9InZpc2liaWxpdHkiIHZhbHVlcz0iaGlkZGVuO2hpZGRlbjtoaWRkZW47dmlzaWJsZTt2aXNpYmxlO2hpZGRlbiIgIGtleVRpbWVzPSIwOy4yOy40Oy42Oy44OzEiIGR1cj0iMTVzIiByZXBlYXRDb3VudD0iaW5kZWZpbml0ZSIgLz4KICA8L3VzZT4KICAKICA8dXNlIGhyZWY9IiNzdGFyMiIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTAgNikiPgogICAgPGFuaW1hdGUgYXR0cmlidXRlTmFtZT0idmlzaWJpbGl0eSIgdmFsdWVzPSJ2aXNpYmxlO2hpZGRlbjtoaWRkZW47dmlzaWJsZTtoaWRkZW47aGlkZGVuIiAga2V5VGltZXM9IjA7LjI7LjQ7LjY7Ljg7MSIgZHVyPSIxNXMiIGJlZ2luPSIxLjVzIiByZXBlYXRDb3VudD0iaW5kZWZpbml0ZSIgLz4KICA8L3VzZT4KICAKICA8dXNlIGhyZWY9IiNzdGFyMiIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMjAgNSkiPgogICAgPGFuaW1hdGUgYXR0cmlidXRlTmFtZT0idmlzaWJpbGl0eSIgdmFsdWVzPSJ2aXNpYmxlO2hpZGRlbjt2aXNpYmxlO2hpZGRlbjt2aXNpYmxlO2hpZGRlbiIgIGtleVRpbWVzPSIwOy4yOy40Oy42Oy44OzEiIGR1cj0iMTVzIiBiZWdpbj0iMS41cyIgcmVwZWF0Q291bnQ9ImluZGVmaW5pdGUiIC8+CiAgPC91c2U+CiAgCiAgPHVzZSBocmVmPSIjc3RhcjIiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDM0IDYpIj4KICAgIDxhbmltYXRlIGF0dHJpYnV0ZU5hbWU9InZpc2liaWxpdHkiIHZhbHVlcz0iaGlkZGVuO3Zpc2libGU7dmlzaWJsZTtoaWRkZW47dmlzaWJsZTtoaWRkZW4iICBrZXlUaW1lcz0iMDsuMjsuNDsuNjsuODsxIiBkdXI9IjE1cyIgYmVnaW49IjEuNXMiIHJlcGVhdENvdW50PSJpbmRlZmluaXRlIiAvPgogIDwvdXNlPgo8L3N2Zz4=')


svg 
  border: solid thin gray;
<h1>Sparkly text.</h1>

<svg viewBox="0 0 50 10" xmlns="http://www.w3.org/2000/svg">
  <symbol id="star1" viewBox="0 0 4 4"  >
    <g>
      <path transform-origin="1.5 1.5" fill="orange"
        d="M 0 0 C 1 1 1 2 0 3 C 1 2 2 2 3 3 C 2 2 2 1 3 0 C 2 1 1 1 0 0">
        <animateTransform attributeName="transform"
          attributeType="XML" type="scale"
          values="0 0; 1 1; 0 0"
          keyTimes="0 ; .4 ; 1"
          dur="3s" repeatCount="indefinite"/>
      </path>
      <animateTransform attributeName="transform"
          attributeType="XML" type="rotate"
          values="0 1.5 1.5; 90 1.5 1.5"
          keyTimes="0 ; 1"
          dur="1s" repeatCount="indefinite"/>
    </g>
  </symbol>
  
  <symbol id="star2" viewBox="0 0 4 4"  >
    <g>
      <path transform="scale(0 0)" transform-origin="1.5 1.5" fill="orange"
        d="M 0 0 C 1 1 1 2 0 3 C 1 2 2 2 3 3 C 2 2 2 1 3 0 C 2 1 1 1 0 0">
        <animateTransform attributeName="transform"
          attributeType="XML" type="scale"
          values="0 0; 1 1; 0 0"
          keyTimes="0 ; .4 ; 1"
          dur="3s" begin="1.5s" repeatCount="indefinite"/>
      </path>
      <animateTransform attributeName="transform"
          attributeType="XML" type="rotate"
          values="0 1.5 1.5; 90 1.5 1.5"
          keyTimes="0 ; 1"
          dur="1s" begin="1.5s" repeatCount="indefinite"/>
    </g>
  </symbol>

  <use href="#star1" transform="translate(2 1)">
    <animate attributeName="visibility" values="visible;hidden;hidden;visible;hidden;hidden"  keyTimes="0;.2;.4;.6;.8;1" dur="15s" repeatCount="indefinite" />
  </use>
  
  <use href="#star1" transform="translate(8 2)">
    <animate attributeName="visibility" values="hidden;visible;hidden;visible;hidden;hidden"  keyTimes="0;.2;.4;.6;.8;1" dur="15s" repeatCount="indefinite" />
  </use>
  
  <use href="#star1" transform="translate(26 2)">
    <animate attributeName="visibility" values="hidden;hidden;hidden;visible;visible;hidden"  keyTimes="0;.2;.4;.6;.8;1" dur="15s" repeatCount="indefinite" />
  </use>
  
  <use href="#star2" transform="translate(10 6)">
    <animate attributeName="visibility" values="visible;hidden;hidden;visible;hidden;hidden"  keyTimes="0;.2;.4;.6;.8;1" dur="15s" begin="1.5s" repeatCount="indefinite" />
  </use>
  
  <use href="#star2" transform="translate(20 5)">
    <animate attributeName="visibility" values="visible;hidden;visible;hidden;visible;hidden"  keyTimes="0;.2;.4;.6;.8;1" dur="15s" begin="1.5s" repeatCount="indefinite" />
  </use>
  
  <use href="#star2" transform="translate(34 6)">
    <animate attributeName="visibility" values="hidden;visible;visible;hidden;visible;hidden"  keyTimes="0;.2;.4;.6;.8;1" dur="15s" begin="1.5s" repeatCount="indefinite" />
  </use>
</svg>

【讨论】:

你是对的,旋转可能应该在 svg 本身中完成。我会尝试用 css 应用你的解决方案,非常感谢你的帮助! @ThéophileDesmedt 您可以抓住其中一个符号并将其用作所有伪元素的背景 :-)

以上是关于CSS:在一个元素上缩放和旋转多个 SVG 背景图像 - 没有 JS 可能吗?的主要内容,如果未能解决你的问题,请参考以下文章

svg子元素上的CSS变换原点问题

如何在 Firefox 中不尊重纵横比的情况下缩放 SVG 背景图像?

CSS 样式转换与 svg 转换

如何修复 FabricJS 中 Inkscape SVG 的缩放和旋转?

前端如何实现div在固定角度缩放

SVG+CSS3仿作2018草莓音乐节的宣传动画