/** * @ignore * scrollbar for KISSY scroll-view * @author yiminghe@gmail.com */ KISSY.add('scroll-view/plugin/scrollbar/control', function (S, Node, Control, ScrollBarRender) { var MIN_BAR_LENGTH = 20; var SCROLLBAR_EVENT_NS = '.ks-scrollbar'; var Gesture = Node.Gesture; var Features = S.Features; var allowDrag = !Features.isTouchEventSupported() && !Features.isMsPointerSupported(); /** * @class KISSY.ScrollView.ScrollBar * @extend KISSY.Component.Control * @private */ return Control.extend({ initializer: function () { var self = this; var scrollType = self.scrollType = self.get('axis') == 'x' ? 'left' : 'top'; var ucScrollType = S.ucfirst(scrollType); self.pageXyProperty = scrollType == 'left' ? 'pageX' : 'pageY'; var wh = self.whProperty = scrollType == 'left' ? 'width' : 'height'; var ucWH = S.ucfirst(wh); self.afterScrollChangeEvent = 'afterScroll' + ucScrollType + 'Change'; self.scrollProperty = 'scroll' + ucScrollType; self.dragWHProperty = 'drag' + ucWH; self.dragLTProperty = 'drag' + ucScrollType; self.clientWHProperty = 'client' + ucWH; self.scrollWHProperty = 'scroll' + ucWH; }, bindUI: function () { var self = this, autoHide = self.get('autoHide'), scrollView = self.get('scrollView'); if (autoHide) { self.hideFn = S.bind(self.hide, self); } else { S.each([self.$downBtn, self.$upBtn], function (b) { b.on(Gesture.start, self.onUpDownBtnMouseDown, self) .on(Gesture.end, self.onUpDownBtnMouseUp, self); }); self.$trackEl.on(Gesture.start, self.onTrackElMouseDown, self); if (allowDrag) { S.use('dd', function (S, DD) { self.dd = new DD.Draggable({ node: self.$dragEl, disabled: self.get('disabled'), groups: false, // allow nested scroll-view halt: true }).on('drag', self.onDrag, self) .on('dragstart', self.onDragStart, self); }); } } scrollView .on(self.afterScrollChangeEvent + SCROLLBAR_EVENT_NS, self.afterScrollChange, self) .on('scrollEnd' + SCROLLBAR_EVENT_NS, self.onScrollEnd, self) .on('afterDisabledChange', self.onScrollViewDisabled, self); }, destructor: function () { this.get('scrollView').detach(SCROLLBAR_EVENT_NS); this.clearHideTimer(); }, onScrollViewDisabled: function (e) { this.set('disabled', e.newVal); }, onDragStart: function () { var self = this, scrollView = self.scrollView; self.startMousePos = self.dd.get('startMousePos')[self.scrollType]; self.startScroll = scrollView.get(self.scrollProperty); }, onDrag: function (e) { var self = this, diff = e[self.pageXyProperty] - self.startMousePos, scrollView = self.scrollView, scrollType = self.scrollType, scrollCfg = {}; scrollCfg[scrollType] = self.startScroll + diff / self.trackElSize * self.scrollLength; scrollView.scrollToWithBounds(scrollCfg); }, startHideTimer: function () { var self = this; self.clearHideTimer(); self.hideTimer = setTimeout(self.hideFn, self.get('hideDelay') * 1000); }, clearHideTimer: function () { var self = this; if (self.hideTimer) { clearTimeout(self.hideTimer); self.hideTimer = null; } }, onUpDownBtnMouseDown: function (e) { if (this.get('disabled')) { return; } e.halt(); var self = this, scrollView = self.scrollView, scrollProperty = self.scrollProperty, scrollType = self.scrollType, step = scrollView.getScrollStep()[self.scrollType], target = e.target, direction = (target == self.downBtn || self.$downBtn.contains(target)) ? 1 : -1; clearInterval(self.mouseInterval); function doScroll() { var scrollCfg = {}; scrollCfg[scrollType] = scrollView.get(scrollProperty) + direction * step; scrollView.scrollToWithBounds(scrollCfg); } self.mouseInterval = setInterval(doScroll, 100); doScroll(); }, onTrackElMouseDown: function (e) { var self = this; if (self.get('disabled')) { return; } var target = e.target; var dragEl = self.dragEl; var $dragEl = self.$dragEl; if (dragEl == target || $dragEl.contains(target)) { return; } var scrollType = self.scrollType, pageXy = self.pageXyProperty, trackEl = self.$trackEl, scrollView = self.scrollView, // align mouse with bar center per = Math.max(0, (e[pageXy] - trackEl.offset()[scrollType] - self.barSize / 2) / self.trackElSize), scrollCfg = {}; scrollCfg[scrollType] = per * self.scrollLength; scrollView.scrollToWithBounds(scrollCfg); // prevent drag e.halt(); }, onUpDownBtnMouseUp: function () { clearInterval(this.mouseInterval); }, onScrollEnd: function () { var self = this; if (self.hideFn) { self.startHideTimer(); } }, // percentage matters! afterScrollChange: function () { // only show when scroll var self = this; var scrollView = self.scrollView; if (!scrollView.allowScroll[self.scrollType]) { return; } self.clearHideTimer(); self.set('visible', true); if (self.hideFn && !scrollView.isScrolling) { self.startHideTimer(); } self.view.syncOnScrollChange(); }, _onSetDisabled: function (v) { if (this.dd) { this.dd.set('disabled', v); } } }, { ATTRS: { /** * minimum scrollbar length. * Defaults to 20. * @cfg {Number} minLength */ /** * @ignore */ minLength: { value: MIN_BAR_LENGTH }, scrollView: { }, axis: { view: 1 }, /** * whether auto hide scrollbar after scroll end. * Defaults: true for touch device, false for non-touch device. * @cfg {Boolean} autoHide */ /** * @ignore */ autoHide: { value: S.UA.ios }, visible: { valueFn: function () { return !this.get('autoHide'); } }, /** * second of hide delay for scrollbar if allow autoHide * @cfg {Number} hideDelay */ /** * @ignore */ hideDelay: { value: 0.1 }, dragWidth: { setter: function (v) { var minLength = this.get('minLength'); if (v < minLength) { return minLength; } return v; }, view: 1 }, dragHeight: { setter: function (v) { var minLength = this.get('minLength'); if (v < minLength) { return minLength; } return v; }, view: 1 }, dragLeft: { view: 1 }, dragTop: { view: 1 }, dragEl: { }, downBtn: { }, upBtn: { }, trackEl: { }, focusable: { value: false }, xrender: { value: ScrollBarRender } }, xclass: 'scrollbar' }); }, { requires: ['node', 'component/control', './render'] });