CSS 심층 분석 - 완벽한 프레임의 맞춤 스크롤바를 위한 trix3d()

맞춤 스크롤바는 극히 드물게 발생하며, 스크롤바는 웹상의 나머지 부분 중 하나인데 날짜 선택기를 보고 있습니다. JavaScript를 사용하여 직접 빌드할 수 있지만, 비용이 많이 들고 느리게 느껴질 수 있습니다. 이 기사에서는 색다른 CSS 매트릭스를 사용하여 스크롤할 필요가 없는 맞춤 스크롤러를 빌드할 수 있습니다. 스크롤하는 동안 JavaScript, 일부 설정 코드만.

요약

사소한 것에 신경 안 써? 다만 냥 고양이 데모 라이브러리를 가져오는 것인가요? 데모 코드는 GitHub 저장소.

LAM;WRA (긴 수학적이며 계속 읽음)

얼마 전에 우리는 시차 스크롤러를 빌드했는데, 기사는 무엇인가요? 정말 훌륭하고 시간을 할애할 가치가 있습니다.) CSS 3D를 사용하여 요소를 다시 푸시함 변환, 요소가 실제 스크롤 속도보다 느리게 움직였습니다.

요약

먼저 시차 스크롤러의 작동 방식을 요약해 보겠습니다.

애니메이션에서 볼 수 있듯이 요소를 푸시하여 시차 효과를 얻었습니다. 3D 공간에서 Z축을 따라 '뒤로' 문서 스크롤은 사실상 변환이 생성됩니다. 예를 들어 100픽셀씩 아래로 스크롤하면 요소가 위쪽으로 100픽셀씩 번역됩니다. 이는 모든 요소에 적용되며 '더 멀리'에 있는 광고도 검색할 수 있습니다. 하지만 다음 위치에서 더 멀리 떨어져 있기 때문에 카메라에서 관찰된 화면상의 움직임도 100px 미만이기 때문에 패럴랙스 효과를 적용합니다.

물론 요소를 다시 공간으로 이동하면 이미지가 더 작아지고 요소의 크기를 다시 조정하여 수정합니다. 정확한 계산을 통해 애플리케이션을 빌드할 때 패럴랙스 스크롤러 모든 세부정보를 반복하지 않겠습니다.

0단계: 수행할 작업

스크롤바 그것이 바로 우리가 빌드할 것입니다. 하지만 한 번이라도 어떻게 하는 지에 관한 질문입니다. 확실히 하지 않았어요. 스크롤바는 사용 가능한 콘텐츠 중 현재 표시 가능한 진행률 확인할 수 있습니다 아래로 스크롤하면 스크롤바가 진전을 보이고 있음을 나타냅니다. 모든 콘텐츠가 스크롤바는 일반적으로 숨겨져 있습니다. 콘텐츠의 높이가 표시 영역의 2배인 경우 스크롤바가 표시 영역 높이의 1⁄2을 채웁니다. 높이의 3배에 해당하는 콘텐츠 표시 영역이 스크롤바의 크기를 표시 영역의 1⁄3 등으로 조정합니다. 패턴이 보입니다. 스크롤하는 대신 스크롤바를 클릭하고 드래그하여 도움이 됩니다. 눈에 띄지 않는 직원이 눈에 잘 띄지 않는 사람들이 확인할 수 있습니다. 한 번에 하나씩 싸우죠.

1단계: 거꾸로 만들기

CSS 3D를 사용하면 요소가 스크롤 속도보다 느리게 움직이도록 할 수 있습니다. 시차 스크롤 도움말에 설명된 대로 변환됩니다. Google의 데이터 웨어하우스를 알려주시겠어요? 할 수 있다는 것이 밝혀졌고 그렇게 해서 맞춤 스크롤바를 표시합니다. 이것이 어떻게 작동하는지 이해하려면 몇 가지 CSS 3D 기본사항을 살펴보았습니다.

수학적 의미에서 원근 투영을 하려면 Cloud Shell에서 동종 좌표를 참조하세요. 그것이 무엇이고 왜 작동하는지는 자세히 설명하지 않겠지만 w이라는 추가 네 번째 좌표가 있는 3D 좌표와 같습니다. 이 좌표는 1이어야 합니다(원근 왜곡을 원하는 경우 제외). w의 세부사항은 걱정하지 않아도 됩니다. 여기서는 1과 다른 값으로 구성됩니다. 따라서 모든 점은 지금부터 4차원 벡터에 있습니다. [x, y, z, w=1] 이므로 행렬은 4x4도 가능합니다.

CSS가 공식을 사용하여 변환 속성에서 자체 4x4 행렬을 matrix3d() 함수를 사용하세요. matrix3d는 인수 16개를 사용합니다 (행렬이 4x4), 한 열을 차례로 지정합니다. 이 함수를 사용하여 회전, 변환 등을 수동으로 지정할 수 있습니다. 하지만 그 w 좌표가 엉망진창이라고 하네요!

matrix3d()를 사용하려면 3D 컨텍스트가 필요합니다. 3D 컨텍스트에서는 원근 왜곡이 없으며 좌표로 표현됩니다. 3D 컨텍스트를 만들려면 perspective 및 내부의 일부 요소는 새로운 3D 공간을 만들었습니다. 대상 :

CSS의
    Perspective 속성에
적용할 수 있습니다

Perspective 컨테이너 내부의 요소는 CSS 엔진에서 처리됨 방법은 다음과 같습니다.

  • 요소의 각 모서리 (꼭짓점)를 동질 좌표로 바꾸기 [x,y,z,w]: 원근 컨테이너를 기준으로 합니다.
  • 요소의 모든 변환을 오른쪽에서 왼쪽으로 행렬로 적용합니다.
  • 원근 요소를 스크롤할 수 있는 경우 스크롤 매트릭스를 적용합니다.
  • 원근 행렬을 적용합니다.

스크롤 행렬은 y축에 따른 변환입니다. 아래로 스크롤하면 400px인 경우 모든 요소를 400px 위로 이동해야 합니다. 원근 행렬은 3D로 사라지는 지점에 더 가까이 점을 '끌어오는' 행렬입니다. 존재한다는 것입니다. 이렇게 하면 이미지가 더 작아 보일 때 두 가지 효과를 모두 얻을 수 있습니다. 이동하면서 이동 속도가 느려질 수 있습니다. 따라서 요소를 밀어넣을 경우 400픽셀의 변환이 화면에서 300픽셀만큼만 이동합니다.

모든 자세한 내용은 사양을 확인합니다. 이 문서에서는 기본 머신러닝 워크플로에 관한 참조하세요.

상자는 perspective의 값 p가 있는 Perspective 컨테이너 안에 있습니다. 컨테이너를 스크롤할 수 있고 아래로 스크롤하면 n픽셀

원근 행렬 곱하기 행렬 곱하기 요소 변환 행렬
  네 번째 행의 p에 대해 1을 뺀 값이 4x4 행렬입니다.
  세 번째 열에 4x4 행렬 곱하기 두 번째 열 곱하기 빼기 n
  행 네 번째 열에 요소 변환 행렬을 곱한 값입니다.

첫 번째 매트릭스는 원근 매트릭스, 두 번째 매트릭스는 스크롤 매트릭스입니다 행렬입니다. 요약: 스크롤 매트릭스의 역할은 아래로 스크롤되므로 음의 부호가 표시됩니다.

하지만 스크롤바의 경우 반대 요소인 아래로 스크롤할 때 아래로 이동할 수 있습니다. 여기서 트릭을 사용할 수 있습니다. 상자 모서리의 w 좌표를 반전합니다. w 좌표가 -1로 설정하면 모든 변환이 반대 방향으로 적용됩니다. 그렇다면 뭐라고요? CSS 엔진은 상자의 모서리를 w를 1로 설정합니다. matrix3d()이(가) 빛날 시간입니다.

.box {
  transform:
    matrix3d(
      1, 0, 0, 0,
      0, 1, 0, 0,
      0, 0, 1, 0,
      0, 0, 0, -1
    );
}

이 행렬은 w를 부정하는 것 외에는 어떤 작업도 하지 않습니다. CSS 엔진에서 각 모서리를 [x,y,z,1] 형태의 벡터로 바꾸면 행렬은 다음과 같이 됩니다. [x,y,z,-1]로 변환합니다.

네 번째 행에 p보다 1이 마이너스 1이 있는 4x4 단위행렬
  세 번째 열에 4x4 행렬 곱하기 두 번째 열 곱하기 빼기 n
  행 네 번째 열에 4x4 행렬을 곱한 값에서 마이너스 1이
  네 번째 행의 네 번째 열에 4차원 벡터 x, y, z, 1은 4가 됩니다.
  네 번째 행의 세 번째 열에 있는 p에 1을 빼고 1을 더한 4개의 단위행렬로
  두 번째 행의 네 번째 열에 - n, 네 번째 행의 -1
  네 번째 열은 4차원 벡터 x, y 더하기 n, z, z 및 z를 뺀 값과 같음
  p - 1입니다.

요소 변환의 효과를 보여주는 중간 단계를 나열했습니다. 행렬입니다. 행렬 계산이 익숙하지 않더라도 괜찮습니다. 유레카 마지막 행에서 스크롤 오프셋 n을 y에 추가하게 됩니다. 좌표의 값을 빼는 대신 사용합니다. 요소가 아래쪽으로 변환됩니다. 아래로 스크롤하면

그러나 이 행렬을 : 요소가 표시되지 않습니다. 이는 CSS 사양에 w <가 있는 꼭짓점 0은 요소가 렌더링되지 않도록 차단합니다. 그리고 우리의 z 좌표는 현재 0, p는 1, w는 -1입니다.

다행히 z 값을 선택할 수 있습니다. w=1이 되도록 하려면 z = -2로 설정합니다.

.box {
  transform:
    matrix3d(
      1, 0, 0, 0,
      0, 1, 0, 0,
      0, 0, 1, 0,
      0, 0, 0, -1
    )
    translateZ(-2px);
}

보세요. Box가 돌아왔습니다!

2단계: 움직이기

이제 상자가 생겼고 사용할 수 있습니다. 지금은 Perspective 컨테이너는 스크롤할 수 없으므로 하지만 요소가 선을 그리면 다른 방향으로 이동한다는 것을 알고 있습니다. 스크롤했습니다. 그럼 컨테이너를 스크롤해 보겠습니다. 단순히 공간을 차지하는 스페이서 요소입니다.

<div class="container">
    <div class="box"></div>
    <span class="spacer"></span>
</div>

<style>
/* … all the styles from the previous example … */
.container {
    overflow: scroll;
}
.spacer {
    display: block;
    height: 500px;
}
</style>

이제 상자를 스크롤하세요. 빨간색 상자가 아래로 이동합니다.

3단계: 크기 지정

페이지를 아래로 스크롤하면 아래로 이동하는 요소가 있습니다. 어려운 일이죠 완전히 짜증 나네요. 이제 스크롤바처럼 보이도록 스타일을 지정해야 합니다. 좀 더 양방향으로 만들 수 있습니다.

스크롤바는 일반적으로 'thumb'과 '트랙'으로 구성되지만 트랙은 항상 표시됩니다. 엄지손가락의 높이는 엄지손가락의 높이에 콘텐츠가 표시되는지 확인합니다

<script>
    const scroller = document.querySelector('.container');
    const thumb = document.querySelector('.box');
    const scrollerHeight = scroller.getBoundingClientRect().height;
    thumb.style.height = /* ??? */;
</script>

scrollerHeight는 스크롤 가능한 요소의 높이입니다. scroller.scrollHeight는 스크롤 가능한 콘텐츠의 총 높이입니다. scrollerHeight/scroller.scrollHeight는 표시됩니다. 엄지손가락을 덮는 세로 공간의 비율은 표시되는 콘텐츠의 비율:

<ph type="x-smartling-placeholder">
</ph> 스크롤러 높이 위의 엄지 점 스타일 점 높이가 스크롤러 높이와 같음
  엄지 점 스타일 점 높이인 경우에만 스크롤러 점 스크롤 높이
  스크롤러 높이 x 스크롤러 높이 초과 스크롤 도트 스크롤과 같음
  높이.
<script>
    // …
    thumb.style.height =
    scrollerHeight * scrollerHeight / scroller.scrollHeight + 'px';
    // Accommodate for native scrollbars
    thumb.style.right =
    (scroller.clientWidth - scroller.getBoundingClientRect().width) + 'px';
</script>

엄지손가락의 크기는 좋습니다, 매우 빠르게 움직이고 있습니다. 여기에서 이 테크닉을 패럴랙스 스크롤러입니다. 엘리먼트를 더 뒤로 이동하면 움직일 때 이동 속도가 느려집니다. 사용할 수 있습니다. 크기를 확장하여 수정할 수 있습니다. 하지만 어느 정도의 정확하게 되돌리시겠어요? 짐작하셨겠지만 수학 문제를 풀어볼까요? 이번이 마지막입니다. 약속하지 않죠.

중요한 정보는 엄지손가락의 하단 가장자리가 끝까지 스크롤했을 때 스크롤 가능한 요소의 하단 가장자리와 정렬 감소합니다. 다시 말해, 스크롤 한 번으로 scroller.scrollHeight - scroller.height픽셀입니다. 엄지손가락으로 scroller.height - thumb.height로 번역됨 스크롤러의 모든 픽셀에 대해 엄지손가락으로 픽셀의 일부를 움직이고자 합니다.

