Детальное управление прокруткой полосы прокрутки элемента DOM

HTML
Детальное управление прокруткой полосы прокрутки элемента DOM

предисловие

Я не знаю, сталкивались ли вы когда-нибудь с такой потребностью, в определенномdomДобавьте к элементу новый дочерний элемент, а затем потребуйте, чтобы, если вновь добавленный новый элемент превышал область действия контейнера, нам нужно было автоматически выполнить прокрутку до положения вновь добавленного дочернего элемента, как показано на следующем рисунке:

Тогда давайте узнаем некоторыеdomТочки знаний, связанные с прокруткой элементов, при реализации эффекта изображения выше и некоторых других функций, связанных с прокруткой.

Свойства и методы DOM, которые вам нужно знать

scrollTop, clientHeight и scrollHeight

scrollTopАтрибут является верхним значением, которое описывает содержимое элемента контейнера и элемента контейнера (viewport) в верхней части окна просмотраtopРазница между значениями, которая представляет собой часть контейнера, выдвигающуюся за пределы окна просмотра контейнера. Состоянием прокрутки можно управлять, изменяя это свойство.

clientHeightэто высота контейнераdomАтрибуты.

scrollHeightЭто описание содержимого высоты контейнераdomАтрибуты.

Взаимосвязь трех атрибутов показана на следующем рисунке:

关系图

getBoundingClientRect()

Этот метод используется для получения некоторых геометрических свойств, необходимых для размещения элементов, таких какleft,right,top,bottom,height,widthЖдать.

srollBy(x,y)

domконтейнерscrollToМетод можно использовать для прямого управления полосой прокрутки для прокрутки на указанное расстояние. Этот метод удобен, когда требуется прокрутка до указанного элемента.

srollTo(x,y)

domконтейнерscrollToможно использовать для прямого управления полосой прокрутки для прокрутки до указанной позиции. Этот метод удобно использовать при управлении полосой прокрутки для прокрутки вверх или вниз.

Реализовать управление прокруткой

Подготовить

Давайте подготовим одинhtml

<!DOCTYPE html>
<html>
    <head>
       <title>滚动条设置详解</title>
       <style>
          #scroll_container{
              height: 500px;
              width: 500px;
              overflow-y: scroll;
              padding: 50px;
              box-sizing: border-box;
          }
          .scroll_item{
              height: 200px;
              width: 500px;
              margin-top: 20px;
              background-color: aquamarine;
              display: flex;
              align-items: center;
              justify-content: center;
          }
       </style>
    </head>
    <body>
       <div  id="scroll_container">
           <div  id="scroll_container">
               <div id="item1" class="scroll_item">
                   <span>1</span>
               </div>
               <div id="item2" class="scroll_item">
                    <span>2</span>
                </div>
                <div id="item3" class="scroll_item">
                    <span>3</span>
                </div>
                <div id="item4" class="scroll_item">
                    <span>4</span>
                </div>
                <div id="item5" class="scroll_item">
                    <span>5</span>
                </div> 
           </div>
           <button onclick="addItem()">添加一个元素</button>
       </div>
    </body>
    <script>
        let container=document.getElementById("scroll_container");
        let index=5;
        //添加一个元素
        function addItem(){
            index+=1;
            let item=`<div id="${'item'+index}" class="scroll_item">
                            <span>${index}</span>
                        </div>`;
            container.innerHTML+=item;  
            setTimeout(()=>{
                scrollToIndex();
            })
         }
    </script>
</html>

Приведенный выше код содержит прокручиваемую область и может добавлять элементы в область прокрутки или прокручивать до указанной позиции элемента.Общий эффект следующий.

Реализовано с помощью scrollTop

Базовая реализация

было объяснено раньшеscrollTopЭто означает, что мы можем изменить элемент контейнера, изменивscrollTopзначение для управления прокруткой полосы прокрутки.scrollTopЧем больше значение , полоса прокрутки находится относительно исходного состояния (scrollTopравно 0) тем больше расстояние прокрутки.

понялscrollTopзначение, мы можем использоватьscrollTopЧтобы реализовать управление полосой прокрутки, мы сначала реализуем реализацию прокрутки вниз и добавимscrollToBottom()Методы:

function scrollToBottom(){
    let y=container.scrollHeight-container.clientHeight;
    container.scrollTop=y;
}

Соответственно, если мы хотим прокрутить вверх, нам нужно только установитьscrollTopМожет быть 0:

function scrollToTop(){
    container.scrollTop=0;
}

комбинироватьgetBoundingClientRect()метод Мы также можем легко перейти к указанному элементу, гдеgetBoundingClientRect().topУказывает расстояние от верха текущего элемента элемента до верха области просмотра:

