javascript自定义滚动条实现代码


Posted in Javascript onApril 20, 2020

在工作中经常会遇到内容会超出固定的一个范围,超出的内容一般会使用到滚动条来滚动显示。

但是用浏览器默认的滚动条经常被产品经理鄙视,可是用css却改变不了滚动条的样式,还好,有万能的js ^_^~~

网上有各种各样的插件,但最顺手的还是自己写的,还可以一边撸一边当学习,自己动手丰衣足食 (*^__^*)

其中这三个问题深深地困扰我:

  • 1、滚动条高度
  • 2、每次点击向上、向下按钮的时候滚动条应该移动多少距离
  • 3、每拖动1px滚动条,页面需要移动多少?

整个的框架大概是长这样的:

javascript自定义滚动条实现代码

先来看看第一个问题。

由于目前已经知道内容区域的高度和内容可视高度以及滚动条可滚动区域的高度了,由于内容区域与滚动条每次移动的距离都是成正比的,所以第一个问题很好解决:

滚动条可移动范围 / 滚动条高度 = 内容高度 / 内容可视高度

每次点击按钮的时候滚动条应该移动多少距离?

这里我是给参数distance设置一个值来决定每次点按钮的时候,内容区域应该滚动多少的距离。改变这个值可以改变内容区域滚动的快慢,如果有更好的处理方法和建议记得告诉我喔~

目前,内容区域每次滚动的距离是知道了,剩下的是计算滚动条相对于应该移动多少距离?

滚动条可移动范围 /滚动条每次移动距离 = 内容区域高度 / 内容区域每次移动多少距离

效果如下:

javascript自定义滚动条实现代码

这里还有一个问题,就是同时还得区分是单次点击还是长按。

所以得判断一下从按下按钮到松开时候的时长,目前设置为<100ms为单次点击,否则为长按:

javascript自定义滚动条实现代码

拖动滚动条的时候,每移动1px滚动条,内容区域需要移动多少?

先知道每1PX的距离占滚动条可移动范围的百分之几,再用内容区域高度除以所得的这个百分比,就得出滚动条每移动1px内容区域相对滚动多少距离了。

内容区域滚动的距离 = 内容区域高度 / (滚动条滚动区域 / 1)

javascript自定义滚动条实现代码

demo完整代码如下:

注意:因为用的是seajs写的,所以稍微留意下文件的加载情况啦

css:

