Chrome으로 빌드

다양한 기기가 사용되는 웹에서 LEGO® 블록 사용

Build with Chrome은 데스크톱 Chrome 사용자를 위한 재미있는 실험 버전이며 오스트레일리아에서 처음 출시되어 2014년에 다시 출시되었으며, 전 세계에서 출시되며 The LEGO® 영화와의 연계 및 새로 추가된 휴대기기 지원 기능을 포함합니다. 이 도움말에서는 프로젝트에서 얻은 몇 가지 교훈, 특히 데스크톱 전용 환경에서 마우스와 터치 입력을 모두 지원하는 멀티스크린 솔루션으로의 전환과 관련된 몇 가지 교훈을 공유합니다.

Build with Chrome의 역사

첫 번째 Build with Chrome 버전은 오스트레일리아에서 2012년에 출시되었습니다. 우리는 웹의 강력한 기능을 완전히 새로운 방식으로 증명하고 Chrome을 완전히 새로운 잠재고객에게 제공하고자 했습니다.

사이트의 두 가지 주요 부분이 있습니다. 하나는 사용자가 레고 블록을 사용하여 창작물을 만들 수 있는 '빌드' 모드이고, 다른 하나는 Google 지도의 레고 버전에서 만든 사물을 둘러볼 수 있는 '탐색' 모드입니다.

대화형 3D는 사용자에게 최상의 LEGO 빌드 경험을 제공하는 데 필수적이었습니다. 2012년 WebGL은 데스크톱 브라우저에서만 공개적으로 사용 가능했기 때문에 Build는 데스크톱 전용 환경으로 타겟팅되었습니다. 탐색은 Google 지도를 사용하여 만든 항목을 표시했지만 충분히 확대하면 만든 항목을 3D로 표시하는 지도의 WebGL 구현으로 전환되었으며 여전히 Google 지도를 바닥판 텍스처로 사용합니다. Google은 모든 연령대의 LEGO 애호가들이 쉽고 직관적으로 창의력을 표현하고 서로의 창작물을 살펴볼 수 있는 환경을 만들고자 했습니다.

2013년, Google은 Build with Chrome을 새로운 웹 기술로 확장하기로 결정했습니다. 이러한 기술 중 하나로 Android용 Chrome의 WebGL을 사용하면 Build with Chrome을 모바일 환경으로 발전시킬 수 있습니다. 먼저 '빌더 도구'의 하드웨어에 의문을 제기하기 전에 먼저 터치 프로토타입을 개발하여 모바일 앱과 비교하여 브라우저를 통해 직면할 수 있는 동작 동작과 촉각 반응성을 이해했습니다.

반응형 프런트엔드

우리는 터치와 마우스 입력이 모두 가능한 기기를 지원해야 했습니다. 그러나 작은 터치스크린에서 동일한 UI를 사용하는 것은 공간 제약으로 인해 최적의 솔루션이 아닙니다.

빌드에서는 확대 및 축소, 블록 색상 변경, 벽돌 선택, 회전, 배치 등 다양한 상호작용이 이루어집니다. YouTube는 사용자가 많은 시간을 보내는 도구이므로 자주 사용하는 모든 것에 빠르게 액세스할 수 있어야 하며 편안하게 상호작용할 수 있어야 합니다.

고도의 대화형 터치 애플리케이션을 디자인할 때는 화면이 빠르게 작게 느껴지고, 상호작용하는 동안 사용자의 손가락이 화면의 많은 부분을 덮는 경향이 있음을 발견하게 됩니다. 이는 Builder와 작업할 때 분명해졌습니다. 디자인할 때는 그래픽의 픽셀보다는 실제 화면 크기를 고려해야 합니다. 실제 콘텐츠 전용의 화면 공간을 최대한 넓히기 위해 버튼과 컨트롤의 수를 최소화하는 것이 중요합니다.

우리의 목표는 원래의 데스크톱 구현에 터치 입력을 추가하는 것뿐만 아니라 터치를 위한 것인 것처럼 느껴지는 터치 기기에서 빌드가 자연스럽게 느껴지도록 하는 것이었습니다. 최종적으로는 큰 화면의 데스크톱 및 태블릿용과 작은 화면의 휴대기기용 등 두 가지 버전의 UI를 만들었습니다. 가능하면 단일 구현을 사용하고 모드 간에 유연하게 전환하는 것이 가장 좋습니다. 우리의 사례에서는 이러한 두 모드 간의 경험에 상당한 차이가 있음을 판단하여 특정 중단점을 사용하기로 결정했습니다. 두 버전은 공통된 기능이 많고, 단 한 번의 코드 구현으로 대부분의 작업을 하려고 했지만, UI의 일부 측면은 두 버전 간에 다르게 작동합니다.

