Создавайте с помощью Chrome

Использование кубиков LEGO® в Интернете для нескольких устройств

«Создавайте с Chrome», забавный эксперимент для пользователей Chrome на настольных компьютерах , первоначально запущенный в Австралии , был переиздан в 2014 году и стал доступен по всему миру, связан с THE LEGO® MOVIE™ и недавно добавленной поддержкой мобильных устройств. В этой статье мы поделимся некоторыми выводами из проекта, особенно в отношении перехода от использования только настольных компьютеров к многоэкранному решению, поддерживающему как мышь, так и сенсорный ввод.

История сборки с помощью Chrome

Первая версия Build with Chrome была запущена в Австралии в 2012 году. Мы хотели продемонстрировать возможности Интернета совершенно по-новому и представить Chrome совершенно новой аудитории.

Сайт состоял из двух основных частей: режима «Стройка», в котором пользователи могли создавать творения из кубиков LEGO, и режима «Исследование» для просмотра творений в версии Google Maps, модифицированной LEGO.

Интерактивное 3D было необходимо, чтобы предоставить пользователям наилучшие впечатления от сборки LEGO. В 2012 году WebGL был общедоступен только в браузерах для настольных компьютеров, поэтому Build планировался как версия только для настольных компьютеров. Explore использовал Google Maps для отображения творений, но при достаточно близком масштабировании переключился на реализацию карты WebGL, показывающую творения в 3D, по-прежнему используя карты Google в качестве текстуры базовой пластины. Мы надеялись создать среду, в которой энтузиасты LEGO всех возрастов могли бы легко и интуитивно выражать свои творческие способности и исследовать творения друг друга.

В 2013 году мы решили расширить возможности Build with Chrome, включив в них новые веб-технологии. Среди этих технологий был WebGL в Chrome для Android, который, естественно, позволил бы Build with Chrome превратиться в мобильную версию. Для начала мы сначала разработали сенсорные прототипы, а затем подвергли сомнению аппаратное обеспечение «Инструмента Builder», чтобы понять поведение жестов и тактильную реакцию, с которыми мы можем столкнуться через браузер по сравнению с мобильным приложением.

Отзывчивый интерфейс

Нам нужно было поддерживать устройства как с сенсорным вводом, так и с мышью. Однако использование того же пользовательского интерфейса на небольших сенсорных экранах оказалось неоптимальным решением из-за нехватки места.

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

При разработке высокоинтерактивного сенсорного приложения вы обнаружите, что экран быстро кажется маленьким, а пальцы пользователя имеют тенденцию закрывать большую часть экрана во время взаимодействия. Для нас это стало очевидным при работе со Строителем. При разработке дизайна действительно нужно учитывать физический размер экрана, а не количество пикселей в графике. Становится важным свести к минимуму количество кнопок и элементов управления, чтобы как можно больше места на экране было отведено под реальный контент.

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

Мы используем данные пользовательского агента для обнаружения мобильных устройств, а затем проверяем размер области просмотра, чтобы решить, следует ли использовать мобильный пользовательский интерфейс с маленьким экраном. Немного сложно выбрать точку останова для того, каким должен быть «большой экран», потому что трудно получить надежное значение физического размера экрана. К счастью, в нашем случае не имеет большого значения, отображаем ли мы пользовательский интерфейс с маленьким экраном на сенсорном устройстве с большим экраном, потому что инструмент все равно будет работать нормально, вот только некоторые кнопки могут показаться слишком большими. В конце мы установили точку останова на 1000 пикселей; если вы загрузите сайт из окна шириной более 1000 пикселей (в альбомном режиме), вы получите широкоэкранную версию.

Давайте немного поговорим о двух размерах экрана и впечатлениях:

Большой экран с поддержкой мыши и сенсорного экрана.

Версия с большим экраном доступна на всех настольных компьютерах с поддержкой мыши и сенсорных устройствах с большими экранами (например, Google Nexus 10). Эта версия близка к оригинальному решению для настольных компьютеров по доступным элементам управления навигацией, но мы добавили поддержку сенсорного управления и некоторых жестов. Мы настраиваем пользовательский интерфейс в зависимости от размера окна, поэтому, когда пользователь изменяет размер окна, он может удалить или изменить размер некоторой части пользовательского интерфейса. Мы делаем это с помощью медиа-запросов CSS.

