Почему SVG создает размытое изображение в Firefox для больших размеров ViewBox - PullRequest
0 голосов
/ 16 июня 2019

Я пытаюсь создать эффект бесконечного масштабирования с помощью одного повторяющегося изображения. Чтобы сохранить качество изображения в разных масштабах, я выбрал используйте SVG.

Проблема в том, что в Firefox изображение остается четким, но оно становится очень размытым, если интервал масштабирования (мс) установлен равным 100 мс или меньше. Особенно на начальных этапах увеличения.

Я пробовал разные значения для свойства стиля "рендеринг фигуры", а также для параметра preserveAspectRatio = false на графике. Изображение остается четким в Chrome. Я не знаю, как это отладить.

Я очень ценю любую помощь или идеи, которые вы можете предложить!

Пример: https://codepen.io/mannadu/pen/MMybwL

Filter

 <?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 Ответ

2 голосов
/ 16 июня 2019

Более подробно из моего комментария: вы должны изменить / обновить атрибут 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>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...