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 相关文章推荐
超级强大的表单验证
Jun 26 Javascript
Mootools 1.2教程 定时器和哈希简介
Sep 15 Javascript
jquery常用特效方法使用示例
Apr 25 Javascript
javascript解析xml实现省市县三级联动的方法
Jul 25 Javascript
原生JavaScript实现Ajax的方法
Apr 07 Javascript
浅析JavaScript回调函数应用
May 22 Javascript
JavaScript获取当前url根目录(路径)
Jun 17 Javascript
JS继承之借用构造函数继承和组合继承
Sep 07 Javascript
浅谈原生JS实现jQuery的animate()动画示例
Mar 08 Javascript
Angular父组件调用子组件的方法
Apr 02 Javascript
Angular 如何使用第三方库的方法
Apr 18 Javascript
解决vant框架做H5时踩过的坑(下拉刷新、上拉加载等)
Nov 11 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的错误信息
2006/10/09 PHP
把PHP安装为Apache DSO
2006/10/09 PHP
WordPress自定义时间显示格式
2015/03/27 PHP
PHP简单实现生成txt文件到指定目录的方法
2016/04/25 PHP
Yii 使用intervention/image拓展实现图像处理功能
2019/06/22 PHP
PHP 实现重载
2021/03/09 PHP
javascript 静态对象和构造函数的使用和公私问题
2010/03/02 Javascript
javascript 折半查找字符在数组中的位置(有序列表)
2010/12/09 Javascript
jquery入门—访问DOM对象方法
2013/01/07 Javascript
css结合js制作下拉菜单示例代码
2014/02/27 Javascript
JavaScript中省略元素对数组长度的影响
2016/10/26 Javascript
微信开发 使用picker封装省市区三级联动模板
2016/10/28 Javascript
React-router 4 按需加载的实现方式及原理详解
2017/05/25 Javascript
JS使用ActiveXObject实现用户提交表单时屏蔽敏感词功能
2017/06/20 Javascript
angular实现页面打印局部功能的思考与方法
2018/04/13 Javascript
vue中的watch监听数据变化及watch中各属性的详解
2018/09/11 Javascript
Vue动态组件和异步组件原理详解
2019/05/06 Javascript
Node.js API详解之 net模块实例分析
2020/05/18 Javascript
Jquery cookie插件实现原理代码解析
2020/08/04 jQuery
python获取本机外网ip的方法
2015/04/15 Python
windows及linux环境下永久修改pip镜像源的方法
2016/11/28 Python
解决pycharm界面不能显示中文的问题
2018/05/23 Python
python字符串替换第一个字符串的方法
2019/06/26 Python
Win10里python3创建虚拟环境的步骤
2020/01/31 Python
印尼太阳百货公司网站:Matahari
2018/02/04 全球购物
澳大利亚领先的在线机械五金、园艺和存储专家:Edisons
2018/03/24 全球购物
飘柔洗发水广告词
2014/03/14 职场文书
初中班主任评语
2014/04/24 职场文书
小学三好学生事迹材料
2014/08/15 职场文书
远程培训的心得体会
2014/09/01 职场文书
党的群众路线教育实践活动剖析材料
2014/09/30 职场文书
小学数学继续教育研修日志
2015/11/13 职场文书
详解Vue的sync修饰符
2021/05/15 Vue.js
mysql事务隔离级别详情
2021/10/24 MySQL
六个好看实用的 HTML + CSS 后台登录入口页面
2022/04/28 HTML / CSS
nginx之内存池的实现
2022/06/28 Servers