Более подробно из моего комментария: вы должны изменить / обновить атрибут viewBox
, потому что тогда браузер должен постоянно пересчитывать внутреннюю шкалу элемента SVG.Это может привести к проблемам с производительностью или сглаживанию, таким как то, что вы видите в Firefox.
То, что вы пытаетесь сделать, это просто применить еще одно преобразование к вашему элементу SVG.Самый быстрый способ исправить это - просто обернуть внутренний HTML-код вашего SVG-элемента в <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
);
См. подтверждение концепции здесь (также разветвил ваш 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()
Вы заметите, что даже если приведенный выше пример работает, ваша анимация остается прерывистой:это происходит потому, что вы обновляете масштаб элемента <g>
только с интервалами 100 мс, что соответствует частоте кадров 10 к / с.Это движение не будет выглядеть плавным.То, что вы хотите, - это плавно вычислять следующие 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>