Пример. Если доступная высота меньше 730 пикселей, ползунок масштабирования в режиме исследования скрыт:

@media only screen and (max-height: 730px) {
    .zoom-slider {
        display: none;
    }
}

Маленький экран, только сенсорная поддержка

Эта версия предназначена для мобильных устройств и небольших планшетов (целевые устройства Nexus 4 и Nexus 7). Эта версия требует поддержки мультитач.

На устройствах с маленькими экранами нам необходимо предоставить контенту как можно больше экранного пространства, поэтому мы внесли несколько изменений, чтобы максимизировать пространство, в основном за счет перемещения нечасто используемых элементов за пределы поля зрения:

  • Во время строительства окно выбора кубиков сворачивается в селектор цвета.
  • Мы заменили элементы управления масштабированием и ориентацией мультитач-жестами.
  • Полноэкранная функциональность Chrome также полезна для получения дополнительной площади экрана.
Создайте на большом экране
Используйте большой экран. Инструмент выбора кирпичей всегда виден, а с правой стороны есть несколько элементов управления.
Создавать на маленьком экране
Стройте на маленьком экране. Средство выбора кирпичей свернуто, а некоторые кнопки удалены.

Производительность и поддержка WebGL

Современные сенсорные устройства имеют довольно мощные графические процессоры, но они все еще далеки от своих настольных аналогов, поэтому мы знали, что у нас возникнут некоторые проблемы с производительностью, особенно в режиме «Обзор 3D», где нам нужно одновременно визуализировать множество творений.

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

В первой версии Build у нас был максимальный лимит кубиков, которые можно было использовать в одном творении. Там был «кирпич-метр», показывающий, сколько кирпичей осталось. В новой реализации некоторые из новых кирпичей влияли на метр кирпичей больше, чем стандартные кирпичи, тем самым немного уменьшая общее максимальное количество кирпичей. Это был один из способов добавить новые блоки, сохранив при этом достойную производительность.

В режиме Explore 3D одновременно происходит довольно много всего; загрузка текстур базовой пластины, загрузка творений, анимация и рендеринг творений и т. д. Это требует больших усилий как от графического процессора, так и от центрального процессора, поэтому мы провели много профилирования кадров в Chrome DevTools, чтобы максимально оптимизировать эти части. На мобильных устройствах мы решили увеличить масштаб творений, чтобы нам не приходилось отображать столько творений одновременно.

Некоторые устройства заставляли нас пересмотреть и упростить некоторые шейдеры WebGL, но мы всегда находили способ решить эту проблему и двигаться вперед.

Поддержка устройств, отличных от WebGL

Мы хотели, чтобы сайт можно было использовать, даже если устройство посетителя не поддерживает WebGL. Иногда существуют способы упрощенного представления 3D-изображений с помощью решения Canvas или функций CSS3D. К сожалению, мы не нашли достаточно хорошего решения для репликации функций Build and Explore 3D без использования WebGL.

Для обеспечения единообразия визуальный стиль творений должен быть одинаковым на всех платформах. Потенциально мы могли бы попытаться использовать 2.5D- решение, но это заставило бы наши творения выглядеть по-другому. Нам также пришлось подумать о том, как обеспечить, чтобы творения, созданные с помощью первой версии Build with Chrome, выглядели так же и работали в новой версии сайта так же гладко, как и в первой.

Режим исследования 2D по-прежнему доступен для устройств, не поддерживающих WebGL, хотя вы не можете создавать новые творения или исследовать в 3D. Таким образом, пользователи по-прежнему могут получить представление о глубине проекта и о том, что они могли бы создать с помощью этого инструмента, если бы они использовали устройство с поддержкой WebGL. Сайт, возможно, не будет так полезен для пользователей без поддержки WebGL, но он должен, по крайней мере, выступать в качестве тизера и вовлекать их в его тестирование.

