为什么SVG在Firefox中为大型ViewBox尺寸生成模糊图像

时间:2019-06-16 18:29:07

标签: html firefox svg

我正在尝试使用一张重复的图像创建无限缩放效果。为了保持不同尺寸比例的图像质量,我选择了 使用SVG。

问题是在Firefox中静止时图像清晰,但是当缩放间隔(ms)设置为100ms或更小时,图像变得非常模糊。尤其是在缩放的初始阶段。

我已经尝试过为“ shape-rendering”样式属性设置不同的值,以及在图形上设置preserveAspectRatio = false。 在Chrome中,图像保持清晰。我不知道该如何调试。

我非常感谢您可以提供的任何帮助或想法!

示例:https://codepen.io/mannadu/pen/MMybwL

过滤器

 <?xml version="1.0"?>
                <svg xmlns="http://www.w3.org/2000/svg" id="yinyang" width="466" height="466" viewBox="-40 -40 80 80" preserveAspectRatio="false">
                        <g id="yyyy" transform="scale(20) translate(0 21.8)">
                        <g id="yyy">
                        <g id="yy">
                        <g id="y">
                        <circle id="yinner" r="39"/>
                        <path d="M0,38a38,38 0 0 1 0,-76a19,19 0 0 1 0,38a19,19 0 0 0 0,38" fill="#fff"/>
                        <circle cy="-19" r="5"/>
                        <circle cy="19" r="5" fill="#fff"/>
                        </g>
                        <use href="#y" transform="translate(0 , -19) scale(.15)"/>
                        <use href="#y" class="gany" transform="translate(0 , 19) scale(.15)"/>
                        </g>
                        <use href="#yy" transform="translate(0 , -19) scale(.15)" />
                        <use href="#yy" class="gany" transform="translate(0 , 19) scale(.15)" />
                        </g>
                        <use href="#yyy" transform="translate(0 , -19) scale(.15)" />
                        <use href="#yyy" class="gany" transform="translate(0 , 19) scale(.15)" />
                        </g>
                </svg>

1 个答案:

答案 0 :(得分:2)

要进一步说明我的观点:您应该对viewBox属性进行突变/更新,因为这样浏览器必须不断重新计算SVG元素的内部比例。这可能会导致性能问题或抗锯齿问题,例如您在Firefox中看到的内容。

您想要做的就是简单地将另一个转换应用于SVG元素。最快的创可贴修复方法是将SVG元素的内部HTML封装在<g>元素中:

<svg xmlns="http://www.w3.org/2000/svg" id="yinyang" width="466" height="466" viewBox="-40 -40 80 80">
  <g id="outer" transform="scale(1)">
    <!-- Original SVG content -->
  </g>
</svg>

然后,使用JS,您可以在间隔回调中更改其transform属性:

const yinyang = document.getElementById("yinyang");
const outer = document.getElementById('outer');
const scalefactor = 1.01;

let scale = 1;
const zoomyInterval = setInterval(() => {
    scale /= scalefactor;
    outer.setAttribute('transform', `scale(${scale})`);
  },
  100
);

请参见此处的概念验证(另请参见forked your CodePen):

const yinyang = document.getElementById("yinyang");
const outer = document.getElementById('outer');
const scalefactor = 1.01;

let scale = 1;
const zoomyInterval = setInterval(() => {
    scale /= scalefactor;
    outer.setAttribute('transform', `scale(${scale})`);
  },
  100
);
.gany {
  filter: invert(1);
  -webkit-filter: invert(1);
  /* Not working for svg <use> elements in Chrome */
}

svg {
  shape-rendering: geometricPresicion;
  /* 
  Attempt to address blurry rasterized image in Firfox */
}

