Анимация DIV с помощью JavaScript визуализирует артефакты в Chrome

Как эксперимент, я пытаюсь реплицировать функциональность Sprite AS3 в JavaScript без использования объекта canvas. Я думал, что использование абсолютно позиционированных divs и управление их свойствами css было бы неинтересным, однако в Chrome анимация вводит странные артефакты (по-видимому, из-за проблем с перерисованием).

Я не могу найти то, что я делаю неправильно? Код, на самом деле, довольно прост. Вот несколько моментов, которые я пробовал, которые не помогли:

  • Использование относительно расположенных divs (в отличие от абсолютно позиционированных.)
  • Использование полей (в отличие от верхних и левых свойств.)
  • Добавление объектов непосредственно к телу (в отличие от добавления к контейнеру div).
  • Использование setTimeout (в отличие от requestAnimationFrame)

Здесь вы можете увидеть упрощенную скрипту: http://jsfiddle.net/BVJYJ/2/

EDIT: http://jsfiddle.net/BVJYJ/4/

И здесь вы можете увидеть артефакты в моем браузере:

The artifacts in Chrome

Это может быть ошибка в моей настройке (Windows 7 64 бит, Chrome 21.0.1180.75). Никакие другие браузеры не демонстрируют такого поведения. Я был бы очень признателен, если бы кто-нибудь мог прокомментировать, что я могу делать неправильно. Мне больше любопытно, чем это объясняется, а не обходным путем. Тем не менее, каждое объяснение приветствуется.:)

РЕДАКТИРОВАТЬ: В образце кода произошла ошибка, которая привела к использованию setTimeout, даже когда у меня создалось впечатление, что используется RAF. requestAnimationFrame решает проблему с базовым преобразованием, но проблема остается с преобразованиями CSS, такими как вращение.

The artifacts in Chrome with rotation transformation.

Ответы

Ответ 1

У меня была такая же проблема с моим плагином liteAccordion. Его можно устранить, установив видимость обратной стороны на скрытый элемент, который вы анималируете, как вы можете видеть здесь: http://jsfiddle.net/ZPQBp/1/

Ответ 2

Некоторые исследования показывают, что setTimeout может вызывать проблемы по различным причинам. Вы действительно должны использовать requestAnimationFrame:

Таймеры не точно соответствуют миллисекунде. Вот несколько общих таймеров резолюции 1:

  • Internet Explorer 8 и более ранние версии имеют разрешение таймера 15.625мс
  • Internet Explorer 9 и более поздние версии имеют разрешение таймера 4 мс. Firefox
  • и Safari имеют разрешение таймера ~ 10 мс.
  • Chrome имеет разрешение таймера 4 мс.

Internet Explorer до версии 9 имеет разрешение таймера 15,625 ms 1, поэтому любое значение от 0 до 15 может быть 0 или 15, но ничего больше. Internet Explorer 9 улучшил разрешение таймера до 4 мс, но это все еще не очень специфично, когда дело касается анимации.

Разрешение таймера Chromes составляет 4 мс, а Firefox и Safaris - 10 мс. Поэтому, даже если вы установите интервал для оптимального отображения, вы все равно только приближаясь к желаемому сроку.

Ссылка: http://www.nczonline.net/blog/2011/05/03/better-javascript-animations-with-requestanimationframe/

Кроме

setTimeout не учитывает, что еще происходит в браузер. Страница может быть скрыта за вкладкой, забивая ваш процессор, когда это не нужно, или сама анимация могла быть прокручена на странице, что делает вызов обновления лишним. Chrome делает throttle setInterval и setTimeout до 1fps в скрытых вкладках, но это на всех браузерах не следует полагаться.

Во-вторых, setTimeout только обновляет экран, когда захочет, а не когда компьютер способен. Это означает, что ваш плохой браузер должен жонглировать, перерисовывая анимацию при перерисовке всего экрана и если частота кадров анимации не синхронизирована с перерисовкой вашего экрана, он может занять больше вычислительной мощности. Это значит более высокий уровень использования ЦП и вентиляторы ваших компьютеров, или дренирование аккумулятор на вашем мобильном устройстве. Николас Закас делает отличную работу объяснение разрешения таймера воздействия на анимацию в связанной статья.

Ссылка: http://creativejs.com/resources/requestanimationframe/

Ответ 3

Это как-то связано с подпиксельным позиционированием. Если вы округлите до ближайшего пикселя, вы не увидите эти ошибки рендеринга:

thisRef.block.style.left = Math.round((x + (mouseX - ox - x) * .125)) + "px";
thisRef.block.style.top = Math.round((y + (mouseY - oy - y) * .125)) + "px";