VisualViewport 소개

제이크 아치볼드
제이크 아치볼드

말씀드린 경우 표시 영역이 두 개 이상 있습니다.

BRRRRAAAAAAAMMMMMMMMMM

현재 사용 중인 표시 영역은 사실 표시 영역 안에 있는 표시 영역입니다.

BRRRRAAAAAAAMMMMMMMMMM

또한 DOM이 제공하는 데이터가 이러한 표시 영역 중 하나를 참조하지만 다른 표시 영역은 참조하지 않을 때도 있습니다.

BRRRRAAAAM... 뭐라고 해야 할까요?

사실입니다. 다음을 살펴보세요.

Layout 표시 영역과 시각적 표시 영역 비교

위 동영상은 스크롤 및 손가락 모으기/확대/축소 중인 웹페이지를 보여주며 오른쪽에 있는 미니 지도는 페이지 내 표시 영역의 위치를 보여줍니다.

정기적인 스크롤을 사용하면 순조롭게 앞에 놓이게 됩니다. 녹색 영역은 position: fixed 항목이 고정되는 레이아웃 표시 영역을 나타냅니다.

핀치 줌을 도입하면 상황이 이상해집니다. 빨간색 상자는 실제로 볼 수 있는 페이지 부분인 시각적 표시 영역을 나타냅니다. position: fixed 요소가 레이아웃 표시 영역에 연결된 상태로 유지되는 동안 이 표시 영역이 이동할 수 있습니다. 레이아웃 표시 영역의 경계에서 화면을 이동하면 화면 이동 시 레이아웃 표시 영역도 함께 드래그합니다.

호환성 개선

안타깝게도 웹 API는 참조하는 표시 영역의 측면에서 일관되지 않으며 브라우저 간에도 일관되지 않습니다.

예를 들어 element.getBoundingClientRect().y레이아웃 표시 영역 내의 오프셋을 반환합니다. 멋지지만 페이지 내 위치를 원하는 경우가 많으므로 다음과 같이 작성합니다.

element.getBoundingClientRect().y + window.scrollY

그러나 대부분의 브라우저는 window.scrollY시각적 표시 영역을 사용합니다. 즉, 사용자가 손가락을 모으거나 펼쳐 확대/축소하면 위의 코드가 깨집니다.

Chrome 61에서는 window.scrollY를 변경하여 레이아웃 표시 영역을 대신 참조합니다. 즉, 위 코드는 손가락 모으기로 확대/축소해도 작동합니다. 실제로 브라우저는 레이아웃 표시 영역을 참조하도록 모든 위치 속성을 천천히 변경합니다.

단, 새 속성 1개는 예외입니다.

스크립트에 시각적 표시 영역 노출

새 API는 시각적 표시 영역을 window.visualViewport로 노출합니다. 이는 교차 브라우저 승인이 적용된 초안 사양이며 Chrome 61로 출시됩니다.

console.log(window.visualViewport.width);

window.visualViewport의 결과는 다음과 같습니다.

숙박 시설 visualViewport
offsetLeft 시각적 표시 영역의 왼쪽 가장자리와 레이아웃 표시 영역 사이의 거리(CSS 픽셀)입니다.
offsetTop 시각적 표시 영역의 상단 가장자리와 레이아웃 표시 영역 사이의 거리(CSS 픽셀)입니다.
pageLeft 시각적 표시 영역의 왼쪽 가장자리와 문서의 왼쪽 경계 사이의 거리(CSS 픽셀)입니다.
pageTop 시각적 표시 영역의 상단 가장자리와 문서의 상단 경계 사이의 거리(CSS 픽셀)입니다.
width 시각적 표시 영역의 너비(CSS 픽셀)입니다.
height CSS 픽셀로 표시되는 시각적 표시 영역의 높이입니다.
scale 손가락 모으기 확대/축소로 적용된 배율입니다. 확대/축소로 인해 콘텐츠 크기가 두 배로 커지면 2가 반환됩니다. 이는 devicePixelRatio의 영향을 받지 않습니다.

다음과 같은 몇 가지 이벤트도 있습니다.

window.visualViewport.addEventListener('resize', listener);
visualViewport 이벤트
resize width, height 또는 scale가 변경되면 실행됩니다.
scroll offsetLeft 또는 offsetTop가 변경되면 실행됩니다.

데모

이 도움말의 시작 부분에 있는 동영상은 visualViewport를 사용하여 제작되었습니다. Chrome 61 이상에서 확인하세요. visualViewport를 사용하여 미니 지도가 시각적 표시 영역의 오른쪽 상단에 고정되도록 하고 손가락 모으기로 확대/축소하더라도 항상 동일한 크기로 표시되도록 역 배율을 적용합니다.

