这里有新鲜出炉的 Javascript 教程,程序狗速度看过来!
Javascript 是一种由 Netscape 的 LiveScript 发展而来的原型化继承的基于对象的动态类型的区分大小写的客户端脚本语言,主要目的是为了解决服务器端语言,比如 Perl,遗留的速度问题,为客户提供更流畅的浏览效果。
本文给大家分享的是使用原生 javascript 实现模拟滚动条的方法及代码,效果非常棒,有需要的小伙伴可以参考下。
当页面中有很多滚动条,它们相互嵌套,很不好看,这时就会模拟滚动条,并给这个滚动条好看的样式,使得页面美观。
模拟滚动条很多时候是去用 jquery 插件,然后写几行代码就搞定了。不过随着 mvvm 的快速发展,很多时候都懒得用 jquery 了,这就是本文的动机,本屌力求用简单的不依赖 jquery 只依赖 mvvm(avalon) api 的代码, 完成一个简易的滚动条。
要求:
1. 鼠标滚轮可以让滚动条工作, 界面滚动
2. 鼠标可以拖动滚动条并让界面滚动
3. 页面 resize 时,滚动条根据页面尺寸变化,仍然可以工作
效果:
很显然,这个组件是基于拖动 drag 的,本屌又不想重新写,就只有改下 ui 框架的 drag 了,这里改的是 easy js ui 的 drag 组件。用 easy js 是因为注释比较多,代码简洁。
本屌把 easy js ui 的 drag 组件里的相应方法换成 avalon api 里的方法,删掉 prototype 里的方法及冗余代码
- define('drag', ['avalon-min'],
- function(avalon) {
- function getBoundary(container, target) {
- var borderTopWidth = 0,
- borderRightWidth = 0,
- borderBottomWidth = 0,
- borderLeftWidth = 0,
- cOffset = avalon(container).offset(),
- cOffsetTop = cOffset.top,
- cOffsetLeft = cOffset.left,
- tOffset = avalon(target).offset();
- borderTopWidth = parseFloat(avalon.CSS(container, 'borderTopWidth'));
- borderRightWidth = parseFloat(avalon.css(container, 'borderRightWidth'));
- borderBottomWidth = parseFloat(avalon.css(container, 'borderBottomWidth'));
- borderLeftWidth = parseFloat(avalon.css(container, 'borderLeftWidth'));
- cOffsetTop = cOffsetTop - tOffset.top + parseFloat(avalon(target).css('top'));
- cOffsetLeft = cOffsetLeft - tOffset.left + parseFloat(avalon(target).css('left'));
- return {
- top: cOffsetTop + borderTopWidth,
- right: cOffsetLeft + avalon(container).outerWidth() - avalon(target).outerWidth() - borderRightWidth,
- left: cOffsetLeft + borderLeftWidth,
- bottom: cOffsetTop + avalon(container).outerHeight() - avalon(target).outerHeight() - borderBottomWidth
- };
- }
- var drag = function(target, options) {
- var defaults = {
- axis: null,
- container: null,
- handle: null,
- ondragmove: null
- };
- var o = avalon.mix(defaults, options),
- doc = target.ownerDocument,
- win = doc.defaultView || doc.parentWindow,
- originHandle = target,
- isIE = !-[1, ],
- handle = isIE ? target: doc,
- container = o.container ? o.container: null,
- count = 0,
- drag = this,
- axis = o.axis,
- isMove = false,
- boundary,
- zIndex,
- originalX,
- originalY,
- clearSelect = 'getSelection' in win ?
- function() {
- win.getSelection().removeAllRanges();
- }: function() {
- try {
- doc.selection.empty();
- } catch(e) {};
- },
- down = function(e) {
- o.isDown = true;
- var newTarget = target,
- left, top, offset;
- o.width = avalon(target).outerWidth();
- o.height = avalon(target).outerHeight();
- o.handle = handle;
- left = avalon(newTarget).css('left');
- top = avalon(newTarget).css('top');
- offset = avalon(newTarget).offset();
- drag.left = left = parseInt(left);
- drag.top = top = parseInt(top);
- drag.offsetLeft = offset.left;
- drag.offsetTop = offset.top;
- originalX = e.pageX - left;
- originalY = e.pageY - top;
- if ((!boundary && container)) {
- boundary = getBoundary(container, newTarget);
- }
- if (axis) {
- if (axis === 'x') {
- originalY = false;
- } else if (axis === 'y') {
- originalX = false;
- }
- }
- if (isIE) {
- handle.setCapture();
- }
- avalon.bind(handle, 'mousemove', move);
- avalon.bind(handle, 'mouseup', up);
- if (isIE) {
- avalon.bind(handle, 'losecapture', up);
- }
- e.stopPropagation();
- e.preventDefault();
- },
- move = function(e) {
- if (!o.isDown) {
- return;
- }
- count++;
- if (count % 2 === 0) {
- return;
- }
- var currentX = e.pageX,
- currentY = e.pageY,
- style = target.style,
- x, y, left, right, top, bottom;
- clearSelect();
- isMove = true;
- if (originalX) {
- x = currentX - originalX;
- if (boundary) {
- left = boundary.left;
- right = boundary.right;
- x = x < left ? left: x > right ? right: x;
- }
- drag.left = x;
- drag.offsetLeft = currentX - e.offsetX;
- style.left = x + 'px';
- }
- if (originalY) {
- y = currentY - originalY;
- if (boundary) {
- top = boundary.top;
- bottom = boundary.bottom;
- y = y < top ? top: y > bottom ? bottom: y;
- }
- drag.top = y;
- drag.offsetTop = currentY - e.offsetY;
- style.top = y + 'px';
- }
- o.ondragmove.call(this, drag);
- e.stopPropagation();
- },
- up = function(e) {
- o.isDown = false;
- if (isIE) {
- avalon.unbind(handle, 'losecapture');
- }
- avalon.unbind(handle, 'mousemove');
- avalon.unbind(handle, 'mouseup');
- if (isIE) {
- handle.releaseCapture();
- }
- e.stopPropagation();
- };
- avalon(originHandle).css('cursor', 'pointer');
- avalon.bind(originHandle, 'mousedown', down);
- drag.refresh = function() {
- boundary = getBoundary(container, target);
- };
- };
- return drag;
- });
另外在最后暴露的 drag 上加了一个 refresh()方法,作用是在 resize 时,需要更新滚动条可以拖动的范围。这个方法在 scrollbar 的更新视图中会用到。
- drag.refresh=function(){
- boundary=getBoundary(container,target);
- };
还有在滚动条拖动过程 move 中,添加一个钩子,允许从外面添加一个监听函数,拖动时会触发监听函数,并传入 drag 参数。
- o.ondragmove.call(this,drag);
然后是 scrollbar.js
- define('scrollbar', ['avalon-min', 'drag'],
- function(avalon, drag) {
- function scrollbar(wrap, scrollbar, height_per_scroll) { //容器,滚动条,每次滚轮移动的距离
- this.scroll_height = 0; //滚动条高度
- this.dragger = null; //drag组件实例
- wrap.scrollTop = 0;
- //容器的位置要减去浏览器最外面的默认滚动条垂直方向位置
- var self = this,
- wrap_top = avalon(wrap).offset().top - avalon(document).scrollTop();
- function ondragmove(drag) { //drag组件拖动时的监听函数,更新容器视图
- wrap.scrollTop = (parseFloat(scrollbar.style.top) - wrap_top) * (wrap.scrollHeight - wrap.clientHeight) / (wrap.clientHeight - self.scroll_height);
- };
- function setScrollPosition(o) { //更新滚动条位置
- scrollbar.style.top = o.scrollTop * wrap.clientHeight / wrap.scrollHeight + wrap_top + 'px';
- }
- function inti_events() {
- avalon.bind(wrap, 'mousewheel',
- function(e) {
- if (e.wheelDelta < 0) wrap.scrollTop += height_per_scroll;
- else wrap.scrollTop -= height_per_scroll;
- setScrollPosition(wrap);
- e.preventDefault();
- });
- self.dragger = new drag(scrollbar, {
- container: wrap,
- axis: 'y',
- ondragmove: ondragmove
- });
- window.onresize = function() {
- self.refresh_views();
- self.dragger.refresh();
- };
- }
- this.refresh_views = function() { //更新组件所有部分视图,并暴露供外部调用
- //容器高度这里设置成浏览器可视部分-容器垂直方向位置,没有考虑容器有border,padding,margin.可根据相应场景修改
- wrap.style.height = document.documentElement.clientHeight - wrap_top + 'px';
- self.scroll_height = wrap.clientHeight * wrap.clientHeight / wrap.scrollHeight;
- //容器高度等于滚动条高度,隐藏滚动条
- if (self.scroll_height == wrap.clientHeight) scrollbar.style.display = 'none';
- else scrollbar.style.display = 'block';
- scrollbar.style.height = self.scroll_height + 'px';
- setScrollPosition(wrap);
- }
- function init() {
- self.refresh_views();
- inti_events();
- }
- init();
- }
- return scrollbar;
- });
可以看到,在 resize 时,调用了 drag 组件的 refresh 方法,更新滚动条可以拖动的范围。这里暴露了 refresh_views()方法,以应对外部需要手动更新视图的情况。比如,聊天分组的折叠和展开。
这样就完成了简易滚动条。代码很简单,如果出问题需要 fix bug 或定制的话,也很容易。
来源: http://www.phperz.com/article/17/0625/270861.html