Сохранение резервных версий для решений WebGL иногда просто невозможно. Есть много возможных причин; производительность, визуальный стиль, затраты на разработку и обслуживание и так далее. Однако, если вы решите не реализовывать запасной вариант, вам следует по крайней мере позаботиться о посетителях, не поддерживающих WebGL, объяснить, почему они не могут получить полный доступ к сайту, и дать инструкции, как они могут решить проблему с помощью браузер с поддержкой WebGL.

Управление активами

В 2013 году Google представила новую версию Google Maps с наиболее значительными изменениями пользовательского интерфейса с момента ее запуска. Поэтому мы решили изменить дизайн Build with Chrome, чтобы он соответствовал новому пользовательскому интерфейсу Google Maps, и при этом учли другие факторы в редизайне. Новый дизайн относительно плоский, с чистыми однотонными цветами и простыми формами. Это позволило нам использовать чистый CSS для многих элементов пользовательского интерфейса, сводя к минимуму использование изображений.

В Explore нам нужно загрузить много изображений; миниатюры изображений для творений, текстуры карт для базовых пластин и, наконец, сами 3D-творения. Мы уделяем особое внимание тому, чтобы не было утечки памяти при постоянной загрузке новых изображений.

3D-модели сохраняются в файле специального формата, упакованном в изображение PNG. Хранение данных 3D-созданий в виде изображения позволило нам, по сути, передавать данные непосредственно в шейдеры, осуществляющие рендеринг творений.

Для всех изображений, созданных пользователями, дизайн позволил нам использовать одинаковые размеры изображений для всех платформ, тем самым минимизируя использование хранилища и пропускной способности.

Управление ориентацией экрана

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

На традиционном веб-сайте с включенной прокруткой вы можете применить правила CSS, чтобы получить адаптивный сайт, который переупорядочивает контент и меню. Пока вы можете использовать функцию прокрутки, это вполне осуществимо.

Мы также использовали этот метод со Build, но были немного ограничены в том, как мы могли решить макет, потому что нам нужно было, чтобы контент был виден в любое время и при этом имел быстрый доступ к ряду элементов управления и кнопок. Для сайтов с чистым контентом, таких как новостные сайты, гибкий макет имеет большой смысл, но для такого игрового приложения, как наше, это было проблемой. Стало непросто найти макет, который работал бы как в альбомной, так и в книжной ориентации, сохраняя при этом хороший обзор контента и удобный способ взаимодействия. В конце концов мы решили оставить сборку только в альбомной ориентации и просим пользователя поворачивать свое устройство.

Исследовать было намного проще в обеих ориентациях. Нам просто нужно было отрегулировать уровень масштабирования 3D-изображения в зависимости от ориентации, чтобы добиться единообразия.

Большая часть макета контента контролируется CSS, но некоторые элементы, связанные с ориентацией, пришлось реализовать в JavaScript. Мы обнаружили, что не существует хорошего решения для разных устройств, позволяющего использовать window.orientation для определения ориентации, поэтому в итоге мы просто сравнивали window.innerWidth и window.innerHeight для определения ориентации устройства.

if( window.innerWidth > window.innerHeight ){
  //landscape
} else {
  //portrait
}

Добавление поддержки сенсорного ввода

Добавить поддержку сенсорного управления к веб-контенту достаточно просто. Базовая интерактивность, такая как событие щелчка, работает одинаково на настольных компьютерах и устройствах с сенсорным экраном, но когда дело доходит до более сложных взаимодействий, вам также необходимо обрабатывать события касания: touchstart, touchmove и touchend. В этой статье рассматриваются основы использования этих событий. Internet Explorer не поддерживает события касания, а вместо этого использует события указателя (pointerdown, pointermove, pointerup). События указателя были отправлены в W3C для стандартизации, но на данный момент реализованы только в Internet Explorer.

В режиме «Обзор 3D» нам нужна была та же навигация, что и в стандартной реализации Google Maps; используя один палец для перемещения по карте и сведение двух пальцев для масштабирования. Поскольку творения выполнены в 3D, мы также добавили жест поворота двумя пальцами. Обычно это требует использования событий касания.

