我正在尝试使用一张重复的图像创建无限缩放效果。为了保持不同尺寸比例的图像质量,我选择了 使用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>
答案 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>