Браузер не поддерживает canvas
Но остальная часть страницы остается прежней, что может быть неуместным или вводить в заблуждение. Я бы хотел, чтобы какой-то способ обнаружить несущую холст, чтобы я мог представить остальную часть моей страницы. Что бы вы порекомендовали?
ОТВЕТЫ
Ответ 1
Это метод, используемый в Modernizr, и в основном каждая другая библиотека, которая делает работу с холстом:
Поскольку ваш вопрос был обнаружен, когда он не, я рекомендую использовать его так:
Ответ 2
Существует два популярных метода определения поддержки canvas в браузерах:
Мэтт предлагает проверить существование getContext , также использованный подобным образом библиотекой Modernizr:
Проверка существования интерфейса HTMLCanvasElement , как определено спецификациями WebIDL и HTML. Этот подход также был рекомендован в блоге команды IE 9.
Моя рекомендация - это вариант последнего (см. "Дополнительные примечания") по нескольким причинам:
- Каждый известный браузер, поддерживающий canvas - включая IE 9 - реализует этот интерфейс;
- Это более кратко и мгновенно ясно, что делает код;
- Подход getContext значительно медленнее во всех браузерах, поскольку он включает в себя создание элемента HTML. Это не идеально, когда вам нужно максимально снизить производительность (например, в такой библиотеке, как Modernizr).
Там нет заметных преимуществ использования первого метода. Оба подхода могут быть подделаны, но это вряд ли произойдет случайно.
Дополнительные заметки
Может все еще быть необходимо проверить, что 2D-контекст может быть восстановлен. Как сообщается, некоторые мобильные браузеры могут возвращать true для обеих вышеуказанных проверок, но возвращать null для .getContext('2d') . Вот почему Modernizr также проверяет результат .getContext('2d') . Тем не менее, WebIDL & HTML - опять же - дает нам еще один лучший, более быстрый вариант :
Обратите внимание, что мы можем полностью пропустить проверку элемента canvas и перейти непосредственно к проверке поддержки 2D-рендеринга. Интерфейс CanvasRenderingContext2D также является частью спецификации HTML.
Вы должны использовать подход getContext для обнаружения поддержки WebGL, потому что, даже если браузер может поддерживать WebGLRenderingContext , getContext() может возвращать ноль, если браузер не может взаимодействовать с графическим процессором из-за драйвера проблемы и нет реализации программного обеспечения. В этом случае проверка интерфейса сначала позволяет пропустить проверку для getContext :
Сравнение производительности
Производительность подхода getContext на 85-90% ниже в Firefox 11 и Opera 11 и примерно на 55% ниже в Chromium 18.
Ответ 3
Обычно я запускаю проверку для getContext при создании объекта canvas.
Если он поддерживается, вы можете продолжить установку холста и добавить его в DOM. Это простой пример Progressive Enhancement, который я (лично) предпочитаю более грациозную деградацию.
Ответ 4
Почему бы не попробовать modernizr? Это библиотека JS, которая обеспечивает возможности обнаружения.
Вы когда-нибудь хотели сделать if-statements в вашем CSS для наличие интересных функций, таких как граница радиуса? Ну, с Modernizr вы можете это сделать!
Ответ 5
Ответ 6
Здесь может быть получена: некоторые клиенты не поддерживают методы all canvas.
Ответ 7
Вы можете использовать canisuse.js script, чтобы определить, поддерживает ли ваш браузер холст или нет
Ответ 8
Если вы хотите получить контекст вашего холста, вы можете использовать его в качестве теста:
На холсте можно в совершенстве нарисовать любую желаемую графику, от набора линий или простых геометрических фигур до портрета со всеми мельчайшими подробностями. Но с повышением уровня сложности графики повышается уровень сложности кода. В высшей степени маловероятно, что можно было бы написать самостоятельно весь код, требуемый для создания высококачественного изображения.
К счастью, у разработчиков есть другие варианты, кроме как писать весь код самостоятельно. Возможности контекста рисования не ограничиваются рисованием простых прямых и кривых линий, также существуют методы для вставки готовых изображений, текста, узоров и даже рамок для показа видео.
Вставка в Canvas изображений
Вам, наверное, приходилось видеть веб-страницы со спутниковыми картами того или иного региона планеты. Эти карты создаются из снимков отдельных участков поверхности земли, которые эти страницы загружают со спутника и объединяют в одно изображение. Это пример того, как можно взять несколько изображений и объединить их в одно требуемым образом.
Холст поддерживает работу с обычными изображениями посредством метода текста рисования drawImage(). Чтобы вставить изображение в холст, методу drawImage() в параметрах передается объект изображения и координаты холста, по которым это изображение следует вставить:
Но чтобы передать объект изображения, его сначала нужно получить. В HTML5 есть три разных способа получения объекта изображения. Первый подход — создать его самостоятельно попиксельно с помощью метода createImageData(). Но этот подход очень трудоемкий и медленный.
Второй подход — использовать уже имеющийся в разметке элемент . Например, если у нас есть следующая разметка:
тогда изображение можно вставить в холст с помощью этого кода:
Наконец, можно создать объект изображения и загрузить изображение из отдельного файла. Недостаток этого подхода состоит в том, что изображение нельзя использовать с методом drawImage() до тех пор, пока оно полностью не загрузится. Во избежание проблем нужно подождать, пока не выполнится событие загрузки изображения onLoad, прежде чем пытаться выполнять какие-либо операции с ним.
Чтобы разобраться в этом процессе, рассмотрим пример. Допустим, нам нужно отобразить на холсте изображение face.jpg. Теоретически, это можно сделать такой последовательностью операций:
Здесь проблема состоит в том, что установка атрибута src начинает загрузку изображения, но код продолжает исполняться дальше, не ожидая завершения загрузки. Правильно будет использовать следующий код:
Этот подход может показаться нелогичным, т.к. порядок указания операций в коде не совпадает с порядком их исполнения. В данном примере вызов метода context.drawImage() происходит в последнюю очередь, вскоре после установки свойства img.src.
Изображения имеют широкий диапазон применений. Их можно использовать, чтобы приукрасить штриховые рисунки или как альтернативу рисованию изображений самому. Изображения можно использовать для разных объектов и персонажей, разместив их соответствующим образом на холсте. Изображения можно также применять для заполнения линий, чтобы придать им текстурированный вид.
Искажение рисунков в элементе Canvas
Если обнаружится, что ваше изображение по непонятной причине растянуто, сжато или искажено каким-либо другим образом, наиболее вероятной причиной этому будет установка размера холста посредством правила таблицы стилей. Правильно устанавливать размер холста надо через указание его высоты и ширины в атрибутах height и width элемента в разметке страницы. Может показаться, что эти значения не обязательно задавать в разметке, используя такую форму тега:
а установить их в правиле таблицы стилей следующим образом:
Но этот подход не будет работать. Проблема состоит в том, что свойства height и width CSS отличаются от одноименных свойств элемента . Если не указать размеры холста в разметке, будет установлен размер холста по умолчанию 300x150 пикселов. Потом таблица стилей будет растягивать или сжимать холст, чтобы подогнать его к указанным в ней размерам, изменяя соответствующим образом размеры всего содержимого холста. В результате размещенные на холсте изображения будут искажены, что, несомненно, не сделает их привлекательными.
Во избежание этой проблемы всегда нужно указывать размер холста в разметке посредством атрибутов height и width элемента . А если нужно изменить размер холста на основе каких-либо других критериев, значения этих атрибутов изменяются в разметке с помощью JavaScript.
Обрезка, разрезка и изменение размеров изображения
Функция drawImage() принимает несколько необязательных параметров, которые позволяют манипулировать изображением на холсте. Например, размер изображения можно изменить, указав параметры width и height следующим образом:
В данном случае метод размещает изображение в рамке размером 80x150 пикселов, левый верхний угол которой находится в точке холста с координатами (10,10). Если первоначальный размер изображения был 160x300 пикселов, то эта операция уменьшает его размеры наполовину в обоих направлениях, в результате чего общий размер конечного изображения будет лишь в четверть размера исходного.
Если нужно вставить в холст только часть изображения, методу drawImage() необходимо передать четыре параметра в начале списка параметра. Эти параметры определяют позицию и размер части изображения, которую нужно вырезать:
Последние четыре параметра в этом коде те же, что и в предыдущем примере - они определяют позицию и размер изображения на холсте. Допустим, что мы хотим вставить в холст только верхнюю половину изображения с исходным размером 225x300 пикселов. Для этого с левого верхнего угла изображения (точка (0,0)) отрезаем часть изображения шириной в 225 и высотой в 150 пикселов, которую потом вставляем в холст в начальной точке с координатами (50,25). Все это делается в одной строчке кода:
Возможностей метода drawImage() недостаточно, если вставляемое в холст изображение нужно повернуть или скосить на определенный угол. Но эти и другие манипуляции с изображением на холсте можно выполнить с помощью трансформаций.
Видеокадр
В первом параметре метода drawImage() указывается изображение, которое нужно вставить в холст. Как мы только что увидели, таким изображением может быть созданный объект изображения или элемент , расположенный где-то в другом месте разметки.
Но это не все, что можно вставить в холст посредством этого метода. Вместо изображения можно указать элемент (но не тот, на котором выполняется настоящее рисование). Таким же образом можно вставить и элемент , в котором воспроизводится видео:
При исполнении этого кода выполняется захват одного кадра видео, воспроизводимого в момент выполнения кода, который потом вставляется в холст.
Эта возможность открывает дверь для создания некоторых интересных эффектов. Например, с помощью таймера можно последовательно выполнять захват кадров воспроизводимого видео и вставлять их в холст. Если делать это достаточно быстро, то вставляемая в холст последовательность кадров будет создавать эффект проигрывающегося видео.
Но чтобы это не был просто другой видеопроигрыватель, захваченные кадры можно модифицировать перед тем, как вставлять их в холст. Например, вставляемый кадр можно увеличить или уменьшить или наложить какой-либо эффект в стиле Photoshop, модифицировав его на уровне пикселов. Один из примеров такой манипуляции смотрите на странице "video + canvas = magic". В нем показано, как сделать цветное видео черно-белым, преобразуя каждый цветной пиксел захваченного кадра в оттенок серого.
Вставка в холст текста
Другим типом данных, который нам вряд ли бы хотелось составлять из отдельных прямых и кривых, является текст. К счастью, разработчики HTML5 позаботились и об этом для нас и предоставляют два метода контекста рисования для работы с текстом.
Но прежде чем вставлять в холст какой-либо текст, нужно указать шрифт для него, установив значение свойства контекста font. Это значение указывается посредством строки с таким же синтаксисом, как и для универсального свойства font CSS. Как минимум, нужно указать размер шрифта в пикселах и его название. В случае неуверенности в поддержке определенного шрифта браузерами, можно указать список шрифтов:
Также можно установить жирный шрифт или курсив, указав соответствующие параметры в начале строки:
Самое главное, благодаря CSS3 можно использовать вычурные встроенные шрифты. Для этого нужно лишь сначала зарегистрировать название шрифта в таблице стилей. После установки шрифта текст в холсте вводится с помощью метода fillText(). Например, следующий код вводит в холст строку текста, левый верхний угол которого находится в точке (10,10):
Текст можно вставлять в любое место на холсте, но только по одной строке за раз. Чтобы вставить несколько строк, нужно делать соответствующее число вызовов метода fillText().
Вместо метода fillText() можно использовать другой метод для ввода текста - strokeText(). Этот метод вводит контуры букв текста; цвет и толщина контуров определяются значениями свойств контекста strokeStyle и lineWidth. Далее показано использование этого метода:
Как уже отмечалось, метод strokeText() вводит только очертания букв. Если требуется создать текст одного цвета, а его обводку — другого, можно использовать сначала метод fillText(), а после него метод strokeText().
Операция вывода текста на холст выполняется намного медленнее, чем рисование линий и даже изображений. Скорость не имеет важности при создании статического изображения (например, диаграммы), но может быть фактором при создании интерактивного приложения или анимации. Оптимизировать ввод текста можно, сначала сохранив требуемый текст в файле изображения, а потом отображая его на холсте посредством метода drawImage().
На данный момент мы многое узнали о элементе Canvas и многому научились. Настало время сделать шаг назад и ответить на вопрос, который дамокловым мечом висит над каждой новой возможностью HTML5: насколько безопасно использование этой возможности в плане кроссбраузерности?
К счастью, холст является одной из наиболее поддерживаемых возможностей HTML5. Она поддерживается всеми последними версиями основных браузеров. Конечно же, чем выше версия браузера, тем выше уровень поддержки, т.к. в более новых версиях браузеров увеличивается скорость прорисовки и исправляются ошибки:
Браузер | IE | Firefox | Chrome | Safari | Opera | Safari iOS | Android |
Минимальная версия | 9 | 3.5 | 3 | 4 | 10 | 1 | 1 |
Вряд ли вам придется столкнуться с более старой версией любого из этих браузеров, за исключением Internet Explorer. Но это одно исключение ставит использующих холст разработчиков перед вопросом: как можно использовать холст в вебстраницах так, чтобы не лишать возможности просматривать подобные страницы пользователей старых, но все еще популярных версий этого браузера, таких как IE 8 и IE 7?
Как и в случае со многими другими возможностями HTML5, к решению этой проблемы совместимости есть два подхода. Первый — определять отсутствие поддержки холста браузером и пытаться использовать другое решение. А второй подход — закрыть этот пробел другим инструментом, способным эмулировать холст HTML5, чтобы страницы работали без изменений на старых браузерах. В случае холста второй подход неожиданно оказывается лучшим.
Чтобы позволить старым версиям Internet Explorer работать с холстом, существует несколько надежных резервных решений. Одним из таких решений является библиотека ExplorerCanvas (также называется excanvas), разработанная инженером из Google - Эриком Арвидссоном. Эта библиотека эмулирует холст в Internet Explorer 7 и 8, используя только JavaScript и устаревшую в настоящее время технологию VML (Vector Markup Language, язык векторной разметки).
Скопируйте файл excanvas.js в папку веб-страницы и добавьте следующую ссылку на этот файл:
Обратите внимание, что эта ссылка является условной. Версии Internet Explorer более ранние, чем IE 9, будут использовать ее, но IE 9 и другие браузеры не будут вообще обращать на нее внимания.
Теперь элемент можно использовать без особых проблем. Например, эта библиотека — все что требуется, чтобы наша программа рисования (из предыдущей статьи) работала в старых версиях Internet Explorer.
К сожалению, библиотека ExplorerCanvas — не совсем идеальное решение. Если и попытаетесь использовать более сложные приемы рисования, существует вероятность, что некоторые детали не будут выглядеть так, как вы ожидаете. Основные возможности, не поддерживаемые в ExplorerCanvas, включают радиальные градиенты, тени, обрезку изображений, обработку "сырых" пикселов, а также URL данных.
Для по-настоящему амбициозных задумок, например сложных анимаций или игр с горизонтальной прокруткой экрана (side-scrolling), ExplorerCanvas может оказаться недостаточно быстрой. В такой ситуации следует рассмотреть возможность применения другого заполнителя, использующего высокопроизводительный модуль расширения, такой как Silverlight или Flash. Используйте бесплатную библиотеку FlashCanvas. Подобно библиотеке ExplorerCanvas, эту библиотеку можно подключить к веб-странице с помощью одной строки кода JavaScript. Но в отличие от ExplorerCanvas она использует модуль Flash, без малейшего следа VML.
Совместив на странице возможности холста и библиотеки наподобие FlahsCanvas, вы получите действительно замечательную поддержку практически всех браузеров, известных человечеству в настоящее время. Таким образом, вы обеспечите поддержку не только для немного устарелых версий Internet Explorer, посредством использования Flash, но также и для мобильных устройств, не использующих Flash, таких как iPad и iPhone, с помощью HTML5.
Несколько дней назад я предложил позадавать на Хабре интересующие вопросы по Canvas. Под Хабракатом — ответы на 27 вопросов.
1. Зачем нужен Canvas, что это вообще такое, какова поддержка браузерами, какова основная область применения, насколько развиты фреймворки, примеры?
Canvas — низкоуровневое API для отрисовки графики. Поддерживается всеми современными браузерами. Естественно, не поддерживается устаревшими версиями IE (8 и ниже)
Фреймворки развиваются, хотя им ещё нужно повзрослеть. Базовые примеры можно найти на MDC. Более мощные примеры можно поискать на сайтах а-ля Chrome Experiments или в примерах приложений на фреймворках, например LibCanvas
2. Когда нужно использовать Canvas, а когда Flash?
Flash быстрее, кроссбраузернее, с хорошими инструментами и фреймворками.
Canvas сейчас используется, в основном, энтузиастами и экспериментаторами.
Особой причины уходить с рынка флешерам нету.
Но Canvas'ом занимаются такие крупные игроки, как Google, Mozilla, Apple, Microsoft, все они оптимизируют и ускоряют отрисовку Canvas, постепенно отмирают старые браузеры и приходят новые. Посмотрите на Firefox 2.0 и Firefox 4.0. За три года скорость увеличилась на порядок и основной скачок сделан именно с выходом четвёртой версии. Аналогично — Опера. Также, за это время успел появиться Хром и выпустить уже 12 мажорных версий своего браузера. В общем, у HTML5 и Canvas в частности — светлое будущее.
3. Когда использовать Canvas, а когда SVG?
С одной стороны, при использовании Canvas необходимо будет реализовывать то, что уже реализовано в SVG. С другой стороны, в Canvas можно применить такие оптимизации, которые в SVG невозможны, например отрисовка из кеша.
На мобильных телефонах актуально использование CSS3 вместо SVG и Canvas, т.к. оно ускоряется аппаратно и очень плавно работает.
4. Какую литературу почитать?
Рекомендую начать с Mozilla Developers Network, там очень классно и с примерами описаны основы Canvas. После этого придумайте себе задание и постарайтесь его реализовать. API — очень простой, тут главное — опыт.
Есть свежая книжка "HTML5 Canvas" издательства O'Reilly Media. Я не читал, но O'Reilly обычно выпускают классные книжки.
5. Как сделать скриншот Canvas?
Есть небольшая библиотека canvas2image она позволяет сохранять Canvas что на сервер, что на клиент. На клиенте сохранение производится при помощи toDataURL. На сервере получается содержимое при помощи getImageData, транслируется в base64 код и отправляется POST-запросом. На сервере достаточно сделать что-то типа такого кода:
Если «Скриншот канваса === Сохранить канвас как картинку», то:
Важно Вы должны задать фиксированные размеры канваса (через width/height или style), иначе получете плохие данные из toDataURL
6. Интересуют методы улучшения быстродействия (поднятие fps).
Есть разные методы оптимизации, которые зависят от приложения. Три из них я описывал в топике Пятнашки на LibCanvas. Это:
* Обновление холста исключительно при необходимости
* Вместо перерисовки всего холста перерисовывать только изменившиеся куски
* Отрисовка объектов в буфер (что позволяет рисовать объект каждый кадр как набор пикселей, а не применять все фильтры и кучу матана)
Вам очень поможет профайлер в вашем любимом браузере.
7. Работа с видеозахватом
Если вы про захват видео с камеры:
API есть только в черновике спецификации Media Capture API ближайший релиз спецификации возможен в PhoneGap — возможно есть в транке. Работать с ним будет очень просто. Вешается обработчик на «устройство», каждый кадр передается в виде картинки в формате data uri:
8. Каково самое эффективное решение на данный момент для попиксельного доступа при отрисовке произвольного изображения на Canvas (без WebGL)? Например, ручная прорисовка граней при построении 3D с использованием закраски по Гуро/Фонгу.
Для попиксельного доступа есть только одно решение — использовать getImageData
9. Есть ли пути эффективно и кросплатформенно смасштабировать канву со всеми внутренностями под размеры экрана?
Попробуйте использовать css. canvas < width: 100%; height: 100%; >. Как-то так. Но js-код должен учитывать этот кусок, т.к. сместятся координаты.
10. Поддержка и быстродействие на Android/iOS устройствах
Поддерживается полностью. Правда, я на iPhone2 заметил неподдержку fillText, но это единственное.
Проблема с производительностью, но кое-что можно запустить. Пока для мобильников лучше использовать CSS3. Возможно, в будущем, что-то поменяется.
11. Интересует самый быстрый способ нарисовать точку (например, для графика). Однопиксельную и четырёхпиксельную, произвольного цвета.
Самый быстрый с точки зрения производительности — использовать fillRect для одиночных отрисовок и getImageData+putImageData для массовых отрисовок.
Я точно не знаю, как оно делается в biolab. Есть несколько путей. Можно наложить несколько слоёв canvas друг на друга, отрисовать на нижнем мир и отображать нужную часть при помощи CSS.
13. 3d-canvas
14. Редакторы — в чём писать?
Подходит любой редактор JavaScript. Раньше я пользовался Netbeans 7, сейчас перешёл на Jetbrains WebIde
15. База данных
Для хранения данных на стороне клиента есть два современных стандарта — webStorage и IndexedDB.
IndexedDB — это крутой интерфейс с кучей возможностей, описывался на Хабре, а webStorage — простое key-value хранилище
16. Canvas и IE
В IE до девятой версии не поддерживается. Все попытки сделать его поддерживаемыми можно назвать подходящими только для очень узкого круга задач и не дают вменяемой скорости.
Имхо, единственный вариант — это Google Chrome Frame, плагин, который устанавливается на полубраузер как Flash или SilverLight и превращает говно в конфетку Internet Explorer в современный браузер.
17. Как посчитать расстояние между нарисованными объектами, есть ли готовые решения
Зависит от объектов. Некоторые вещи делаются очень просто. Например, расстояние между точками считается по теореме Пифагора. Между кругами — считаем расстояние между точками от отнимаем радиусы. У более сложных фигур есть свои законы.
Кое-что (по крайней мере пересечения ректанглов/кругов/полигонов) уже есть в LibCanvas. Если у вас какие-то особые требования — необходимо искать алгоритмы. Я могу посоветовать вот что:
Известные алгоритмы определения столкновений и реакции на них во флэше.
18. Как и какими средствами лучше делать анимации на Canvas?
19. Работа с текстом в Canvas (в т.ч. анимирование)
Текст отрисовывается при помощи fillText/strokeText. Свои шрифты можно подключать при помощи CSS3.
На него воздействуют все правила — такие как тени, трансформации, установка цветов и т.п. К примеру, с помощью светлой тени на тёмном фоне, можно легко сделать светящийся текст
20. Работа с изображениями.
21. Использование Бэк Буффера, как отрисовать один Canvas в другой.
Вы можете отрисовать какую-либо информацию в скрытый Canvas, который затем использовать точно так же как картинку (смотреть предыдущий пункт).
Буферизация позволяет ускорить отрисовку многократно. Например, отрисовать один градиент из буфера в 5 раз быстрее, чем отрисовать этот же градиент напрямую.
Использовать очень просто:
22. Анимация в канвасе происходит методом полной перерисовки. Таким образом информация обо всех объектах хранится в объекте JS и каждый раз перерисовывается, или можно как то создавать спрайты и слои?
Да, есть различные приёмы. Можно использовать буфера для того, чтобы не отрисовывать десятки мелких объектов, можно перерисовывать холст только частично, но чаще всего дешевле просто всё перерисовать, чем понять, что перерисовывать надо, а что — нет.
23. Хотелось бы узнать как с помощью canvas нарисовать 3д объект(желательно с учетом перспективы) и вращать?
24. Отрисовка SVG в Canvas
Можно при помощи CanVG. Смысла практически нету)
25. Насколько различается поддержка в браузерах или все следуют стандарту?
Различия минимальны. Обнаруживались мелкие баги, легкие несоответствия. Например, Опера не могла отрисовать прямоугольник с отрицательными размерами сторон:
По разному сжимаются и поворачиваются картинки. Например, в Хроме при повороте на углах заметны зубья (нету сглаживания)
Зато он лучше, чем Fx и Opera растягивает картинки:
Есть ещё мелкие различия в JavaScript. Например, в некоторых браузерах sort реализует неустойчивую сортировку, так что, если элементы сортировать по Z-индексу, то элементы с одинаковым Z-индексом будут менятся местами.
Это очень мелкие нюансы. Большинство — скрыто за дружелюбным API фреймворков. Лично я разрабатываю только под один браузер и в большинстве случаев всё работает совершенно корректно и в остальных.
26. putImageData vs drawImage
Буду краток — putImageData значительно медленнее. Более того, с увеличением размеров картинки — увеличивается медлительность.
27. Мне было бы очень интересно послушать про типовые реализации основного функционала canvas-библиотек, таких как: эмуляция слоев, определение активного элемента (на котором в данный момент находится курсор), создание системы управления событиями и т. д.
Этот вопрос задавали мне чаще всего, потому для него — отдельный топик)
Ребята, кто может дать ответы на эти вопросы — прошу в комменты
2. Существуют ли какие-то секретные библиотеки, умеющие рисовать линии переменной толщины. В случае с прямыми это относительно несложно реализовать «костыльным» способом, а вот всякие кривые Безье — видимо, только на низком пиксельном уровне.
3. Существуют ли какие-то секретные библиотеки, умеющие рисовать градиентную раскраску линий. Ну то есть чтобы цвет плавно менялся между узлами вдоль линии, не обязательно прямой. Такая функция есть, например, в OpenGL.
Вывод
Браузеры, поддерживающие HTML5, и платформа HTML5 для Windows 8 Metro сегодня становятся серьезными кандидатами для разработки современных игр.
Благодаря canvas, у вас есть доступ к аппаратно-ускоренной поверхности, на которой вы можете отображать контент вашей игры и с помощью некоторых трюков и ухищрений вы сможете достигнуть великолепной производительности рендеринга до 60 кадров в секунду. Подобная непрерывность действительно важна в играх, так как чем плавнее игра (анимация), тем лучше чувствует себя игрок.
Цель данной статьи — дать вам несколько подсказок, как выжать максимум мощности из HTML5 Canvas. Статья состоит из двух основных частей [вы читаете первую]. David Rousset скоро опубликует вторую часть.
В статье я буду показывать ключевые идеи на одном и том же примере — это эффект 2D-туннеля, который я написал для Coding4Fun-сессии на TechDays 2012 во Франции.
На написание данного эффекта меня вдохновил мой код для Commodore AMIGA, который я написал, когда был молодым автором демосцен в далеких 80х :). Сегодня он использует только canvas и Javascript, хотя изначальный код базировался только на 68000 ассемблере:
Пример на JSFiddle (учтите, что текстура зашита в код).
Целью данной статьи является объяснить, не как запрограммирован туннель, а как вы можете взять имеющийся код и оптимизировать его, чтобы добиться лучшей производительности в реальном времени.
Использование скрытого canvas для чтения данных изображения
Первая вещь, о которой я хочу поговорить, это то, как вы можете использовать canvas для оптимизации чтения данных изображения. Действительно, практически в каждой игре вам нужна графика для спрайтов или фоновых изображений. В canvas есть очень удобный метод для отрисовки изображений: drawImage. Эта функция может использоваться для вывода спрайта в canvas-элемент, так как вы можете указать области источника и назначения для отрисовки.
Но иногда этого не достаточно. Например, когда вы хотите применить какие-то эффекты к исходному изображению. Или когда исходное изображение не просто картинка, а более сложный ресурс для вашей игры (к примеру, карта, из которой вам нужно считывать данные).
В таких случаях вам необходим доступ к внутренним данным изображения. Но image-тег не предоставляет способа для считывания содержимого. Вот тут-то на помощь и приходит canvas!
В сущности, каждый раз, когда вам нужно считать содержимое изображения, вы можете использовать невидимый (не выводимый на экран) canvas. Ключевая идея заключается в том, чтобы загрузить изображение, и, когда оно загружено, вам остается только отобразить его на canvas-элемент, не вставленный в DOM. Теперь у вас есть доступ к [скопированным] пикселям исходного изображения через соответствующие пиксели canvas (что очень просто).
Код для этой техники выглядит следующим образом (используется в эффекте 2D-туннеля для чтения текстуры туннеля):
При использовании этого кода имейте в виду, что загрузка текстуры происходит асинхронно, поэтому вам необходимо через параметр then передать функцию для продолжения работы вашего кода:
Использование возможности аппаратного масштабирования
Современные браузеры и Windows 8 поддерживают аппаратное ускорение canvas. Это, в частности, означает, что вы можете использовать GPU для масштабирования контента в canvas.
В случае с эффектом 2D-туннеля алгоритм требует обработки каждого пикселя canvas. К примеру, для canvas размером 1024x768 необходимо обработать 786432 пикселей. Чтобы обеспечить непрерывность отображения, это нужно делать 60 раз в секунду, что соответствует обработке 47185920 пикселей в секунду!
Очевидно, что любое решение, которое помогает вам уменьшить количество пикселей, приведет к заметному улучшению производительности.
Повторю, canvas предоставляет такое средство! Следующий код показывает, как использовать аппаратное ускорение для масштабирования внутреннего буффера canvas до внешнего размера DOM-объекта::
Обратите внимание на разницу между размером DOM-объекта (canvas.style.width и canvas.style.height) и размером рабочего буффера canvas (canvas.width и canvas.height).
При наличии разницы между этими двумя размерами используются возможности железа для масштабирования рабочего буффера — в нашем случае это просто великолепная функция: мы можем работать в меньшем разрешении и позволить GPU смасштабировать результат, чтобы заполнить DOM-объект (с использованием прекрасного бесплатного фильтра размытия для сглаживания результата).
В данном примере рендеринг производится в области 300x200, а GPU масштабирует до размеров вашего окна.
Эта возможность широко поддерживается всеми современными браузерами, поэтому вы можете рассчитывать на нее.
Оптимизация цикла отрисовки
Когда вы разрабатываете игру, у вас, наверняка, должен быть цикл рендеринга, в котором вы отрисовываете все компоненты игры (фон, спрайты, очки и т.д.). Этот цикл — узкое горлышко в вашем коде и должен быть максимально оптимизирован, чтобы быть уверенным, что ваша игра работает быстро и плавно.
RequestAnimationFrame
Одна из интересных возможностей, пришедших с HTML5, это функция window.requestAnimationFrame. Вместо использования window.setInterval для создания таймера, вызывающего ваш цикл отрисовки каждые (1000/16) миллисекунд (чтобы достичь заветных 60fps), вы можете делегировать эту ответственность на браузер с помощью requestAnimationFrame. Вызов данного метода говорит, что вы хотите, чтобы браузер вызвал ваш код сразу же, как только будет возможно обновить графическое представление.
Браузер включит ваш запрос внутрь своего расписания отрисовки и синхронизирует вас со своим кодом отрисовки и анимации (CSS, переходы, и т.д.). Это решение также интересно в связи с тем, что ваш код не будет вызываться, когда окно не видно (свернуто, полностью перекрыто и т.д.).
Это может помочь с производительностью, так как браузер может оптимизировать параллельную отрисовку (например, если ваш цикл рендерига слишком медленный) и при этом выдавать плавные анимации.
Код довольно очевиден (обратите внимание на использование браузерных префиксов):
Для использования этой функции вам достаточно вызвать ее в конце вашего цикла отрисовки, чтобы подписаться на следующий кадр:
Доступ к DOM (Document Object Model)
Чтобы оптимизировать свой цикл отрисовки, вам следует использовать как минимум одно золотое правило: НЕ ОБРАЩАЙТЕСЬ К DOM. Хотя современные браузеры специально оптимизированы в этом месте, чтение свойств DOM-объектов по-прежнему слишком медленное для быстрого цикла отрисовки.
Например, в моем коде я использовал профилировщик Internet Explorer 10 (доступный в инструментах разработчика по F12) и результаты очевидны:
Как вы можете видеть, доступ к ширине и высоте canvas занимает очень много времени в цикле отрисовки!
Изначальный код выглядел так:
Вы можете заменить свойства canvas.width и canvas.height двумя переменными с заранее заведенными правильными значениями:
Просто, не правда ли? Возможно, что не очень просто понять, но, поверьте мне, это стоит попробовать!
Предварительные вычисления
Согласно профилировщику, функция Math.atan2 несколько медленная. На самом деле, эта операция не зашита внутрь CPU, так что среда выполнения JavaScript должна проделать некоторые операции, чтобы вычислить результат.
В целом, если вы можете организовать предварительный расчет для некоторого долго работающего кода, это всегда хорошая идея. Здесь прежде, чем запустить мой цикл отрисовки, я рассчитываю значения для Math.atan2:
Массив atans далее может использовать внутри цикла отрисовки для явного повышения производительности.
Избегайте использования Math.round, Math.floor и parseInt
Пункт на счет использования parseInt имеет смысл в нашем случае:
Когда вы работаете с canvas, для указания на пиксели необходимо использовать целочисленные координаты (x и y). Однако все ваши вычисления производятся с использованием чисел с плавающей точкой и рано или поздно вам нужно будет конвертировать их в целые числа.
JavaScript предоставляет функции Math.round, Math.floor или даже parseInt для конвертации чисел в целые. Но эти функции делают некоторую дополнительную работу (в частности проверяют диапазоны или проверяют, что значения действительно являются числам; parseInt вообще первым делом конвертирует свой параметр в строку!). Таким образом, внутри моего цикла отрисовки мне нужен более быстрый способ для конвертации чисел.
Вспоминая мой старый код на ассемблере, я решил применить небольшой трюк: вместо использования parseInt достаточно просто сдвинуть число вправо на 0. Среда выполнения переместит число с плавающей точкой из соответствующего регистра в целочисленный и применить аппаратное преобразование. Сдвиг числа вправо на 0 оставит число без изменений и вернет вам назад целочисленное значение.
Исходный код был таким:
Новый код выглядит так:
Конечно, это решение требует, чтобы вы были наверняка уверены в корректности передаваемого числа :)
Финальный результат
Применение всех описанных оптимизаций приводит к следующему отчету:
Как видите, теперь код выглядит хорошо оптимизированным с использованием только ключевых функций.
Мы начали с такого оригинального неоптимизированного туннеля:
И пришли к такому результату после оптимизации:
[пер.: данные скриншоты сделаны на комьютере переводчика, на котором fps не достигает заветных 60fps, но крутится довольно близко к этому — около 50fps в IE10.]
Мы можем оценить вклад каждой оптимизации следущей диаграммой, показывающей частоту кадров на моем компьютере:
Двигаясь дальше
Помня об этих ключевых моментах, теперь вы готовы к разработке быстрых и плавных игр для современных браузеров и Windows 8!
Читайте также: