말씀드린 경우 표시 영역이 두 개 이상 있습니다.
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
가 실행되지 않습니다.
pageTop
및 pageLeft
를 포함하여 시각적 표시 영역의 모든 변경사항에 관한 소식을 들으려면 창의 스크롤 이벤트도 수신 대기해야 합니다.
visualViewport.addEventListener('scroll', update);
visualViewport.addEventListener('resize', update);
window.addEventListener('scroll', update);
여러 리스너로 작업 중복 방지
창에서 scroll
및 resize
를 수신 대기하는 것과 마찬가지로 일종의 '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 버그인 것 같습니다.
offsetLeft
및 offsetTop
는 반올림되므로 사용자가 확대하면 매우 부정확합니다. 데모에서 이와 관련된 문제를 확인할 수 있습니다. 사용자가 화면을 확대하고 천천히 이동하면 미니 지도가 확대/축소되지 않은 픽셀 간에 맞춰집니다.
이벤트 속도가 느림
다른 resize
및 scroll
이벤트와 마찬가지로 특히 모바일에서 프레임마다 실행되지는 않습니다. 데모에서 이를 확인할 수 있습니다. 손가락을 벌리거나 모아 축소하면 미니 지도가
표시 영역에 고정되지 않습니다.
접근성
데모에서는 visualViewport
을 사용하여 사용자의 손가락 모으기 확대/축소에 대응했습니다. 이 데모에서는 적합하지만, 사용자의 확대 욕구를 무시하는
작업을 수행하기 전에 신중하게 생각해야 합니다.
visualViewport
앱을 사용하여 접근성을 개선할 수 있습니다. 예를 들어 사용자가 확대하는 경우 장식용 position: fixed
항목을 숨겨 사용자가 방해하지 않도록 할 수 있습니다. 그러나 사용자가 더 자세히 보려고 하는 것을 숨기지 않도록 주의하세요.
사용자가 확대하면 분석 서비스에 게시하는 것을 고려할 수 있습니다. 이를 통해 사용자가 기본 확대/축소 수준에서 어려움을 겪는 페이지를 식별할 수 있습니다.
visualViewport.addEventListener('resize', () => {
if (visualViewport.scale > 1) {
// Post data to analytics service
}
});
이상입니다 visualViewport
은 그 과정에서 호환성 문제를 해결하는 훌륭한 API입니다.