• JavaScript手工编写滚动条组件


    0 前言

      上周的一个练习,由于没来得及编写笔记,这里补充一下~

      虽然CSS3中提供了overflow:scroll; 来实现滚动条,但是这里可以使用原生JS来编写一个,以达到练习组件编写的效果。

      练习要点包括以下几点:

    • dom对象操作

    • 鼠标点击事件

    • 鼠标滑轮事件

    • 滚动条显示比例设计逻辑

      感兴趣的朋友欢迎点击屏幕左上角的小猫咪进入我的github,或猛戳这里下载源码~

    1 需求分析

    • 需要支持鼠标点击响应:点击上下箭头时,能上下滚动文章内容。

    • 需要支持鼠标拖拽响应:拖拽滚动条并上下滚动文章内容。此外应支持拖拽过快的状况,即鼠标超出滚动条区域范围依然能够上下滚动。

    • 边界限制:当滚动条到达范围的边界的时候,必须能够停止,不得超出屏幕显示范围。

    • 对于不在显示区域的文字部分需要隐藏。

    • 这里我们只关心其实现逻辑,而不关注其展现形式上的美化。

      效果如下:

        

    2 实现细节

    2-1 HTML结构

      结构上非常简单,两栏布局,内容文本区和滚动条区。

      滚动条区采用ul-li结构实现,分别加载图片和使用一个div。

      代码如下

     1 <!DOCTYPE html>
     2 <html lang="en">
     3 <head>
     4     <meta charset="UTF-8">
     5     <meta name="viewport" content="width=device-width, initial-scale=1.0">
     6     <meta http-equiv="X-UA-Compatible" content="ie=edge">
     7     <title>Scroll</title>
     8     <link rel="stylesheet" href="index.css">
     9 </head>
    10 <body>
    11     <div class="container">
    12         <div class="content">
    13             区域文本内容。      
    14         </div>
    15         <ul class="scroll">
    16             <li class="up"><img id="up_img" src="img/up.png" alt=""></li>
    17             <li class="duration"><div class="bar"></div></li>
    18             <li class="down"><img id="down_img" src="img/down.png" alt=""></li>
    19         </ul>
    20     </div>
    21     <script src="index.js"></script>
    22 </body>
    23 </html>

    2-2 CSS样式

     几个要点:

    • 在外层container上使用overflow:hidden隐藏超出区域范围的文本内容。

    • 选择器的使用cursor:-webkit-grab;设置一个鼠标移入的形状控制。

      • 若根据父子选择器的选择逻辑,则需要在父子标签之间添加空格,即表示选中子元素。
      • 若不添加空格,则表示的是选中同一级标签,通过多个标签来缩小选中范围。

      代码如下:

     1 *{
     2     margin: 0;
     3     padding: 0;
     4     list-style: none;
     5 }
     6 body{
     7     overflow: hidden;
     8 }
     9 .container{
    10     width: 380px;
    11     height: 400px;
    12     margin: 100px auto;
    13     border: 1px solid red;
    14     position: relative;
    15     overflow: hidden;
    16 }
    17 .container .content{
    18     width: 350px;
    19     position: absolute;
    20     left: 0;
    21     top: 0;
    22 }
    23 .container .scroll{
    24     width: 28px;
    25     height: 398px;
    26     position: absolute;
    27     border: 1px solid black;
    28     right: 0;
    29     top: 0;
    30 }
    31 .container .scroll li{
    32     width: 100%;
    33 }
    34 .container .scroll li.up{
    35     height: 28px;
    36     cursor: pointer;
    37 }
    38 .container .scroll li.down{
    39     height: 28px;
    40     cursor: pointer;
    41 }
    42 .container .scroll li img{
    43     height: 100%;
    44     width: 100%;
    45 }
    46 .container .scroll li.duration{
    47     height: 342px;
    48     width: 100%;
    49     position: relative;
    50 }
    51 .container .scroll .duration .bar{
    52     position: absolute;
    53     right: 0;
    54     top: 0;
    55     height: 27px;
    56     width: 100%;
    57     background-color: red;
    58     cursor: -webkit-grab;
    59 }

    2-3 Javascript实现

    2-3-1 模块调用入口

      在html文档引入了JS代码以后,需要一个函数调用的入口,这里我采用的是以init()函数作为主入口。最外层是一个立即执行函数,保证这一组件内部的变量只可以被内部成员访问,避免变量污染的发生。

      在入口函数init()中调用了三个函数,分别是控制拖拽的scrollDrage(ele_bar);控制点击事件的scrollClick(ele_bar);和控制滚轮事件的scrollWheel(ele_container , ele_bar);

    (function(){
        function init(){
            scrollDrage(ele_bar);
            scrollClick(ele_bar);
            scrollWheel(ele_container , ele_bar);
        }
        init();
    });

    2-3-2 获取dom对象

      由于需要对dom元素进行操作,因此首先需要先获取到与滚轮操作有关的对象:页面容器container、文本content、滚动条中间滑动部分duration、滑块bar

      可以在初始时设置滑块bar的高度,这里采用相似变换的原理,计算其值并进行赋值即可。

      滑块高度的计算公式为:

    Math.floor((container.offsetHeight / content.offsetHeight) * duration.offsetHeight)

      offsetHeight属性为获取还dom元素的height属性。

    1     var ele_container = document.getElementsByClassName('container')[0];
    2     var ele_content = document.getElementsByClassName('content')[0];
    3     var ele_duration = document.getElementsByClassName('duration')[0];
    4     var ele_bar = document.getElementsByClassName('bar')[0];
    5     var percentH = Math.floor((ele_container.offsetHeight / ele_content.offsetHeight) * ele_duration.offsetHeight);
    6     ele_bar.style.height = percentH + 'px

    2-3-3 文本移动控制

      文本移动控制的方法同样用到了相似变换的原理,并根据计算的高度,修改文本context的top值。

      传入的参数为滑块bar对象,在鼠标事件中调用该方法。

      注意:由于向下查看内容时,文本是向上移动的,因此这个top值需要设置为负值

    1     function contentMove(item){
    2         var percentHeight = item.offsetTop / (ele_duration.offsetHeight - item.offsetHeight);
    3         var moveH = percentHeight * Math.floor(ele_content.offsetHeight - ele_container.offsetHeight);
    4         ele_content.style.top = -moveH + 'px';
    5     }

    2-3-4 鼠标拖拽事件

    在这一部分有以下几点注意:

    • e = e||window.event 一般在获取事件对象e的时候要如此给一个初值,大体上是为了给firefox做一个兼容,细节上的讨论可以参考这篇文章。 

    • e.preventDefault()这里是为了阻止事件的默认动作,比如提交表单等。

    • 通过e.pageY来获取点击位置的y坐标。关于使用事件对象e来获取点击位置坐标的方法有screen,offset,page和client四种。

      • event.clientX、event.clientY

        鼠标相对于浏览器窗口可视区域的X,Y坐标(窗口坐标),可视区域不包括工具栏和滚动条。IE事件和标准事件都定义了这2个属性

      • event.pageX、event.pageY

        类似于event.clientX、event.clientY,但它们使用的是文档坐标而非窗口坐标。这2个属性不是标准属性,但得到了广泛支持。IE事件中没有这2个属性

      • event.offsetX、event.offsetY

        鼠标相对于事件源元素(srcElement)的X,Y坐标,只有IE事件有这2个属性,标准事件没有对应的属性

      • event.screenX、event.screenY

        鼠标相对于用户显示器屏幕左上角的X,Y坐标。标准事件和IE事件都定义了这2个属性

      • 用一张图可以表示成下图所示

              

    • 鼠标事件绑定的对象:需要确定鼠标事件的作用范围,因此必须明确鼠标事件所绑定的对象。对于mousedown事件,就需要绑定到bar滑块对象上;而mousemove和mouseup事件,则需要绑定在document对象上(即整个窗口)。这是为了避免鼠标移动过快,而导致鼠标移出滑块区域导致鼠标事件响应失效。

    • 解除绑定:当mouseup时,需要解除mousemove的绑定,只需为mousemove赋值为null即可。

    • 获取当前对象相对其父元素的top值:offsetTop,进而根据此值,来更新鼠标拖拽的滑块位置。

     1     function scrollDrage(item){
     2         item.onmousedown = function(e){
     3             e = e || window.event;
     4             e.preventDefault();
     5             var e_y = e.pageY;
     6             document.onmousemove = function(e){
     7                 var chay = e.pageY - e_y;
     8                 item.style.top = item.offsetTop + chay + 'px';
     9                 e_y = e.pageY;
    10 
    11                 if(item.offsetTop <= 0){
    12                     item.style.top = 0 + 'px';
    13                 }
    14                 else if(item.offsetTop + item.offsetHeight >= ele_duration.offsetHeight){
    15                     item.style.top = ele_duration.offsetHeight - item.offsetHeight + 'px';
    16                 }
    17                 contentMove(item);
    18             }  
    19         }
    20         document.onmouseup = function(){
    21             document.onmousemove = null;
    22         }
    23     }

    2-3-5 鼠标点击上下箭头移动事件

    这里的要点有:

    • e.target:
      • target定义:

          target 事件属性可返回事件的目标节点(触发该事件的节点),如生成事件的元素、文档或窗口。

      • 语法:

          event.target

          event.target.nodeName    //获取事件触发元素标签name(li,p...)

          event.target.id       //获取事件触发元素id

          event.target.className  //获取事件触发元素classname

          event.target.innerHTML  //获取事件触发元素的内容(li)

          等等。

      • 这里我用e.target.id来判断是上翻还是下翻页面。
    • 边界判断:

        这里仍要进行边界判断,比如offsetTop <= 0的情况和bar.offsetTop + bar.offsetHeight >= scroll.offsetHeight的情况需要直接赋予边界值,保证滑块不会从临界值超出范围。

     1     function scrollClick(item){
     2         var ele_scroll = document.getElementsByClassName('scroll')[0];
     3         var speed = 5;
     4         ele_scroll.onclick = function(e){
     5             // console.log(e.target.id);
     6             if(e.target.id == 'up_img'){
     7                 item.style.top = item.offsetTop - speed + 'px';
     8                 if(item.offsetTop <= 0){
     9                     item.style.top = 0 + 'px';
    10                 }
    11             }
    12             else if(e.target.id == 'down_img'){
    13                 item.style.top = item.offsetTop + speed + 'px';
    14                 if(item.offsetTop + item.offsetHeight >= ele_duration.offsetHeight){
    15                     item.style.top = ele_duration.offsetHeight - item.offsetHeight + 'px';
    16                 }
    17             }
    18             contentMove(item);
    19         }
    20     }

    2-3-6 鼠标滚轮控制

    • 通过onmousewheel绑定滚轮事件,e.wheelDelta表示滚轮方向,为正向上,负向下。

    • 同样要施加一个边界判断。其他流程与2-3-5完全类似。

     1     function scrollWheel(container , item){
     2         var speed = 5;
     3         container.onmousewheel = function(e){
     4             // console.log(e.wheelDelta); //上下滑动滑轮
     5             if(e.wheelDelta > 0){ //向上滑动滑轮
     6                 item.style.top = item.offsetTop - speed + 'px';
     7                 if(item.offsetTop <= 0){
     8                     item.style.top = 0 + 'px';
     9                 }
    10             }
    11             else{
    12                 item.style.top = item.offsetTop + speed + 'px';
    13                 if(item.offsetTop + item.offsetHeight >= ele_duration.offsetHeight){
    14                     item.style.top = ele_duration.offsetHeight - item.offsetHeight + 'px';
    15                 }
    16             }
    17             contentMove(item);
    18         }
    19     }

    3 总结

      通过练习基础组件的写法,达到基础巩固的目的。但还是不要忘记使用overflow:scroll;这种自动实现滚动条的方法。

  • 相关阅读:
    (二)Spring Security 入门体验之——用户密码配置
    (一)Spring Security 入门体验
    (十二)权限之RBAC
    (十一)jwt详解
    (十)登录拦截器之前后端
    (九)优化登录页面
    (八)前后端整合之跨域问题
    SecureCRT 8.1.4 破解教程
    centOS配置网络(6.8)securCRT连接虚拟机
    二叉树的下一个节点(给定一棵二叉树的其中一个节点,请找出中序遍历序列的下一个节点)
  • 原文地址:https://www.cnblogs.com/chrischen98/p/10765150.html
Copyright © 2020-2023  润新知