인수가 스크롤러 도트 높이에서 스크롤러 위의 엄지 도트 높이를 뺀 값과 같습니다.
  도트 스크롤 높이에서 스크롤러 도트 높이를 뺀 값입니다.

이것이 바로 Google의 배율입니다. 이제 배율을 이미 패럴랙스 스크롤에서 했던 것과 같습니다. 도움말을 참조하세요. 출처: 관련 섹션을 참고하세요. 배율은 p/(p − z)와 같습니다. z의 방정식을 z 축을 따라 엄지손가락을 얼마나 이동시켜야 하는지를 알 수 있습니다. 그러나 유지 w 좌표가 일치하기 때문에 z를 따라 추가 -2px 또한 요소의 변환이 적용된 것을 볼 수 있습니다. 즉, 특수 행렬 앞의 모든 번역은 그러나 특수 행렬 다음에 오는 모든 번역은 반전됩니다. 시작 코드화

<script>
    // ... code from above...
    const factor =
    (scrollerHeight - thumbHeight)/(scroller.scrollHeight - scrollerHeight);
    thumb.style.transform = `
    matrix3d(
        1, 0, 0, 0,
        0, 1, 0, 0,
        0, 0, 1, 0,
        0, 0, 0, -1
    )
    scale(${1/factor})
    translateZ(${1 - 1/factor}px)
    translateZ(-2px)
    `;
</script>

Google은 스크롤바를 말합니다. 이 요소는 원하는 대로 스타일을 지정할 수 있는 DOM 요소일 뿐입니다. 한 가지 중요한 점은 엄지손가락으로 손가락이 닿도록 하는 것이 많은 사용자가 이런 식으로 스크롤바와 상호작용하는 데 익숙하기 때문입니다. 이 블로그 게시물을 더 오래 작성하지 않도록, 확인할 수 있습니다. 자세한 내용은 라이브러리 코드 를 참조하세요.

iOS의 경우

아, 내 오랜 친구 iOS Safari. 패럴랙스 스크롤과 마찬가지로 확인할 수 있습니다 요소를 스크롤하므로 -webkit-overflow-scrolling: touch 그러나 이로 인해 3D 평면화가 발생하고 전체 스크롤 효과가 작동하지 않습니다. 이 문제는 패럴랙스 스크롤러 iOS Safari를 감지하고 position: sticky를 사용하여 문제를 해결합니다. 여기서도 똑같이 하겠습니다. 자세한 내용은 시차 기사 기억을 상기할 수 있습니다.

브라우저 스크롤바는 어떨까요?

일부 시스템에서는 영구적인 네이티브 스크롤바를 처리해야 합니다. 이전에는 스크롤바를 숨길 수 없었습니다( 비표준 의사 선택기)를 사용합니다. 그래서 그것을 숨기려면 (수학이 필요 없는) 몇 가지 해커에 의지해야 합니다. 우리는 overflow-x: hidden를 사용하여 컨테이너에서 스크롤 요소를 가리키고 스크롤 요소의 너비가 컨테이너보다 넓습니다. 브라우저의 네이티브 스크롤바는 보이지 않습니다

Fin

지금까지 종합해보면, 이제 프레임에 맞는 완벽한 커스텀 이미지를 만들 수 있습니다. 인코더-디코더에 있는 냥 고양이 데모

냥 고양이가 보이지 않는다면 버그를 발견하여 신고한 버그와 ('냥 고양이'를 표시하려면 엄지손가락 클릭) Chrome은 불필요한 작업을 피하는 데 매우 효과적입니다 그림을 그리거나 애니메이션을 적용하는 것과 같은 효과를 볼 수 있습니다. 나쁜 소식은 우리의 행렬 반향으로 인해 Chrome은 냥 고양이 GIF가 실제로 화면에 벗어났다고 생각하게 됩니다. 이 문제가 곧 해결되기를 바랍니다.

자, 다 됐습니다. 수고 많았어요. 전체 책을 읽어주셔서 박수를 보냅니다 있습니다. 이것은 진짜 속임수로 작동하게 되고 아마도 그만한 가치가 없을 것입니다. 맞춤설정된 스크롤바가 환경의 필수적인 부분인 경우는 예외입니다. 하지만 정말 다행이라고 생각합니다. 한 가지 중요한 게 CSS 측에서 해야 할 작업이 있음을 보여줍니다. 하지만 걱정하지 마세요. 앞으로는 HoudiniAnimationWorklet은 훨씬 더 쉽게 프레임에 잘 맞는 스크롤 연결 효과를 얻을 수 있습니다.