웹뷰 - Fixed, 가상 키보드, VisualViewport 사용

TH_velog·2024년 2월 28일
4

javascript

목록 보기
1/1
post-thumbnail

✍️기록

📌 fixed, 가상 키보드

📖 모바일 웹에서 position:fixed; bottom:0;
고정 시킨 영역이 키보드를 열었을 시 키보드 위에 자리 잡기.

※ 키보드 유무에 따른 대응

🔗 참고 url ⭐⭐⭐⭐⭐

📗 채널톡 IOS 대응기

📗 VisualViewport

✅ EX) 네이버앱 실행 네이버 검색 화면처럼 키보드 위에 영역

⚠️ 참고 : Safari -> 네이버 검색

  • 앱으로 실행했을 때와 다르게 하단 고정 된 메뉴가 없습니다.

⚠️ Android, IOS 키보드 테스트

🟢 Android - 삼성 인터넷, 네이버 앱 👈 원하는 동작.

  • 키보드를 제외한 영역 만큼의 높이를 viewport로 조절.
  • fixed 영역 키보드 위에 노출.

🔴 Android - Chrome

  • fixed 영역 키보드에 가려져 보이지 않는다.
  • 키보드를 열면 document 위에 키보드가 생성되고 viewport 스크롤 생성.

🔴 IOS - Safari, Chrome

  • fixed 영역 키보드에 가려져 보이지 않는다.
  • 키보드를 열었을 경우 키보드의 높이가 추가가 된다.
  • 가상의 영역 생성, 가상의 영역 스크롤 끝 부분 도달 후 내부의 스크롤 동작

※ position: fixed 동작하지 않는 것이 아닌 가상의 영역 스크롤로 인해 동작하지 않는것처럼 보인다.

✅ IOS 테스트

  • html, body border - blue, red
  • body background - gray
  • 확인용 fixed(top,middle,bottom 위치) 영역 : fixed

✅ 문제의 키보드 열었을 시 가상영역

  • 키보드 없을 경우엔 스크롤 시 바운스 후 벗어나지 않고 다시 원상태로 돌아온다.
  • input 클릭하여 키보드를 열고 스크롤을 할 경우 아래에 가상영역(빈 공간)이 생긴 것을 알 수 있습니다.
  • ⚠️ fixed가 제대로 적용이 되지 않게 보여지지만 html, body 기준으로 잘 적용되고 있습니다.

⌨️ 참고 url 채널톡 해결 방법

  • 터치하여 움직이는 영역이 scrollable하다(overflow: auto | scroll)
  • scrollable한 영역이 실제로 스크롤이 되어야 한다(내부에 height가 더 높은 다른 element가 존재해야한다)
.wrap {
  position:relative;
  overflow-y: auto;
  height:100%;
  background:#FFF3CF;
}
#make-scrollable {
  position: absolute;
  left: 0;
  width: 1px;
  height: calc(100% + 1px); 
  /* 100% 보다 1px 높게 하여 .wrap scroll 발생하도록 */
}

⚠️ 채널톡 해결 방법과 차이가 있는 구조라서 그런지.. 문제 발견

  • 내부 element가 스크롤이 정상적으로 되지만 키보드가 올라온 만큼 가려져서
    fixed: bottom 영역이 보이지 않고 element의 영역도 다 보이지 않는 문제..!
  • fixed된 영역을 잡고 스크롤 할 경우 생성된 가상영역이 보여지도록 스크롤이 되는 경우가 발생.
  • fixed 영역을 잡고 스크롤할 경우 가상 영역이 다시 보이는 문제

✅ 다른 방법으로!!

  • 키보드 열었을 경우 제외한 보여지는 view 영역기준 새롭게 fixed
  • 스크롤 진행 후 가상 영역이 보인다면 보이지 않도록 스크롤 이동

📖 VisualViewport 사용

🔗 VisualViewport 참고 자료

✅ VisualViewport

  • 주어진 창에 대한 시각적 뷰포트를 나타내는 인터페이스
  • 최상위 창의 개체 기준

👉 키보드가 생겼을 때 VisualViewport 사용해서
키보드 영역을 제외한 보여지는 view영역을 지정.

☝️
// 📍scroll
function handleWindowScroll(){
  windowScroll.textContent = window.scrollY;
}
function handleViewportScroll(e){
  viewportScroll.textContent = e.target.offsetTop;
}
// window scroll, visualViewport scroll 다르기에 둘 다 사용!
window.addEventListener('scroll',handleWindowScroll);
visualViewport.addEventListener("scroll", handleViewportScroll);

// 📍 resize
function viewportResize () {
  const bodyHeight = document.body.offsetHeight;
  const windowInnerHeight = window.innerHeight;
  const viewportHeight = visualViewport.height;
  // body height 확인용
  bodyHeightText.textContent = bodyHeight;
  // window height 확인용
  windowHeightText.textContent = windowInnerHeight;
  // viewport height 확인용
  viewportHeightText.textContent = viewportHeight;
}
// visualVieport resize 사용
visualViewport.addEventListener('resize',viewportResize);

📍 👀 테스트 확인용
position: fixed 영역

  • w-scroll: window scroll event
  • viewport-scroll: visualViewport scroll event
  • body-H: body height
  • window-H: window innerHeight
  • viewport-H: VisualViewport height

✅ 테스트 확인 결과

  • 키보드 열기 전 window innerHeight와 VisualViewport height 같다.
  • 키보드 열었을 때 window innerHeight와 VisualViewport height 다르다
  • window.scrollY 값과 viewport scroll 값은 다르다.

✔️ 키보드를 열었을 때 viewport 값을 구해서 viewport 영역 활용.
✔️ window.scrollY, viewport scroll 차이 값을 활용.

📗 키보드 유무에 따른 viewport 영역 지정

✅ IOS 키보드 유무 확인

const windowInnerHeight = window.innerHeight;
const viewportHeight = parseInt(visualViewport.height);
let isKeyboard = false;

// ✔️ IOS 키보드 On & Off
if(windowInnerHeight > viewportHeight){ 
  // 키보드 ON
  isKeyboard = true;
}else{
  // 키보드 OFF
  isKeyboard = false;
}

✅ windowInnerHeight 보다 viewport height(보여지는 화면) 값이 작을 때 키보드를 열었을 경우로 생각!

  • 키보드 유무에 viewport height 값 변경을 확인할 수 있습니다.
  • 키보드 ON일 경우 viewport 영역을 지정.
  • 지정된 영역의 높이를 변경한다.
  • 노란색 라인 영역은 viewport에 맞게 높이가 바뀌도록 하였고 top center bottom 위치를 나타내기 위해 position: absolute 사용!!
  • ⚠️ 단점으로는 키보드가 열린 후 실행이 되어 시간 차가 발생
// IOS 키패드 전체 화면 높이보다 viewport 높이가 작을 때
if(windowInnerHeight > viewportHeight){
  isKeyboard = true;
  // 👇 높이 지정
  viewportwrap.style.height = `${viewportHeight}px`; 
}else{
  isKeyboard = false;
  viewportwrap.style.height = "100%"; // or auto
}

📗 viewport 영역 - scroll

✅ 키보드가 열린 후 스크롤 시 새롭게 높이가 지정된 viewport가 잘려 보이게 되는 문제를 해결하기 위해 2가지를 생각하였습니다.
1. 스크롤 시 키보드를 닫는다.
2. window scroll 값과 viewport scroll 값을 계산하여 viewport 영역을 움직인다.

✔️ 1번이 간단한 방법이지만 2번의 경우도 필요하여 테스트 진행!!

📍 👀 추가된 테스트 확인용

  • offsetTop: visualViewport.offsetTop
  • pageTop: visualViewport.pageTop
  • gapTop: offsetTop - pageTop
  • translateY: viewport Box 위치

✅ 키보드 On - window.innerHeight > visualViewport.height

  • 제공되는 높이와 실제 보여지는 높이의 차이 발생 시 window scroll 이벤트와 visualViewport scroll 이벤트를 실행.
  • 키보드 열었을 경우 viewport(가운데 위치한 녹색 영역) height 수정.
  • 스크롤 값에 따라 viewport(가운데 위치한 녹색 영역)을 움직입니다.

✅ viewport(가운데 위치한 녹색 영역)을 스크롤 시 보여지는 화면의 위치를 생각하고 스크롤 된 값과 비교!!
최종 값으로 transform:translateY으로 움직이게 하였습니다. 😁