사용자 에이전트 데이터를 사용하여 휴대기기를 감지한 다음 표시 영역 크기를 확인하여 소형 화면 모바일 UI를 사용할지 결정합니다. '대형 화면'에 관한 중단점을 선택하는 것은 약간 어렵습니다. 실제 화면 크기의 신뢰할 수 있는 값을 얻기 어렵기 때문입니다. 다행히 우리의 경우에는 대형 화면의 터치 기기에 소형 화면 UI를 표시해도 문제가 되지 않습니다. 이 도구는 여전히 잘 작동하기 때문에 일부 버튼이 약간 크게 느껴질 수도 있기 때문입니다. 마지막으로 중단점을 1000픽셀로 설정합니다. 너비가 1,000픽셀 이상인 창에서 사이트를 로드하면 (가로 모드에서) 대형 화면 버전이 표시됩니다.

두 가지 화면 크기와 환경에 대해 조금 이야기해 봅시다.

대형 화면, 마우스 및 터치 지원

대형 화면 버전은 마우스를 지원하는 모든 데스크톱 컴퓨터와 대형 화면의 터치 기기 (예: Google Nexus 10)에 제공됩니다. 이 버전은 제공되는 탐색 컨트롤의 종류에 있어 원래의 데스크톱 솔루션과 유사하지만 터치 지원과 몇 가지 동작이 추가되었습니다. UI는 창 크기에 따라 조정되므로 사용자가 창 크기를 조절하면 UI 중 일부를 삭제하거나 크기를 조절할 수 있습니다. CSS 미디어 쿼리를 사용합니다.

예: 사용 가능한 높이가 730픽셀 미만이면 탐색 모드의 확대/축소 슬라이더 컨트롤이 숨겨집니다.

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

작은 화면, 터치 지원만 해당

이 버전은 휴대기기 및 소형 태블릿 (Nexus 4 및 Nexus 7 타겟팅)에 게재됩니다. 이 버전에서는 멀티터치 지원이 필요합니다.

소형 화면 기기에서는 콘텐츠에 가능한 한 많은 화면 공간을 제공해야 하므로 공간을 최대화하기 위해 자주 사용하지 않는 요소를 눈에 띄지 않게 이동함으로써 몇 가지 조정을 했습니다.

  • 빌드 벽돌 선택기는 빌드하는 동안 색상 선택기로 최소화됩니다.
  • 확대/축소 및 방향 컨트롤을 멀티 터치 동작으로 대체했습니다.
  • Chrome 전체 화면 기능도 화면 공간을 추가로 확보하는 데 유용합니다.
대형 화면에서 빌드
대형 화면에서 빌드하세요. 벽돌 선택기는 항상 표시되며 오른쪽에 몇 가지 컨트롤이 있습니다.
작은 화면에 빌드
작은 화면에서 빌드하세요. 벽돌 선택기가 최소화되고 일부 버튼이 삭제되었습니다.

WebGL 성능 및 지원

최신 터치 기기는 꽤 강력한 GPU를 갖추고 있지만 데스크톱과는 거리가 멀기 때문에 특히 많은 창작물을 동시에 렌더링해야 하는 3D 탐색 모드에서 특히 성능에 문제가 있다는 것을 알고 있었습니다.

창의적으로 우리는 복잡한 모양과 심지어 투명도를 가진 몇 가지 새로운 유형의 블록을 추가하고 싶었습니다. 일반적으로 GPU에 매우 많은 부담을 주는 기능들이었습니다. 하지만 이전 버전과 호환되어야 하고 첫 번째 버전부터 계속 지원해야 했기 때문에 게임 내 총 블록 수를 크게 줄이는 등의 새로운 제한사항은 설정할 수 없었습니다.