function scrollToElement(el){
     container.scrollTop+=el.getBoundingClientRect().top;
}

Добавить анимацию

  • прокрутить вниз

Но прокрутка приведенного выше кода слишком тупая, мы можем добавить к нему анимационные эффекты, мы можем воспользоваться помощьюsetInterval()Сделай это. Проанализируйте процесс реализации анимационного эффекта.Реализация анимации — это не что иное, как завершение изменения переменной в течение определенного периода времени, поэтому нам сначала нужно знать две переменные, переменную (scrollTop) смещение и время изменения, где смещениеscrollTopОкончательное значение минус исходное значение, а продолжительность изменения обычно задается как параметр, который можно изменить. Зная описанный выше процесс, давайте в качестве примера возьмем прокрутку вниз:

//首先编写一个scrollToBottom函数
function scrollToBottom(el){
              if(!el){
                  el=container;
              }
              //原始值
              let startTop=el.scrollTop;
              //最终值
              let endTop=el.scrollHeight-el.clientHeight;
              //生成一个动画控制函数
              let scrollAnimationFn=doAnimation(startTop,endTop,300,el);
              //执行动画,每10ms执行一次
              let interval=setInterval(()=>{
                scrollAnimationFn(interval)
              },10)
 }
/**
 * @description: 一个生成动画控制函数的工厂函数(使用闭包)
 * @param {
    startValue:变量原始值
    endValue:变量最终值
    duration:动画时长
    el:执行滚动动画的元素
 } 
 * @return: null
 */
function doAnimation(startValue,endValue,duration,el){
              //使用闭包保存变量dy和step(每次动画滚动的距离)
              let dy=0;
              let step=(endValue-startValue)/(duration/10);
              //返回动画控制函数
              return function(interval){
                  dy+=step;
                  if(dy>=endValue-startValue){
                      clearInterval(interval);
                  }
                  el.scrollTop+=step;
              }
 }

Измените функцию addItem, чтобы добавить прокрутку к нижней анимации:

function addItem(){
            index+=1;
            let item=`<div id="${'item'+index}" class="scroll_item">
                            <span>${index}</span>
                        </div>`;
            container.innerHTML+=item;  
            setTimeout(()=>{
                // scrollToIndex();
                scrollToBottom(container);
            })
           
 }

Затем добавьте кнопку в html, которая прокручивается вниз:

<button onclick="scrollToBottom()">滚动到底部</button>

  • пролистать наверх

По вышеуказанному способу можно реализовать и обычный скролл вверх с анимацией:

//编写一个scrollToTop函数
function scrollToTop(el){
              if(!el){
                  el=container;
              }
              //原始值
              let startTop=el.scrollTop;
              //最终值
              let endTop=0;
              //生成一个动画控制函数
              let scrollAnimationFn=doAnimation(startTop,endTop,300,el);
              //执行动画,每10ms执行一次
              let interval=setInterval(()=>{
                scrollAnimationFn(interval)
              },10)
 }

Чтобы адаптироваться к прокрутке вниз, нам нужно изменить время остановки анимации.doAnimation()Функция выглядит следующим образом:

function doAnimation(startValue,endValue,duration,el){
              //使用闭包保存变量dy和step(每次动画滚动的距离)
              let dy=0;
              let step=(endValue-startValue)/(duration/10);
              return function(interval){
                  dy+=step;
                  //这里改成使用绝对值判断
                  if(Math.abs(dy)>=Math.abs(endValue-startValue)){
                      clearInterval(interval);
                  }
                  el.scrollTop+=step;
              }
 }

Наконец мы даемhtmlДобавьте кнопку прокрутки вниз:

<button onclick="scrollToTop()">滚动到顶部</button>

Эффект следующий:

  • прокрутить до указанного элемента

Сначала добавьте необходимые кнопки и поля ввода в элемент html:

<input type="number" placeholder="请输入要滚动到的元素index" style="width: 200px;"/>
<button onclick="scrollToElement()">滚动到指定元素</button>

Добавьте функцию выполнения анимации, которая прокручивает указанный элемент:

function scrollToElement(containerEl,el){
            if(!containerEl){
                //父元素
                containerEl=container;
            }
            if(!el){
                //获取到要滚动到的元素
                let input=document.getElementsByTagName('input')[0];
                let id='item'+input.value;
                if(!input.value){
                    id='item'+index;
                }
                el=document.getElementById(id);
            }
            let startTop=containerEl.scrollTop;
            let endTop=startTop+el.getBoundingClientRect().top;
            let scrollAnimationFn=doAnimation(startTop,endTop,300,containerEl);
            let interval=setInterval(()=>{
                scrollAnimationFn(interval)
            },10)
}