Хорошей практикой является избегать тяжелых вычислений, таких как обновление или рендеринг 3D-изображений в обработчиках событий. Вместо этого сохраните сенсорный ввод в переменной и реагируйте на ввод в цикле рендеринга requestAnimationFrame. Это также упрощает одновременную реализацию мыши: вы просто сохраняете соответствующие значения мыши в одних и тех же переменных.

Начните с инициализации объекта для хранения входных данных и добавьте прослушиватель событий touchstart. В каждом обработчике событий мы вызываем event.preventDefault(). Это сделано для того, чтобы браузер не продолжал обрабатывать событие касания, которое может вызвать непредвиденное поведение, например прокрутку или масштабирование всей страницы.

var input = {dragStartX:0, dragStartY:0, dragX:0, dragY:0, dragDX:0, dragDY:0, dragging:false};
plateContainer.addEventListener('touchstart', onTouchStart);

function onTouchStart(event) {
  event.preventDefault();
  if( event.touches.length === 1){
    handleDragStart(event.touches[0].clientX , event.touches[0].clientY);
    //start listening to all needed touchevents to implement the dragging
    document.addEventListener('touchmove', onTouchMove);
    document.addEventListener('touchend', onTouchEnd);
    document.addEventListener('touchcancel', onTouchEnd);
  }
}

function onTouchMove(event) {
  event.preventDefault();
  if( event.touches.length === 1){
    handleDragging(event.touches[0].clientX, event.touches[0].clientY);
  }
}

function onTouchEnd(event) {
  event.preventDefault();
  if( event.touches.length === 0){
    handleDragStop();
    //remove all eventlisteners but touchstart to minimize number of eventlisteners
    document.removeEventListener('touchmove', onTouchMove);
    document.removeEventListener('touchend', onTouchEnd);
    //also listen to touchcancel event to avoid unexpected behavior when switching tabs and some other situations
    document.removeEventListener('touchcancel', onTouchEnd);
  }
}

Мы не храним вводимые данные в обработчиках событий, а в отдельных обработчиках: handleDragStart, handleDragged и handleDragStop. Это потому, что мы хотим иметь возможность вызывать их и из обработчиков событий мыши. Имейте в виду, что хотя это и маловероятно, но пользователь может одновременно использовать сенсорный ввод и мышь. Вместо того, чтобы решать этот случай напрямую, мы просто следим за тем, чтобы ничего не взорвалось.

function handleDragStart(x ,y ){
  input.dragging = true;
  input.dragStartX = input.dragX = x;
  input.dragStartY = input.dragY = y;
}

function handleDragging(x ,y ){
  if(input.dragging) {
    input.dragDX = x - input.dragX;
    input.dragDY = y - input.dragY;
    input.dragX = x;
    input.dragY = y;
  }
}

function handleDragStop(){
  if(input.dragging) {
    input.dragging = false;
    input.dragDX = 0;
    input.dragDY = 0;
  }
}

При создании анимации на основе touchmove часто бывает полезно сохранить дельта-перемещение с момента последнего события. Например, мы использовали это значение в качестве параметра скорости камеры при перемещении по всем базовым пластинам в режиме «Обзор», поскольку вы не перетаскиваете базовые пластины, а фактически перемещаете камеру.

function onAnimationFrame() {
  requestAnimationFrame( onAnimationFrame );

  //execute animation based on input.dragDX, input.dragDY, input.dragX or input.dragY
 /*
  /
  */

  //because touchmove is only fired when finger is actually moving we need to reset the delta values each frame
  input.dragDX=0;
  input.dragDY=0;
}

Встроенный пример: перетаскивание объекта с помощью событий касания. Аналогичная реализация, как при перетаскивании карты Explore 3D в Build with Chrome: http://cdpn.io/qDxvo

Мультитач-жесты

Существует несколько фреймворков или библиотек, таких как Hammer или QuoJS , которые могут позаботиться об упрощении управления мультитач-жестами, но если вы хотите объединить несколько жестов и получить полный контроль, иногда лучше сделать это с нуля.