circle {
  position: relative;
}
<svg xmlns="http://www.w3.org/2000/svg" id="yinyang" width="466" height="466" viewBox="-40 -40 80 80">
  <g id="outer" transform="scale(1)">
    <g id="yyyy" transform="scale(20) translate(0 21.8)">
      <g id="yyy">
        <g id="yy">
          <g id="y">
            <circle id="yinner" r="39" />
            <path d="M0,38a38,38 0 0 1 0,-76a19,19 0 0 1 0,38a19,19 0 0 0 0,38" fill="#fff" />
            <circle cy="-19" r="5" />
            <circle cy="19" r="5" fill="#fff" />
          </g>
          <use href="#y" transform="translate(0 , -19) scale(.15)" />
          <use href="#y" class="gany" transform="translate(0 , 19) scale(.15)" />
        </g>
        <use href="#yy" transform="translate(0 , -19) scale(.15)" />
        <use href="#yy" class="gany" transform="translate(0 , 19) scale(.15)" />
      </g>
      <use href="#yyy" transform="translate(0 , -19) scale(.15)" />
      <use href="#yyy" class="gany" transform="translate(0 , 19) scale(.15)" />
    </g>
  </g>
</svg>

更好:使用window.requstAnimationFrame()

您会注意到,即使上面的示例可行,您的动画仍会断断续续:这是因为您仅以100ms的间隔更新<g>元素的比例,即转换为10fps的帧速率。这种运动看起来不会流畅。您想要的是每当浏览器重新粉刷时就平滑计算下一个scale

如果我们轻轻地重构您的调整大小逻辑,您将具有以下内容:

const yinyang = document.getElementById("yinyang");
const outer = document.getElementById('outer');
const scalefactor = 1.01;
let scale = 1;

let start = null;
const zoomStep = (timestamp) => {
  if (!start) {
    start = timestamp;
  }

  const progress = timestamp - start;

  // Here, we want to shrink the scale by the scalefactor by exponential transformation
  // You can change the `500` value to whatever value you want to achieve the speed you desire
  scale = scale / Math.pow(scalefactor, progress / 500);
  outer.setAttribute('transform', `scale(${scale})`);

  // Optional: Arbirary limit to stop animation
  if (scale > 0.01) {
    window.requestAnimationFrame(zoomStep);
  }
};

window.requestAnimationFrame(zoomStep);

查看改进的概念验证:

const yinyang = document.getElementById("yinyang");
const outer = document.getElementById('outer');
const scalefactor = 1.01;
let scale = 1;

let start = null;
const zoomStep = (timestamp) => {
  if (!start) {
    start = timestamp;
  }
  
  const progress = timestamp - start;
  
  // Here, we want to shrink the scale by the scalefactor by exponential transformation
  // You can change the `500` value to whatever value you want to achieve the speed you desire
  scale = scale / Math.pow(scalefactor, progress / 500);
  outer.setAttribute('transform', `scale(${scale})`);
  
  // Optional: Arbirary limit to stop animation
  if (scale > 0.01) {
    window.requestAnimationFrame(zoomStep);
  }
};

window.requestAnimationFrame(zoomStep);
.gany {
  filter: invert(1);
  -webkit-filter: invert(1);
  /* Not working for svg <use> elements in Chrome */
}

svg {
  shape-rendering: geometricPresicion;
  /* 
  Attempt to address blurry rasterized image in Firfox */
}

circle {
  position: relative;
}
<svg xmlns="http://www.w3.org/2000/svg" id="yinyang" width="466" height="466" viewBox="-40 -40 80 80">
  <g id="outer" transform="scale(1)">
    <g id="yyyy" transform="scale(20) translate(0 21.8)">
      <g id="yyy">
        <g id="yy">
          <g id="y">
            <circle id="yinner" r="39" />
            <path d="M0,38a38,38 0 0 1 0,-76a19,19 0 0 1 0,38a19,19 0 0 0 0,38" fill="#fff" />
            <circle cy="-19" r="5" />
            <circle cy="19" r="5" fill="#fff" />
          </g>
          <use href="#y" transform="translate(0 , -19) scale(.15)" />
          <use href="#y" class="gany" transform="translate(0 , 19) scale(.15)" />
        </g>
        <use href="#yy" transform="translate(0 , -19) scale(.15)" />
        <use href="#yy" class="gany" transform="translate(0 , 19) scale(.15)" />
      </g>
      <use href="#yyy" transform="translate(0 , -19) scale(.15)" />
      <use href="#yyy" class="gany" transform="translate(0 , 19) scale(.15)" />
    </g>
  </g>
</svg>