You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

387 lines
14 KiB

/*!
* better-scroll / wheel
* (c) 2016-2020 ustbhuangyi
* Released under the MIT License.
*/
// ssr support
var inBrowser = typeof window !== 'undefined';
var ua = inBrowser && navigator.userAgent.toLowerCase();
var isWeChatDevTools = !!(ua && /wechatdevtools/.test(ua));
var isAndroid = ua && ua.indexOf('android') > 0;
/* istanbul ignore next */
var isIOSBadVersion = (function () {
if (typeof ua === 'string') {
var regex = /os (\d\d?_\d(_\d)?)/;
var matches = regex.exec(ua);
if (!matches)
return false;
var parts = matches[1].split('_').map(function (item) {
return parseInt(item, 10);
});
// ios version >= 13.4 issue 982
return !!(parts[0] >= 13 && parts[1] >= 4);
}
return false;
})();
var extend = function (target, source) {
for (var key in source) {
target[key] = source[key];
}
return target;
};
var elementStyle = (inBrowser &&
document.createElement('div').style);
var vendor = (function () {
/* istanbul ignore if */
if (!inBrowser) {
return false;
}
var transformNames = [
{
key: 'standard',
value: 'transform',
},
{
key: 'webkit',
value: 'webkitTransform',
},
{
key: 'Moz',
value: 'MozTransform',
},
{
key: 'O',
value: 'OTransform',
},
{
key: 'ms',
value: 'msTransform',
},
];
for (var _i = 0, transformNames_1 = transformNames; _i < transformNames_1.length; _i++) {
var obj = transformNames_1[_i];
if (elementStyle[obj.value] !== undefined) {
return obj.key;
}
}
/* istanbul ignore next */
return false;
})();
/* istanbul ignore next */
function prefixStyle(style) {
if (vendor === false) {
return style;
}
if (vendor === 'standard') {
if (style === 'transitionEnd') {
return 'transitionend';
}
return style;
}
return vendor + style.charAt(0).toUpperCase() + style.substr(1);
}
var cssVendor = vendor && vendor !== 'standard' ? '-' + vendor.toLowerCase() + '-' : '';
var transform = prefixStyle('transform');
var transition = prefixStyle('transition');
var hasPerspective = inBrowser && prefixStyle('perspective') in elementStyle;
var style = {
transform: transform,
transition: transition,
transitionTimingFunction: prefixStyle('transitionTimingFunction'),
transitionDuration: prefixStyle('transitionDuration'),
transitionDelay: prefixStyle('transitionDelay'),
transformOrigin: prefixStyle('transformOrigin'),
transitionEnd: prefixStyle('transitionEnd'),
transitionProperty: prefixStyle('transitionProperty'),
};
function hasClass(el, className) {
var reg = new RegExp('(^|\\s)' + className + '(\\s|$)');
return reg.test(el.className);
}
var ease = {
// easeOutQuint
swipe: {
style: 'cubic-bezier(0.23, 1, 0.32, 1)',
fn: function (t) {
return 1 + --t * t * t * t * t;
}
},
// easeOutQuard
swipeBounce: {
style: 'cubic-bezier(0.25, 0.46, 0.45, 0.94)',
fn: function (t) {
return t * (2 - t);
}
},
// easeOutQuart
bounce: {
style: 'cubic-bezier(0.165, 0.84, 0.44, 1)',
fn: function (t) {
return 1 - --t * t * t * t;
}
}
};
var sourcePrefix = 'plugins.wheel';
var propertiesMap = [
{
key: 'wheelTo',
name: 'wheelTo'
},
{
key: 'getSelectedIndex',
name: 'getSelectedIndex'
}
];
var propertiesConfig = propertiesMap.map(function (item) {
return {
key: item.key,
sourceKey: sourcePrefix + "." + item.name
};
});
var CONSTANTS = {
rate: 4,
};
var Wheel = /** @class */ (function () {
function Wheel(scroll) {
this.scroll = scroll;
this.init();
}
Wheel.prototype.init = function () {
this.handleBScroll();
this.handleOptions();
this.handleHooks();
// init boundary for Wheel
this.refreshBoundary();
this.handleSelectedIndex();
};
Wheel.prototype.handleBScroll = function () {
this.scroll.proxy(propertiesConfig);
};
Wheel.prototype.handleOptions = function () {
var userOptions = (this.scroll.options.wheel === true
? {}
: this.scroll.options.wheel);
var defaultOptions = {
wheelWrapperClass: 'wheel-scroll',
wheelItemClass: 'wheel-item',
rotate: 25,
adjustTime: 400,
selectedIndex: 0,
wheelDisabledItemClass: 'wheel-disabled-item',
};
this.options = extend(defaultOptions, userOptions);
};
Wheel.prototype.handleHooks = function () {
var _this = this;
var scroller = this.scroll.scroller;
var actionsHandler = scroller.actionsHandler, scrollBehaviorX = scroller.scrollBehaviorX, scrollBehaviorY = scroller.scrollBehaviorY, animater = scroller.animater;
var prevContent = scroller.content;
// BScroll
this.scroll.hooks.on(this.scroll.hooks.eventTypes.refresh, function (content) {
if (content !== prevContent) {
prevContent = content;
_this.handleSelectedIndex();
}
// check we are stop at a disable item
_this.wheelTo(_this.selectedIndex, 0);
});
this.scroll.hooks.on(this.scroll.hooks.eventTypes.beforeInitialScrollTo, function (position) {
// selectedIndex has a better priority than bs.options.startY
position.x = 0;
position.y = -(_this.selectedIndex * _this.itemHeight);
});
// Scroller
scroller.hooks.on(scroller.hooks.eventTypes.checkClick, function () {
var index = Array.prototype.slice
.call(_this.items, 0)
.indexOf(_this.target);
if (index === -1)
return true;
_this.wheelToAfterClick(index, _this.options.adjustTime, ease.swipe);
return true;
});
scroller.hooks.on(scroller.hooks.eventTypes.scrollTo, function (endPoint) {
endPoint.y = _this.findNearestValidWheel(endPoint.y).y;
});
scroller.hooks.on(scroller.hooks.eventTypes.scrollToElement, function (el, pos) {
if (!hasClass(el, _this.options.wheelItemClass)) {
return true;
}
else {
pos.top = _this.findNearestValidWheel(pos.top).y;
}
});
// ActionsHandler
actionsHandler.hooks.on(actionsHandler.hooks.eventTypes.beforeStart, function (e) {
_this.target = e.target;
});
// ScrollBehaviorX
// Wheel has no x direction now
scrollBehaviorX.hooks.on(scrollBehaviorX.hooks.eventTypes.computeBoundary, function (boundary) {
boundary.maxScrollPos = 0;
boundary.minScrollPos = 0;
});
// ScrollBehaviorY
scrollBehaviorY.hooks.on(scrollBehaviorY.hooks.eventTypes.computeBoundary, function (boundary) {
_this.items = _this.scroll.scroller.content.children;
_this.checkWheelAllDisabled();
_this.itemHeight =
_this.items.length > 0
? scrollBehaviorY.contentSize / _this.items.length
: 0;
boundary.maxScrollPos = -_this.itemHeight * (_this.items.length - 1);
boundary.minScrollPos = 0;
});
scrollBehaviorY.hooks.on(scrollBehaviorY.hooks.eventTypes.momentum, function (momentumInfo, distance) {
momentumInfo.rate = CONSTANTS.rate;
momentumInfo.destination = _this.findNearestValidWheel(momentumInfo.destination).y;
// TODO algorithm optimize
var maxDistance = 1000;
var minDuration = 800;
if (distance < maxDistance) {
momentumInfo.duration = Math.max(minDuration, (distance / maxDistance) * _this.scroll.options.swipeTime);
}
});
scrollBehaviorY.hooks.on(scrollBehaviorY.hooks.eventTypes.end, function (momentumInfo) {
var validWheel = _this.findNearestValidWheel(scrollBehaviorY.currentPos);
momentumInfo.destination = validWheel.y;
momentumInfo.duration = _this.options.adjustTime;
_this.selectedIndex = validWheel.index;
});
// Animater
animater.hooks.on(animater.hooks.eventTypes.time, function (time) {
_this.transitionDuration(time);
});
animater.hooks.on(animater.hooks.eventTypes.timeFunction, function (easing) {
_this.timeFunction(easing);
});
animater.hooks.on(animater.hooks.eventTypes.beforeForceStop, function (_a) {
var y = _a.y;
_this.target = _this.items[_this.findNearestValidWheel(y).index];
// don't dispatch scrollEnd when forceStop from transition or animation
return true;
});
// bs.stop() to make wheel stop at a correct position
animater.hooks.on(animater.hooks.eventTypes.callStop, function () {
var index = Array.prototype.slice
.call(_this.items, 0)
.indexOf(_this.target);
if (index > 0) {
var y = -(index * _this.itemHeight);
animater.translate({ x: 0, y: y });
}
});
// Translater
animater.translater.hooks.on(animater.translater.hooks.eventTypes.translate, function (endPoint) {
_this.rotateX(endPoint.y);
_this.selectedIndex = _this.findNearestValidWheel(endPoint.y).index;
});
};
Wheel.prototype.refreshBoundary = function () {
var _a = this.scroll.scroller, scrollBehaviorX = _a.scrollBehaviorX, scrollBehaviorY = _a.scrollBehaviorY, content = _a.content;
scrollBehaviorX.refresh(content);
scrollBehaviorY.refresh(content);
};
Wheel.prototype.handleSelectedIndex = function () {
this.selectedIndex = this.options.selectedIndex;
};
Wheel.prototype.getSelectedIndex = function () {
return this.selectedIndex;
};
Wheel.prototype.wheelTo = function (index, time, ease) {
if (index === void 0) { index = 0; }
if (time === void 0) { time = 0; }
var y = -index * this.itemHeight;
var currentY = Math.round(this.scroll.y);
this.scroll.scrollTo(0, y, time, ease);
return y === currentY;
};
Wheel.prototype.wheelToAfterClick = function (index, time, ease) {
if (index === void 0) { index = 0; }
if (time === void 0) { time = 0; }
var needDispatchScrollEnd = this.wheelTo(index, time, ease);
// startpoint === endpoint
// manually trigger scrollEnd
if (needDispatchScrollEnd) {
var hooks = this.scroll.scroller.hooks;
hooks.trigger(hooks.eventTypes.scrollEnd);
}
};
Wheel.prototype.transitionDuration = function (time) {
for (var i = 0; i < this.items.length; i++) {
this.items[i].style[style.transitionDuration] =
time + 'ms';
}
};
Wheel.prototype.timeFunction = function (easing) {
for (var i = 0; i < this.items.length; i++) {
this.items[i].style[style.transitionTimingFunction] = easing;
}
};
Wheel.prototype.rotateX = function (y) {
var _a = this.options.rotate, rotate = _a === void 0 ? 25 : _a;
for (var i = 0; i < this.items.length; i++) {
var deg = rotate * (y / this.itemHeight + i);
// Too small value is invalid in some phones, issue 1026
var SafeDeg = deg.toFixed(3);
this.items[i].style[style.transform] = "rotateX(" + SafeDeg + "deg)";
}
};
Wheel.prototype.findNearestValidWheel = function (y) {
y = y > 0 ? 0 : y < this.scroll.maxScrollY ? this.scroll.maxScrollY : y;
var currentIndex = Math.abs(Math.round(-y / this.itemHeight));
var cacheIndex = currentIndex;
var items = this.items;
var wheelDisabledItemClassName = this.options
.wheelDisabledItemClass;
// Impersonation web native select
// first, check whether there is a enable item whose index is smaller than currentIndex
// then, check whether there is a enable item whose index is bigger than currentIndex
// otherwise, there are all disabled items, just keep currentIndex unchange
while (currentIndex >= 0) {
if (!hasClass(items[currentIndex], wheelDisabledItemClassName)) {
break;
}
currentIndex--;
}
if (currentIndex < 0) {
currentIndex = cacheIndex;
while (currentIndex <= items.length - 1) {
if (!hasClass(items[currentIndex], wheelDisabledItemClassName)) {
break;
}
currentIndex++;
}
}
// keep it unchange when all the items are disabled
if (currentIndex === items.length) {
currentIndex = cacheIndex;
}
// when all the items are disabled, this.selectedIndex should always be -1
return {
index: this.wheelItemsAllDisabled ? -1 : currentIndex,
y: -currentIndex * this.itemHeight,
};
};
Wheel.prototype.checkWheelAllDisabled = function () {
var wheelDisabledItemClassName = this.options.wheelDisabledItemClass;
var items = this.items;
this.wheelItemsAllDisabled = true;
for (var i = 0; i < items.length; i++) {
if (!hasClass(items[i], wheelDisabledItemClassName)) {
this.wheelItemsAllDisabled = false;
break;
}
}
};
Wheel.pluginName = 'wheel';
return Wheel;
}());
export default Wheel;