Чтобы управлять жестами сведения и поворота, мы сохраняем расстояние и угол между двумя пальцами, когда второй палец касается экрана:

//variables representing the actual scale/rotation of the object we are affecting
var currentScale = 1;
var currentRotation = 0;

function onTouchStart(event) {
  event.preventDefault();
  if( event.touches.length === 1){
    handleDragStart(event.touches[0].clientX , event.touches[0].clientY);
  }else if( event.touches.length === 2 ){
    handleGestureStart(event.touches[0].clientX, event.touches[0].clientY, event.touches[1].clientX, event.touches[1].clientY );
  }
}

function handleGestureStart(x1, y1, x2, y2){
  input.isGesture = true;
  //calculate distance and angle between fingers
  var dx = x2 - x1;
  var dy = y2 - y1;
  input.touchStartDistance=Math.sqrt(dx*dx+dy*dy);
  input.touchStartAngle=Math.atan2(dy,dx);
  //we also store the current scale and rotation of the actual object we are affecting. This is needed to support incremental rotation/scaling. We can't assume that an object is always the same scale when gesture starts.
  input.startScale=currentScale;
  input.startAngle=currentRotation;
}

В событии touchmove мы затем непрерывно измеряем расстояние и угол между этими двумя пальцами. Разница между начальным расстоянием и текущим расстоянием затем используется для установки масштаба, а разница между начальным углом и текущим углом используется для установки угла.

function onTouchMove(event) {
  event.preventDefault();
  if( event.touches.length  === 1){
    handleDragging(event.touches[0].clientX, event.touches[0].clientY);
  }else if( event.touches.length === 2 ){
    handleGesture(event.touches[0].clientX, event.touches[0].clientY, event.touches[1].clientX, event.touches[1].clientY );
  }
}

function handleGesture(x1, y1, x2, y2){
  if(input.isGesture){
    //calculate distance and angle between fingers
    var dx = x2 - x1;
    var dy = y2 - y1;
    var touchDistance = Math.sqrt(dx*dx+dy*dy);
    var touchAngle = Math.atan2(dy,dx);
    //calculate the difference between current touch values and the start values
    var scalePixelChange = touchDistance - input.touchStartDistance;
    var angleChange = touchAngle - input.touchStartAngle;
    //calculate how much this should affect the actual object
    currentScale = input.startScale + scalePixelChange*0.01;
    currentRotation = input.startAngle+(angleChange*180/Math.PI);
    //upper and lower limit of scaling
    if(currentScale<0.5) currentScale = 0.5;
    if(currentScale>3) currentScale = 3;
  }
}

Потенциально вы могли бы использовать изменение расстояния между каждым событием touchmove аналогично примеру с перетаскиванием, но этот подход часто более полезен, когда вам нужно непрерывное движение.

function onAnimationFrame() {
  requestAnimationFrame( onAnimationFrame );
  //execute transform based on currentScale and currentRotation
  /*
  /
  */

  //because touchmove is only fired when finger is actually moving we need to reset the delta values each frame
  input.dragDX=0;
  input.dragDY=0;
}

Вы также можете включить перетаскивание объекта при выполнении жестов сжатия и вращения, если хотите. В этом случае вы должны использовать центральную точку между двумя пальцами в качестве входных данных для обработчика перетаскивания.

Встроенный пример: вращение и масштабирование объекта в 2D. Аналогично тому, как реализована карта в Explore: http://cdpn.io/izloq

Поддержка мыши и сенсорного экрана на одном оборудовании

Сегодня существует несколько портативных компьютеров, таких как Chromebook Pixel, которые поддерживают как мышь, так и сенсорный ввод. Если вы не будете осторожны, это может привести к неожиданному поведению.

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

Если вы не используете event.preventDefault() в своих обработчиках сенсорных событий, также будут запускаться некоторые эмулированные события мыши, чтобы большинство сайтов, оптимизированных без сенсорного управления, продолжали работать. Например, при одном нажатии на экран эти события могут быть запущены в быстрой последовательности и в следующем порядке:

  1. сенсорный старт
  2. сенсорное перемещение
  3. касание
  4. наведение курсора мыши
  5. перемещение мыши
  6. мышь вниз
  7. наведение курсора мыши
  8. щелкнуть