.wapper{scrollbar-3dlight-color:#000; position:relative; height:302px;width:300px;overflow:hidden;margin:0 auto;line-height:40px;text-align:center;}
 .area{background-color:#E2E2EF;width:100%; position:absolute;top:0px;left:0px;}
 .bar{position:absolute;top:0px;right:0px; height:100%;width:1rem;background-color:#ccc;}
 .scroll,.middle,.forward,.backward{display:block;cursor:pointer;position:absolute;right:0px;width:100%;}
 .forward,.backward{height:16px;background-color:#6868B1;}
 .middle{background-color:rgba(255, 255, 255, 0.22);top:16px;cursor:auto;}
 .scroll{position:absolute;top:0px;background-color:#C2C2E9;}
 .forward{top:0px;}
 .backward{bottom:0px;}

html:

<div class="wapper">
 <div class="area">
 <p>1、this is content</p>
 <p>2、this is content</p>
 <p>3、this is content</p>
 <p>4、this is content</p>
 <p>5、this is content</p>
 <p>6、this is content</p>
 <p>7、this is content</p>
 <p>8、this is content</p>
 <p>9、this is content</p>
 <p>10、this is content</p>
 <p>11、this is content</p>
 </div>
 <div class="bar">
 <span class="forward"></span>
 <span class="middle"><em class="scroll"></em></span>
 <span class="backward"></span>
 </div>
</div>

<script type="text/javascript" src="../../lib/seajs/sea.js"></script>
<script type="text/javascript" src="../../lib/base/1.0.x/base.js"></script>
<script type="text/javascript">
seajs.use(['lib/jquery/1.11.x/index.js', '_example/simulationScroll/simulationScroll.js'], function($, scroll) {
 scroll.init({
 wapper: $('.wapper'), 
 distance: 10,
 });
});

js:

define(function(require, exports, module) {

 'use strict';

 var $ = require('lib/jquery/1.11.x/index.js');

 var parameter = null;

 //检测设备类型
 var startWhen, endWhen, moveWhen;
 var u = navigator.userAgent; 

 if ( u.match(/\b(Windows\sNT|Macintosh)\b/) ) {
 // 鼠标
 startWhen = 'mousedown';
 endWhen = 'mouseup';
 moveWhen = 'mousemove';
 } else {
 // 触摸屏
 startWhen = 'touchstart';
 endWhen = 'touchend';
 moveWhen = 'touchmove';
 }

 var simulation = {

 _mousedownTimer: 0,
 _setintervalId: 0,
 _longClick: false, //是否长点击
 _turnOf: null, //滚动方向

 init: function(options) {

  var t = this;

  t._scroll = $('.scroll'); //滚动条

  t._wapper = options.wapper.find('.area'); //内容区域
  t._distance = options.distance; //点击上下按钮页面每次滚动的距离

  var forward = $('.forward'),
  middle = $('.middle'),
  backward = $('.backward');

  parameter = {
  view: t._wapper.parent().innerHeight(), //视图高度
  page: t._wapper.height(), //内容高度
  barArea: 0, //滚动条可移动范围
  scrollHeight: 0, //滚动条的高度
  scrollDistance: 0 //滚动条每次滚动的距离
  };

  //初始化滚动条
  if (parameter.page > parameter.view) {

  //滚动条可移动范围
  middle.height( parameter.view - forward.height() * 2);

  parameter.barArea = middle.height();

  //滚动条高度 = 滚动条可滚动范围 / (页面高度 / 可视高度)的百分比
  parameter.scrollHeight = parameter.barArea / (parameter.page / parameter.view) ;
  t._scroll.height(parameter.scrollHeight);

  //滚动条每次滚动的距离 = 滚动条可移动范围 * 页面每次滚动的百分比
  parameter.scrollDistance = parameter.barArea / (parameter.page / t._distance) ;

  //拖动滚动条
  t.liveEvent();

  //点击向前按钮,如果按下鼠标到松开鼠标的时长<100ms,则为单次点击
  forward.bind(startWhen, function(e){

   t._turnOf = 'forward';

   t.longPress(e, t.direction );

  }).bind(endWhen, function(e) { 

   t.mouseupFun(e, t.direction);

   t._turnOf = null;

  });

  //点击向后按钮
  backward.bind(startWhen, function(e){

   t.longPress(e, t.direction );

  }).bind(endWhen, function(e){

   t.mouseupFun(e, t.direction );

  });

  //注册鼠标滚动事件
  // FF
  if(document.addEventListener){
   document.addEventListener('DOMMouseScroll',t.mouseRuning,false);
  }

  //其它浏览器
  document.onmousewheel = t.mouseRuning;
  }
 },

 //鼠标滚动
 mouseRuning: function(e) {

  var t = simulation;

  e = e || window.event;

  //ie、FF
  if (e.detail) {
  if (e.detail < 0) {

   t._turnOf = 'forward';

   t.direction ();

  } else{

   t._turnOf = null;
   t.direction ();
  }
  // chrome
  } else if(e.wheelDelta) {

  if (e.wheelDelta > 0) {

   t._turnOf = 'forward';

   t.direction ();

  } else{

   t._turnOf = null;
   t.direction ();

  }
  } 
 },

 //判断是否长点击
 longPress: function(e, moveFun ) {

  var t = this;

  if ( u.match(/\b(Windows\sNT|Macintosh)\b/) ) {
  e = e || window.event;

  // 限制为鼠标左键点击才触发
  if (/^mouse/.test(e.type) && e.which !== 1) {
   return;
  }
  }

  t._setintervalId = setInterval(function(){

  t._mousedownTimer += 10;

  if( t._mousedownTimer >= 100 ){

   moveFun();
  }

  },20);
 },

 mouseupFun: function(e, moveFun) {
  
  var t = this;

  if ( u.match(/\b(Windows\sNT|Macintosh)\b/) ) {
  e = e || window.event;

  // 限制为鼠标左键点击才触发
  if (/^mouse/.test(e.type) && e.which !== 1) {
   return;
  }
  }

  clearTimeout(t._setintervalId);

  if( t._mousedownTimer < 100 ) {

  moveFun();
  }

  t._mousedownTimer = 0;
 },

 direction:function() {
  var t = simulation,
  barTop = t._scroll.position().top,
  pageTop = t._wapper.position().top,
  moveDistance = {};

  if ( t._turnOf === 'forward') {

   //页面到顶,不执行任何操作
   if (barTop == 0) {
   return;
   }

   moveDistance = {
   page: pageTop + t._distance,
   bar: barTop - parameter.scrollDistance
   }

   //如果滚动条距离顶部的距离少 < 每次滚动的距离,或者已经滚动到顶部,则不再滚动
   if(barTop < parameter.scrollDistance || barTop <= 0){

   moveDistance = {
    page: 0,
    bar: 0
   }
   }

  } else {

   //页面到底,不执行任何操作
   if (barTop == parameter.barArea - parameter.scrollHeight){
   return;
   }

   moveDistance = {
   page: pageTop - t._distance,
   bar: barTop + parameter.scrollDistance
   };

   // 如果滚动条距离底部的距离值 < 每次滚动的距离 或者已经到底部,则一次滚到底
   if ( moveDistance.bar + parameter.scrollHeight >= parameter.barArea) {

   moveDistance = {
    page: parameter.view - parameter.page,
    bar: parameter.barArea - parameter.scrollHeight
   };

   }
  }

  t._scroll.css({top: moveDistance.bar});
  t._wapper.css({top: moveDistance.page}); 
 },

 //拖动滚动条
 liveEvent: function() {
  var t = this,
  draging = false,
  currentY = 0,
  lastY = 0,
  pageY = 0; 

  //检测设备类型
  var _ua = function(e) {

  var Pos = null;

  if ( u.match(/\b(Windows\sNT|Macintosh)\b/) ) {
   e = e || window.event;

   // 限制为鼠标左键点击才触发
   if (/^mouse/.test(e.type) && e.which !== 1) {
   return;
   }

   Pos = {
   left : e.pageX,
   top: e.pageY
   }

  } else {
   Pos = {
   left : e.originalEvent.targetTouches[0].pageX,
   top: e.originalEvent.targetTouches[0].pageY
   }
  }
  return Pos;
  };

  var _start = function(e) {

  //监控鼠标
  e.preventDefault();

  if (t._scroll.get(0).setCapture) {
   t._scroll.get(0).setCapture();
  }

  draging = true;

  //记录当前滚动条的坐标
  lastY = t._scroll.position().top; 

  //记录按下鼠标的坐标
  pageY = _ua(e).top;
  };

  var _drag = function(e) {

  if( draging ) {

   var pageTop = t._wapper.position().top;
   var barTop = t._scroll.position().top;

   //滚动条每移动1px,页面相对滚动Npx 再 * 当前滚动条的到顶部的距离
   var pageMoveDistance = -(parameter.page / (parameter.barArea / 1)) * barTop;

   if (lastY + ( _ua(e).top - pageY ) < 0) {
   currentY = 0;
   pageMoveDistance = 0;

   } else if( lastY + ( _ua(e).top - pageY) + parameter.scrollHeight >= parameter.barArea) {
   currentY = parameter.barArea - parameter.scrollHeight;
   pageMoveDistance = parameter.view - parameter.page;
   }
   else {
   currentY = lastY + ( _ua(e).top - pageY);
   }

   t._scroll.css({ top:currentY});
   t._wapper.css({top: pageMoveDistance}); 
  }
  };

  var _end = function(e) {

  if (draging) {

   draging = false;

   //在IE下释放对鼠标的控制
   if (t._scroll.get(0).setCapture) {
   t._scroll.get(0).releaseCapture();
   }
   
   document.onmousemove = null;
   document.onmouseup = null;
  }
  };

  t._scroll.bind( startWhen, _start );

  t._wapper.bind( startWhen, _start );

  $(document).bind( moveWhen, _drag );
  
  $(document).bind( endWhen, _end );

  $(document).bind('blur', _end);
 }
 }
 return simulation;
});

以上就是javascript模拟滚动条实现代码,希望对大家的学习有所帮助。

Javascript 相关文章推荐
showModelessDialog()使用详解
Sep 21 Javascript
JS前端框架关于重构的失败经验分享
Mar 17 Javascript
javascript使用onclick事件改变选中行的颜色
Dec 30 Javascript
javascript计时器事件使用详解
Jan 07 Javascript
原生js实现类似fullpage的单页/全屏滚动
Jan 22 Javascript
Node.js中多进程模块Cluster的介绍与使用
May 27 Javascript
windows下更新npm和node的方法
Nov 30 Javascript
vue中echarts3.0自适应的方法
Feb 26 Javascript
ng-repeat指令在迭代对象时的去重方法
Oct 02 Javascript
vue-router启用history模式下的开发及非根目录部署方法
Dec 23 Javascript
解决vue中使用proxy配置不同端口和ip接口问题
Aug 14 Javascript
微信小程序自定义组件components(代码详解)
Oct 21 Javascript
JavaScript File API实现文件上传预览
Feb 02 #Javascript
jQuery AjaxUpload 上传图片代码
Feb 02 #Javascript
js+html5操作sqlite数据库的方法
Feb 02 #Javascript
详解Webwork中Action 调用的方法
Feb 02 #Javascript
JavaScript File API文件上传预览
Feb 02 #Javascript
javascript绘制漂亮的心型线效果完整实例
Feb 02 #Javascript
Webwork 实现文件上传下载代码详解
Feb 02 #Javascript
You might like
PHP中如何定义和使用常量
2013/02/28 PHP
php读取文件内容的三种可行方法示例介绍
2014/02/08 PHP
thinkPHP引入类的方法详解
2016/12/08 PHP
jquery tab标签页的制作
2010/05/10 Javascript
js自定义事件代码说明
2011/01/31 Javascript
jQuery 淡出一个图像到另一个图像的实现代码
2013/06/12 Javascript
EasyUI,点击开启编辑框,并且编辑框获得焦点的方法
2015/03/01 Javascript
基于Bootstrap和jQuery构建前端分页工具实例代码
2016/11/23 Javascript
多种方式实现js图片预览
2016/12/12 Javascript
js点击任意区域弹出层消失实现代码
2016/12/27 Javascript
Bootstrap中glyphicons-halflings-regular.woff字体报404错notfound的解决方法
2017/01/19 Javascript
bootstrap输入框组使用方法
2017/02/07 Javascript
浅谈React深度编程之受控组件与非受控组件
2017/12/26 Javascript
浅谈vue项目重构技术要点和总结
2018/01/23 Javascript
JS中用EL表达式获取上下文参数值的方法
2018/03/28 Javascript
微信小程序实现聊天对话(文本、图片)功能
2018/07/06 Javascript
基于vue框架手写一个notify插件实现通知功能的方法
2019/03/31 Javascript
原生js实现随机点名功能
2019/11/05 Javascript
ES6常用小技巧总结【去重、交换、合并、反转、迭代、计算等】
2019/12/21 Javascript
在vue-cli创建的项目中使用sass操作
2020/08/10 Javascript
python获取外网ip地址的方法总结
2015/07/02 Python
python中pip的安装与使用教程
2018/08/10 Python
Python中面向对象你应该知道的一下知识
2019/07/10 Python
tensorflow实现二维平面模拟三维数据教程
2020/02/11 Python
Python greenlet和gevent使用代码示例解析
2020/04/01 Python
Python3操作读写CSV文件使用包过程解析
2020/04/10 Python
基于SpringBoot构造器注入循环依赖及解决方式
2020/04/26 Python
10行Python代码实现Web自动化管控的示例代码
2020/08/14 Python
python 爬虫网页登陆的简单实现
2020/11/30 Python
init进程的作用
2012/04/12 面试题
联谊会主持词
2014/03/26 职场文书
《与朱元思书》的教学反思
2014/04/17 职场文书
教师党员个人自我剖析材料
2014/09/29 职场文书
2016参观监狱警示教育活动心得体会
2016/01/15 职场文书
浅析Django接口版本控制
2021/06/26 Python
MIME类型中application/xml与text/xml的区别介绍
2022/01/18 HTML / CSS