Эффект следующий:

Реализовано с помощью scrollTo()

scrollTo(x,y)как использовать иscrollTopИспользование атрибутов в основном такое же, родительский элементscrollTo()Метод может управлять полосой прокрутки для прокрутки до указанной позиции, что фактически эквивалентно установкеscrollTopценность . Чтобы проиллюстрировать на примере:

//这里以y轴滚动为例
element.scrollTo(0,y);
element.scrollTop=y;
//上面两句的效果相同。

Итак, используйтеscrollTo()Метод управления полосой прокрутки в основном такой же, как и при использовании scrollTop, нам нужно только сделать простую модификациюdoAnimation()функция, код выглядит следующим образом:

function doAnimation(startValue,endValue,duration,el){
              //使用闭包保存变量dy和step(每次动画滚动的距离)
              let dy=0;
              let step=(endValue-startValue)/(duration/10);
              return function(interval){
                  dy+=step;
                  if(Math.abs(dy)>=Math.abs(endValue-startValue)){
                      clearInterval(interval);
                  }
                  //el.scrollTop+=step;//这行代码修改为如下
                  el.scrollTo(0,el.scrollTop+step);
              }
}

Эффект исполнения и использованиеscrollTopдобиться согласованности.

Реализовано с помощью scrollBy()

Базовая реализация

Мы также можем использоватьscrollBy(x,y)Реализуйте управление полосой прокрутки, как описано выше,scrollBy()Метод заключается в управлении полосой прокрутки для прокрутки на указанное расстояние (а не положение). Использование scrollBy() может легко реализовать требование прокрутки до указанного элемента Код выглядит следующим образом:

function scrollToElement(containerEl,el){
    //因为getBoundingClientRect().top即为子元素顶部距离父元素顶部的距离,所以这个值就是子元素相对于父元素的偏移量,我们传入这个值到scrollBy中,即滚动到指定元素
    containerEl.scrollBy(0,el.getBoundingClientRect().top);
}

Прокрутите вниз:

function scrollToBottom(containerEl){
    let dy=containerEl.scrollHeight-containerEl.clientHeight;
    containerEl.scrollBy(0,dy);
}

пролистать наверх

function scrollToTop(containerEl){
    let dy=-(containerEl.scrollHeight-containerEl.clientHeight);
    containerEl.scrollBy(0,dy);
}

Добавить анимацию

Здесь мы модифицируем функцию генерации анимации, потому что здесь мыscrollBy()Параметр является смещением переменной, поэтому внесите следующие изменения:

        function scrollToBottom(containerEl){
              if(!containerEl){
                containerEl=container;
              }
              //dy即为偏移量
              let dy=containerEl.scrollHeight-containerEl.clientHeight;
              let scrollAnimationFn=doAnimation(dy,300,containerEl);
              let interval=setInterval(()=>{
                scrollAnimationFn(interval)
              },10)
         }
         function scrollToTop(containerEl){
              if(!containerEl){
                containerEl=container;
              }
              //dy即为偏移量
              let dy=-(containerEl.scrollHeight-containerEl.clientHeight);
              let scrollAnimationFn=doAnimation(dy,300,containerEl);
              let interval=setInterval(()=>{
                scrollAnimationFn(interval)
              },10)
         }
         function scrollToElement(containerEl,el){
            if(!containerEl){
                containerEl=container;
            }
            if(!el){
                let input=document.getElementsByTagName('input')[0];
                let id='item'+input.value;
                if(!input.value){
                    id='item'+index;
                }
                el=document.getElementById(id);
            }
           //dy即为偏移量
            let dy=el.getBoundingClientRect().top;
            let scrollAnimationFn=doAnimation(dy,300,containerEl);
            let interval=setInterval(()=>{
                scrollAnimationFn(interval)
            },10)
         }
         /**
          * @description: 
          * @param {type} 
          * @return: 
          */
         function doAnimation(dy,duration,el){
              //使用闭包保存变量exe_dy和step等变量(每次动画滚动的距离)
              let exe_dy=0;//已经执行的偏移量
              let step=dy/(duration/10);
              return function(interval){
                  exe_dy+=step;
                  if(Math.abs(exe_dy)>=Math.abs(dy)){
                      clearInterval(interval);
                  }
                  el.scrollBy(0,step);
              }
         }

Эффект исполнения и использованиеscrollTopдобиться согласованности.

наконец

Вышеупомянутое 👆 является моим подробным описанием и объяснением управления полосой прокрутки dom, а также некоторыми основными методами использования. Пожалуйста, поправьте меня, если я ошибаюсь.

Если вы думаете, что можете, пожалуйста, поставьте лайк о( ̄▽ ̄)d~