뷰포트가 두 개 이상 있다고 가정해 보겠습니다.
BRRRRAAAAAAAMMMMMMMMMM
현재 사용 중인 뷰포트는 실제로 뷰포트 내의 뷰포트입니다.
BRRRRAAAAAAAMMMMMMMMMM
DOM에서 제공하는 데이터가 이러한 뷰포트 중 하나를 참조하고 다른 뷰포트는 참조하지 않는 경우도 있습니다.
BRRRRAAAAM… 잠깐, 뭐라고요?
맞습니다. 다음을 참고하세요.
레이아웃 표시 영역과 시각적 표시 영역
위의 동영상에는 스크롤되고 핀치 확대/축소되는 웹페이지와 페이지 내 뷰포트의 위치를 보여주는 오른쪽의 미니 맵이 나와 있습니다.
일반 스크롤 시에는 매우 간단합니다. 녹색 영역은 position: fixed
항목이 고정되는 레이아웃 뷰포트를 나타냅니다.
핀치 확대/축소가 도입되면 이상한 일이 발생합니다. 빨간색 상자는 실제로 볼 수 있는 페이지의 일부인 시각적 표시 영역을 나타냅니다. 이 뷰포트는 position: fixed
요소가 레이아웃 뷰포트에 연결된 상태로 원래 위치에 유지된 채로 이동할 수 있습니다. 레이아웃 뷰포트의 경계에서 화면 이동을 하면 레이아웃 뷰포트가 함께 드래그됩니다.
호환성 개선
안타깝게도 웹 API는 참조하는 뷰포인트 측면에서 일관되지 않으며 브라우저 간에 일관되지도 않습니다.
예를 들어 element.getBoundingClientRect().y
는 레이아웃 표시 영역 내의 오프셋을 반환합니다. 좋습니다. 하지만 페이지 내 위치가 필요한 경우가 많으므로 다음과 같이 작성합니다.
element.getBoundingClientRect().y + window.scrollY
그러나 많은 브라우저는 window.scrollY
에 시각적 표시 영역을 사용하므로 사용자가 핀치 줌하면 위 코드가 중단됩니다.
Chrome 61에서는 window.scrollY
를 변경하여 대신 레이아웃 뷰포트를 참조합니다. 즉, 위 코드는 핀치 확대/축소 시에도 작동합니다. 실제로 브라우저는 레이아웃 표시 영역을 참조하도록 모든 위치 속성을 서서히 변경하고 있습니다.
단 하나의 새 속성을 제외하고…
시각적 표시 영역을 스크립트에 노출
새 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
가 실행되지 않습니다.
pageTop
및 pageLeft
를 비롯한 시각적 뷰포트의 모든 변경사항을 수신하려면 창의 스크롤 이벤트도 수신 대기해야 합니다.
visualViewport.addEventListener('scroll', update);
visualViewport.addEventListener('resize', update);
window.addEventListener('scroll', update);
여러 리스너로 인한 작업 중복 방지
창에서 scroll
및 resize
를 리슨하는 것과 마찬가지로 결과적으로 일종의 '업데이트' 함수를 호출할 수 있습니다. 그러나 이러한 이벤트가 동시에 발생하는 경우가 많습니다. 사용자가 창 크기를 조절하면 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 버그라고 생각합니다.
offsetLeft
및 offsetTop
은 반올림되므로 사용자가 확대하면 상당히 부정확합니다. 데모에서 이 문제의 원인을 확인할 수 있습니다. 사용자가 확대하고 천천히 화면을 이동하면 미니 맵이 확대되지 않은 픽셀 간에 스냅됩니다.
이벤트 비율이 느림
다른 resize
및 scroll
이벤트와 마찬가지로 특히 모바일에서는 프레임마다 실행되지 않습니다. 데모에서 확인할 수 있습니다. 핀치 확대/축소를 하면 미니맵이 표시 영역에 고정된 상태로 유지되지 않습니다.
접근성
데모에서는 visualViewport
를 사용하여 사용자의 핀치 줌을 상쇄했습니다. 이 특정 데모에서는 적절하지만 사용자의 확대 의도를 무시하는 작업을 하기 전에 신중하게 생각해야 합니다.
visualViewport
은 접근성을 개선하는 데 사용할 수 있습니다. 예를 들어 사용자가 확대하는 경우 장식용 position: fixed
항목을 숨겨 사용자가 방해받지 않도록 할 수 있습니다. 하지만 사용자가 자세히 살펴보려고 하는 항목을 숨기지 않도록 주의하세요.
사용자가 확대할 때 분석 서비스에 게시하는 것이 좋습니다. 이를 통해 사용자가 기본 확대/축소 수준에서 어려움을 겪는 페이지를 파악할 수 있습니다.
visualViewport.addEventListener('resize', () => {
if (visualViewport.scale > 1) {
// Post data to analytics service
}
});
이상입니다 visualViewport
는 호환성 문제를 해결하는 유용한 소형 API입니다.