Build의 첫 번째 버전에는 한 창작에 사용할 수 있는 블록의 최대 제한이 있었습니다. 남아 있는 벽돌 수를 나타내는 '블록 미터'가 있었습니다. 새 구현에서는 표준 블록보다 벽돌 미터에 더 많은 영향을 주는 일부 새 블록이 있었으므로 총 최대 블록 수가 약간 줄었습니다. 이는 우수한 성능을 유지하면서 새로운 블록을 포함하기 위한 한 가지 방법이었습니다.

3D 탐색 모드에서는 베이스 플레이트 텍스처 로드, 만든 항목 로드, 만든 애니메이션 및 렌더링 등 상당히 많은 작업이 동시에 이루어집니다. 이를 위해서는 GPU와 CPU 모두에서 많은 작업이 필요하기 때문에 이러한 부분을 최대한 최적화하기 위해 Chrome DevTools에서 많은 프레임 프로파일링을 수행했습니다. 휴대기기에서는 동시에 많은 창작물을 렌더링할 필요가 없도록 작품에 조금 더 가까이 확대하기로 했습니다.

일부 기기에서는 일부 WebGL 셰이더를 다시 검토하고 단순화했지만 항상 문제를 해결하고 앞으로 나아갈 방법을 찾았습니다.

WebGL이 아닌 기기 지원

Google은 방문자 기기가 WebGL을 지원하지 않더라도 사이트를 어느 정도 사용할 수 있기를 바랐습니다. 캔버스 솔루션이나 CSS3D 기능을 사용하여 간단하게 3D를 표현하는 방법이 있습니다. 안타깝게도 WebGL을 사용하지 않고 3D 빌드 및 탐색 기능을 복제하기에 충분한 솔루션을 찾지 못했습니다.

일관성을 위해 창작물의 시각적 스타일은 모든 플랫폼에서 동일해야 합니다. 2.5D 솔루션을 시도해 볼 수도 있었지만 이렇게 하면 만든 항목이 몇 가지 측면에서 다르게 보일 수 있습니다. 또한 Build with Chrome의 첫 번째 버전으로 빌드된 창작물이 첫 번째 버전에서와 마찬가지로 새 버전의 사이트에서도 동일하게 보이고 원활하게 실행되는지 확인해야 했습니다.

WebGL이 아닌 기기에서는 2D 탐색 모드에 계속 액세스할 수 있지만, 새로 만든 항목을 빌드하거나 3D로 탐색할 수 없습니다. 따라서 사용자는 WebGL 지원 기기를 사용하는 경우 프로젝트의 깊이와 이 도구를 사용하여 무엇을 만들 수 있는지에 대한 아이디어를 얻을 수 있습니다. WebGL이 지원되지 않는 사이트는 사용자에게 유용하지 않을 수도 있지만 적어도 티저 역할을 하여 사용자가 테스트에 참여하도록 유도해야 합니다.

WebGL 솔루션의 대체 버전을 유지하는 것이 불가능한 경우도 있습니다. 성능, 시각적 스타일, 개발 및 유지보수 비용 등 다양한 이유가 있을 수 있습니다. 하지만 대체를 구현하지 않기로 결정한 경우에는 최소한 WebGL을 사용하지 않는 방문자를 돌보고, 해당 방문자가 사이트에 완전히 액세스할 수 없는 이유를 설명하며, WebGL을 지원하는 브라우저를 사용하여 문제를 해결하는 방법을 안내해야 합니다.

저작물 관리

Google은 2013년에 출시된 새 Google 지도 버전을 출시 이후 UI가 크게 변경된 새 버전을 출시했습니다. 그래서 새 Google 지도 UI에 맞게 Build with Chrome을 재설계하기로 결정했으며, 그 과정에서 다른 요소도 고려했습니다. 새로운 디자인은 깔끔한 단색과 단순한 모양으로 비교적 평평합니다. 이를 통해 많은 UI 요소에 순수한 CSS를 사용하여 이미지 사용을 최소화할 수 있었습니다.

Explore에는 많은 이미지, 즉 만든 항목의 썸네일 이미지, 바닥판의 지도 텍스처, 실제 3D 창작물을 로드해야 합니다. 계속해서 새 이미지를 로드할 때 메모리 누수가 발생하지 않도록 각별히 주의를 기울이고 있습니다.

3D 생성 항목은 PNG 이미지로 패키징된 맞춤 파일 형식으로 저장됩니다. 이미지로 저장된 3D 생성 데이터를 유지하면 기본적으로 생성을 렌더링하는 셰이더에 데이터를 직접 전달할 수 있습니다.