잡음

시각적 표시 영역이 변경될 때만 이벤트가 실행됩니다.

말하기에 당연한 것 같지만 제가 처음 visualViewport로 플레이했을 때 저를 사로잡았어요.

레이아웃 표시 영역의 크기는 조절되지만 시각적 표시 영역의 크기는 조절되지 않는 경우 resize 이벤트가 발생하지 않습니다. 그러나 시각적 표시 영역 없이 너비/높이가 변경되지 않고 레이아웃 표시 영역의 크기가 조절되는 것은 드문 일입니다.

스크롤이 중요합니다. 스크롤이 발생하지만 시각적 표시 영역이 레이아웃 표시 영역을 기준으로 정적인 상태를 유지하면 visualViewport에서 scroll 이벤트가 발생하지 않습니다. 이는 매우 흔한 일입니다. 일반적인 문서 스크롤 중에 시각적 표시 영역이 레이아웃 표시 영역의 왼쪽 상단에 고정된 상태로 유지되므로 visualViewport에서 scroll실행되지 않습니다.

pageToppageLeft를 포함하여 시각적 표시 영역의 모든 변경사항에 관한 소식을 들으려면 창의 스크롤 이벤트도 수신 대기해야 합니다.

visualViewport.addEventListener('scroll', update);
visualViewport.addEventListener('resize', update);
window.addEventListener('scroll', update);

여러 리스너로 작업 중복 방지

창에서 scrollresize를 수신 대기하는 것과 마찬가지로 일종의 'update' 함수를 호출할 수 있습니다. 그러나 이러한 이벤트 중 상당수가 동시에 발생하는 것이 일반적입니다. 사용자가 창 크기를 조절하면 resize가 트리거되지만 scroll도 트리거되는 경우가 많습니다. 성능을 개선하려면 변경사항을 여러 번 처리하지 마세요.

// Add listeners
visualViewport.addEventListener('scroll', update);
visualViewport.addEventListener('resize', update);
addEventListener('scroll', update);

let pendingUpdate = false;

function update() {
    // If we're already going to handle an update, return
    if (pendingUpdate) return;

    pendingUpdate = true;

    // Use requestAnimationFrame so the update happens before next render
    requestAnimationFrame(() => {
    pendingUpdate = false;

    // Handle update here
    });
}

단일 update 이벤트와 같은 더 나은 방법이 있을 수 있기 때문에 이와 관련하여 사양 문제를 제출했습니다.

이벤트 핸들러가 작동하지 않음

Chrome 버그로 인해 다음의 작동하지 않습니다.

금지사항

버그 – 이벤트 핸들러 사용

visualViewport.onscroll = () => console.log('scroll!');

다음을 수행합니다.

권장사항

작동 – 이벤트 리스너 사용

visualViewport.addEventListener('scroll', () => console.log('scroll'));

오프셋 값은 반올림됨

이 문제가 또 다른 Chrome 버그인 것 같습니다.

offsetLeftoffsetTop는 반올림되므로 사용자가 확대하면 매우 부정확합니다. 데모에서 이와 관련된 문제를 확인할 수 있습니다. 사용자가 화면을 확대하고 천천히 이동하면 미니 지도가 확대/축소되지 않은 픽셀 간에 맞춰집니다.

이벤트 속도가 느림

다른 resizescroll 이벤트와 마찬가지로 특히 모바일에서 프레임마다 실행되지는 않습니다. 데모에서 이를 확인할 수 있습니다. 손가락을 벌리거나 모아 축소하면 미니 지도가 표시 영역에 고정되지 않습니다.

접근성

데모에서는 visualViewport을 사용하여 사용자의 손가락 모으기 확대/축소에 대응했습니다. 이 데모에서는 적합하지만, 사용자의 확대 욕구를 무시하는 작업을 수행하기 전에 신중하게 생각해야 합니다.

visualViewport 앱을 사용하여 접근성을 개선할 수 있습니다. 예를 들어 사용자가 확대하는 경우 장식용 position: fixed 항목을 숨겨 사용자가 방해하지 않도록 할 수 있습니다. 그러나 사용자가 더 자세히 보려고 하는 것을 숨기지 않도록 주의하세요.

사용자가 확대하면 분석 서비스에 게시하는 것을 고려할 수 있습니다. 이를 통해 사용자가 기본 확대/축소 수준에서 어려움을 겪는 페이지를 식별할 수 있습니다.

visualViewport.addEventListener('resize', () => {
    if (visualViewport.scale > 1) {
    // Post data to analytics service
    }
});

이상입니다 visualViewport은 그 과정에서 호환성 문제를 해결하는 훌륭한 API입니다.