-
Notifications
You must be signed in to change notification settings - Fork 5
/
jquery.customscroll.js
209 lines (198 loc) · 5.49 KB
/
jquery.customscroll.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
/*!
* jquery.customscroll 1.0
*
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://www.opensource.org/licenses/mit-license.php
* http://www.gnu.org/licenses/gpl-2.0.html
*/
;(function($) {
$.customscrollOptions = {
always: false,
show: {
on: 'mouseenter scrollstart',
effect: 'fadeIn',
speed: 250,
delay: 0
},
hide: {
on: 'mouseleave scrollstop',
effect: 'fadeOut',
speed: 250,
delay: 750
},
grow: {
size: 3,
speed: 100
},
pageUpnDown: {
speed: 100
}
};
$.fn.customscroll = function(options) {
options = $.extend(true, {}, $.customscrollOptions, options || {});
return this.each(function() {
var object = $(this);
// build template
object.addClass('customscroll');
object.html([
'<div class="wrapper" style="height:' + object.height() + 'px">',
'<div class="content" style="width:' + object.width() + 'px">',
object.html(),
'</div>',
'</div>',
'<div class="placeholder" style="top:' + object.css('paddingTop') + ';bottom:' + object.css('paddingBottom') + '">',
'<div class="track">',
'<div class="grip"></div>',
'</div>',
'</div>'
].join(''));
// get elements
var wrapper = object.children('.wrapper'),
placeholder = object.children('.placeholder'),
track = placeholder.children('.track'),
grip = track.children('.grip');
// object hover
var showHideTimer,
show = function(event) {
if (object.height() >= wrapper[0].scrollHeight) {
// we dont need scroll
return;
}
if (wrapper[0].scrollHeight < parseInt(object.css('max-height'))) {
// this is a fix for IE
return;
}
// show
clearInterval(showHideTimer);
showHideTimer = setTimeout(function() {
track[options.show.effect](options.show.speed);
}, options.show.delay);
if (!event || event.type !== 'scrollstart') {
// used for adjusting sizes
wrapper.trigger('scroll');
}
};
wrapper.on(options.show.on, show);
wrapper.on(options.hide.on, function callback() {
// don't hide if always is true
if (options.always) {
return;
}
if (wrapper.hasClass('keep')) {
// someone says do not hide, try again later
return setTimeout(function() {
callback();
}, 250);
}
// hide
clearInterval(showHideTimer);
showHideTimer = setTimeout(function() {
track[options.hide.effect](options.hide.speed);
}, options.hide.delay);
});
// bind scroll event
var scrollingTimer;
wrapper.on('scroll', function(event) {
var height = object.height();
grip.css({
// grip height is view height / scroll height * 200 (4 is for margins)
height: height / this.scrollHeight * height - parseInt(object.css('paddingTop')) - parseInt(object.css('paddingBottom')),
// scroll position
marginTop: this.scrollTop * 100 / this.scrollHeight * height / 100 + 'px'
});
// custom scroll start / end events
if (!scrollingTimer) {
wrapper.trigger('scrollstart');
}
clearInterval(scrollingTimer);
scrollingTimer = setTimeout(function() {
wrapper.trigger('scrollstop');
scrollingTimer = null;
}, 200);
});
// placeholder and track hover
wrapper.on('mouseenter', function() {
// do not hide track and show
wrapper.addClass('keep');
show();
});
// placeholder and track hover
wrapper.on('mouseleave', function() {
// do not hide track and show
wrapper.removeClass('keep');
});
placeholder.on('mouseenter', function() {
// do not hide track and show
wrapper.addClass('keep');
show();
});
placeholder.on('mouseleave', function() {
// hide track
wrapper.removeClass('keep');
});
track.on('mouseenter', function() {
track.addClass('hover');
if (options.grow) {
// stop animation queue and animate
track.stop(true, true);
track.animate({ width: '+=' + options.grow.size + 'px' }, options.grow.speed);
}
});
track.on('mouseleave', function hover() {
track.removeClass('hover');
if (options.grow) {
// stop animation queue and animate
track.stop(true, true);
track.animate({ width: '-=' + options.grow.size + 'px' }, options.grow.speed);
}
});
// page up / down (track click)
track.on('click', function(event) {
if ($(event.target).hasClass('grip')) {
// do not trigger when clicking the egrip
return;
}
if (event.pageY - track.offset().top > parseInt(grip.css('marginTop'))) {
// clicked after grip
var operation = '+';
} else {
// clicked before grip
var operation = '-';
}
// scroll
wrapper.animate({
scrollTop: operation + '=' + object.height() + 'px'
}, options.pageUpnDown.speed);
});
// show initially if always is true
if (options.always) {
show();
}
// grip drag
var dragging = false;
grip.on('mousedown', function(event) {
// do not hide
wrapper.addClass('keep');
// mark as dragging
dragging = true;
// prevent text selection
$('body').addClass('dragging');
});
$(document).on('mousemove', function(event) {
if (dragging) {
// we have drag, move
wrapper.scrollTop(wrapper[0].scrollHeight * (event.pageY - track.offset().top - grip.height() / 2) / object.height());
event.preventDefault();
}
});
$(document).on('mouseup', function() {
if (dragging) {
// we have drag, remove
wrapper.removeClass('keep');
dragging = false;
$('body').removeClass('dragging');
}
});
});
};
})(jQuery);