Если у вас более сложные взаимодействия, эти события мыши могут вызвать неожиданное поведение и испортить вашу реализацию. Часто лучше использовать event.preventDefault() в обработчиках событий касания и управлять вводом мыши в отдельных обработчиках событий. Вы должны знать, что использование event.preventDefault() в обработчиках событий касания также предотвратит некоторые действия по умолчанию, такие как прокрутка и событие щелчка.

«В Build with Chrome мы не хотели, чтобы масштабирование происходило, когда кто-то дважды нажимает на сайт, хотя это стандартно для большинства браузеров. Поэтому мы используем метатег области просмотра, чтобы сообщить браузеру не увеличивать масштаб, когда пользователь дважды нажимает на экран. касаний. Это также устраняет задержку клика в 300 мс, что улучшает отзывчивость сайта. (Задержка клика предназначена для того, чтобы различать одиночное касание и двойное касание, когда включено масштабирование двойным касанием.)

<meta name="viewport" content="width=device-width,user-scalable=no">

Помните, что при использовании этой функции вы должны сделать сайт читабельным на экранах всех размеров, поскольку пользователь не сможет приблизить его.

Мышь, сенсорный ввод и ввод с клавиатуры

В режиме «Исследование 3D» мы хотели, чтобы было три способа навигации по карте: мышь (перетаскивание), касание (перетаскивание, масштабирование и вращение пальцами) и клавиатура (навигация с помощью клавиш со стрелками). Все эти методы навигации работают немного по-разному, но мы использовали для всех один и тот же подход; установка переменных в обработчиках событий и действие на них в цикле requestAnimationFrame. Цикл requestAnimationFrame не обязательно должен знать, какой метод используется для навигации.

В качестве примера мы могли бы задать движение карты (dragDX и dragDY) всеми тремя методами ввода. Вот реализация клавиатуры:

document.addEventListener('keydown', onKeyDown );
document.addEventListener('keyup', onKeyUp );

function onKeyDown( event ) {
  input.keyCodes[ "k" + event.keyCode ] = true;
  input.shiftKey = event.shiftKey;
}

function onKeyUp( event ) {
  input.keyCodes[ "k" + event.keyCode ] = false;
  input.shiftKey = event.shiftKey;
}

//this needs to be called every frame before animation is executed
function handleKeyInput(){
  if(input.keyCodes.k37){
    input.dragDX = -5; //37 arrow left
  } else if(input.keyCodes.k39){
    input.dragDX = 5; //39 arrow right
  }
  if(input.keyCodes.k38){
    input.dragDY = -5; //38 arrow up
  } else if(input.keyCodes.k40){
    input.dragDY = 5; //40 arrow down
  }
}

function onAnimationFrame() {
  requestAnimationFrame( onAnimationFrame );
  //because keydown events are not fired every frame we need to process the keyboard state first
  handleKeyInput();
  //implement animations based on what is stored in input
   /*
  /
  */

  //because touchmove is only fired when finger is actually moving we need to reset the delta values each frame
  input.dragDX = 0;
  input.dragDY = 0;
}

Встроенный пример: использование мыши, сенсорного экрана и клавиатуры для навигации: http://cdpn.io/catlf.

Краткое содержание

Адаптация Build with Chrome для поддержки сенсорных устройств с экранами разных размеров стала полезным опытом. У команды не было большого опыта реализации такого уровня интерактивности на сенсорных устройствах, и мы многому научились на этом пути.

Самой большой проблемой оказалось решение проблем пользовательского опыта и дизайна. Технические проблемы заключались в управлении экранами многих размеров, сенсорными событиями и проблемами производительности.

Несмотря на некоторые проблемы с шейдерами WebGL на сенсорных устройствах, они сработали даже лучше, чем ожидалось. Устройства становятся все более мощными, а реализации WebGL быстро улучшаются. Мы считаем, что в ближайшем будущем мы будем гораздо чаще использовать WebGL на устройствах.

А теперь, если вы еще этого не сделали, постройте что-нибудь потрясающее !