이 설계 덕분에 모든 사용자 생성 이미지의 경우 모든 플랫폼에서 동일한 이미지 크기를 사용할 수 있었기 때문에 저장용량 및 대역폭 사용량을 최소화할 수 있었습니다.

화면 방향 관리

세로 모드에서 가로 모드로 또는 그 반대로 이동할 때 화면의 가로세로 비율이 얼마나 변경되는지 잊어버리기 쉽습니다. 모바일 기기에 맞춰 캠페인을 조정할 때는 처음부터 이 점을 고려해야 합니다.

스크롤을 사용 설정한 기존 웹사이트에서 CSS 규칙을 적용하여 콘텐츠와 메뉴를 재정렬하는 반응형 사이트를 만들 수 있습니다. 스크롤 기능을 사용할 수만 있다면 상당히 쉽게 관리할 수 있습니다.

이 메서드를 Build와 함께 사용했지만, 레이아웃을 해결할 수 있는 방법이 약간 제한적이었습니다. 콘텐츠가 항상 표시되고 여러 컨트롤과 버튼에 빠르게 액세스할 수 있어야 했기 때문입니다. 뉴스 사이트와 같은 순수한 콘텐츠 사이트의 경우 유동 레이아웃은 많은 도움이 되지만 저희와 같은 게임 앱에는 쉽지 않은 일입니다. 콘텐츠의 개요를 잘 보여주고 편안한 상호작용 방식을 유지하면서 가로 모드 방향과 세로 모드 방향 모두에서 작동하는 레이아웃을 찾는 일은 쉽지 않았습니다. 결국 우리는 Build를 가로 모드로만 유지하기로 결정하고 사용자에게 기기를 회전하도록 지시했습니다.

탐색은 두 방향 모두에서 해결하기가 훨씬 쉬웠습니다. 일관성 있는 환경을 제공하기 위해 방향에 따라 3D의 확대/축소 수준을 조정하기만 하면 되었습니다.

대부분의 콘텐츠 레이아웃은 CSS에 의해 제어되지만 일부 방향 관련 항목은 JavaScript로 구현해야 했습니다. window.orientation을 사용하여 방향을 식별하는 데 적합한 교차 기기 솔루션이 없다는 것을 발견했으므로 결국 window.innerWidth와 window.innerHeight를 비교하기만 했습니다.

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

터치 지원 추가

웹 콘텐츠에 터치 지원을 추가하는 것은 상당히 간단합니다. 클릭 이벤트와 같은 기본 상호작용은 데스크톱 및 터치 지원 기기에서 동일하게 작동하지만 더 고급 상호작용의 경우 터치 이벤트(touchstart, touchmove 및 touchend)도 처리해야 합니다. 이 도움말에서는 이러한 이벤트를 사용하는 기본적인 방법을 설명합니다. Internet Explorer는 터치 이벤트를 지원하지 않고 대신 포인터 이벤트 (pointerdown, pointsmove, pointup)를 사용합니다. 포인터 이벤트는 표준화를 위해 W3C에 제출되었지만 현재는 Internet Explorer에서만 구현됩니다.

3D 탐색 모드에서는 표준 Google 지도 구현과 동일한 탐색을 원했습니다. 즉, 한 손가락을 사용하여 지도를 이동하고 두 손가락을 사용하여 손가락을 모으거나 펼쳐 확대/축소하는 방식을 구현했습니다. 만든 항목이 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,handleDragging,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;
}

삽입된 예: 터치 이벤트를 사용하여 객체를 드래그합니다. Build with Chrome에서 3D 지도를 드래그하는 것과 유사한 구현입니다. http://cdpn.io/qDxvo

멀티터치 동작

멀티 터치 동작 관리를 간소화할 수 있는 HammerQuoJS와 같은 여러 프레임워크나 라이브러리가 있지만, 여러 동작을 결합하고 완벽한 제어를 원한다면 처음부터 작업하는 것이 가장 좋을 때도 있습니다.

손가락 모으기 및 회전 동작을 관리하기 위해 두 번째 손가락이 화면에 표시될 때 두 손가락 사이의 거리와 각도를 저장합니다.

//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. Touchend
  4. 마우스 오버
  5. mousemove
  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을 훨씬 더 많이 사용할 것으로 생각합니다.

아직 하지 않았다면 지금 바로 멋진 앱을 만들어 보세요.