从我的评论中进一步阐述:你不应该改变/更新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()
您会注意到,即使上面的示例有效,您的动画仍然不稳定:这是因为您仅以 100 毫秒的间隔更新 <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>