// EX) 키보드 ON - scroll 이벤트 
if(windowInnerHeight > viewportHeight){ // 키보드 ON
  isKeyboard = true;
  viewportwrap.style.height = `${viewportHeight}px`;
  window.addEventListener('scroll',handleWindowScroll);
  visualViewport.addEventListener("scroll", handleViewportScroll);
}else{  // 키보드 OFF - scroll 이벤트 해제
  isKeyboard = false;
  viewportwrap.style.height = "100%";
  window.removeEventListener('scroll',handleWindowScroll);
  visualViewport.removeEventListener("scroll", handleViewportScroll);
}

// scroll event
function handleWindowScroll(){
  let viewportTopGap = parseInt(visualViewport.pageTop - visualViewport.offsetTop);
  let translateY = parseInt(window.scrollY - viewportTopGap);
  // 👇 scroll 변화에 따라 viewport div 이동
  viewportwrap.style.transform = `translateY(${translateY}px)`;
}
// viewport scroll 
function handleViewportScroll (e){ 
  // viewport scroll
  const viewportScrollY = parseInt(e.target.offsetTop);
  // IOS에서는 사용하지 않고 확인용으로만 👀
  // viewport scroll 값을 계산한다면 사용할 수 있습니다.
}

✅ viewport(녹색 영역) 스크롤 시 키보드 위로 움직임 확인

⚠️ 하단 가상영역으로 인해 스크롤 오차 발생.

문제의 하단 가상영역 스크롤 제한을 하여 가상영역이 보여지지 않도록 한다.

✅ scrollY(스크롤) + visualViewport.height (보여지는 화면 높이) > body height 넘는다면 더 이상 넘어가지 않도록
scroll 제한!!

// 가상 영역까지 스크롤 내려가는 것을 방지
if(window.scrollY + visualViewport.height > document.body.offsetHeight - 2){ 
    window.scrollTo(0, document.body.offsetHeight - visualViewport.height-1);
}

✅ 자연스럽게 움직여지지 않고 있지만, viewport에 맞게 보여질 수 있도록 수정 완료!

⚠️ 더 자연스럽게, 중간 중간 끊김, 작은 오류, 추가로 수정이 필요!

📖 Android - Chrome 앱

✔️ Android 삼성 앱에서는 문제 없이 정상 동작 되지만 크롬 앱으로 확인 시
IOS와 동일하게 화면 일부가 키보드로 가려져 fixed가 정상 동작이 안되는 것처럼 확인.

  • IOS와 동일하게 해결! 하면 끝날 줄 알았지만 Android Chrome과 IOS는 다르게 동작을 하고 있습니다.
  • 키보드로 가려지는 영역이 생겼지만 하단 가상 영역은 존재하지 않는다.
  • Android Chrome 키보드 열고 나서 스크롤 시 window scroll이 먼저 움직이는 것이 아닌 viewport scroll을 먼저 움직이고 window scroll이 움직이게 되어 동작에 오류가 발생을 했습니다.

🤔
가장 간단한 방법으로 IOS와 Android scroll 이벤트를 다르게 적용.

// Android 실행
function handleViewportScroll (e){ 
  const viewportScrollY = parseInt(e.target.offsetTop);
  viewportwrap.style.transform = `translateY(${viewportScrollY}px)`;
}

visualViewport.addEventListener("scroll", handleViewportScroll);

✔️ ☝️ viewport scroll에서만 동작을 했을 경우

  • window scroll 기능 삭제 or 주석 후 확인
IOS Android Chrome
  • IOS의 경우 viewport scroll로만 움직임을 제어할 경우 window scroll이 멈춘 후에 동작 되듯이 보여 툭 툭 끊기는 움직임을 확인할 수 있습니다.

  • Android Chrome의 경우 viewport scroll로 움직임을 제어 시 부드러운 움직임을 확인할 수 있습니다.

✅ IOS와 Android 각 기능별로 나눠 사용.
✅ IOS에서 끊기지만 viewport scroll 하나로 둘 다 사용.
✅ IOS와 Android의 scroll 차이를 알고 있다면 굳이 나눠서 사용하지 않아도 충분히 사용이 가능!

⚠️ 좀 더 자연스럽고 오류 없는 움직임을 위해 추가로 수정이 필요.

✍️ 끝

감사합니다. 😁

profile
공부&기록

0개의 댓글