@bytedance/mona-client-web
Version:
web for mona
194 lines • 9.77 kB
JavaScript
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
var __rest = (this && this.__rest) || function (s, e) {
var t = {};
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
t[p] = s[p];
if (s != null && typeof Object.getOwnPropertySymbols === "function")
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
t[p[i]] = s[p[i]];
}
return t;
};
// 参考arco Mobile
import React, { useCallback, useEffect, useMemo, useRef, useState, useLayoutEffect } from 'react';
import { PickerCellMovingStatus } from '../Picker/type';
import { useRefState } from '../Picker/hooks';
import { getStyleWithVendor } from '../../utils/style';
import styles from '../PickerView/index.module.less';
var PickerCell = function (props) {
var data = props.data, itemHeight = props.itemHeight, wrapperHeight = props.wrapperHeight, selectedValue = props.selectedValue, onValueChange = props.onValueChange, _a = props.rows, rows = _a === void 0 ? 5 : _a, restProps = __rest(props, ["data", "itemHeight", "wrapperHeight", "selectedValue", "onValueChange", "rows"]);
var _b = useState(''), transitionDuration = _b[0], setTransitionDuration = _b[1];
var _c = useState(''), bezier = _c[0], setBezier = _c[1];
var _d = useState(0), currentIndex = _d[0], setCurrentIndex = _d[1];
var _e = useState(selectedValue), currentValue = _e[0], setCurrentValue = _e[1];
var _f = useRefState(0), transformY = _f[0], transformYRef = _f[1], setTransformY = _f[2];
var lastTransformYRef = useRef(0);
var touchStartTimeRef = useRef(0);
var latestCallbackTimer = useRef(0);
var touchStartYRef = useRef(0);
var touchingRef = useRef(false);
var wrapRef = useRef(null);
var movingStatusRef = useRef(PickerCellMovingStatus.normal);
var rowCount = Math.max(rows % 2 === 0 ? rows + 1 : rows, 3);
var colStyle = useMemo(function () {
return getStyleWithVendor(__assign(__assign({ transform: "translate3d(0px, ".concat(transformY || 0, "px, 0px)") }, (transitionDuration ? { transitionDuration: transitionDuration } : {})), { transitionTimingFunction: bezier, paddingBottom: "".concat(((rowCount - 1) / 2) * itemHeight, "px"), paddingTop: "".concat(((rowCount - 1) / 2) * itemHeight, "px") }));
}, [transitionDuration, transformY, bezier, itemHeight, rowCount]);
function scrollingComplete(nowItemIndex) {
var _a;
if (currentIndex !== nowItemIndex) {
setCurrentIndex(nowItemIndex);
var newValue = (_a = data[nowItemIndex]) === null || _a === void 0 ? void 0 : _a.value;
if (newValue !== currentValue) {
setCurrentValue(newValue);
onValueChange === null || onValueChange === void 0 ? void 0 : onValueChange(newValue);
}
}
}
var scrollTo = useCallback(function (transY, transDuration, cb) {
if (transDuration === void 0) { transDuration = 0; }
if (cb === void 0) { cb = function () { }; }
setTransitionDuration(transDuration ? "".concat(transDuration, "ms") : '');
setTransformY(transY);
if (latestCallbackTimer.current) {
clearTimeout(latestCallbackTimer.current);
}
latestCallbackTimer.current = window.setTimeout(function () {
movingStatusRef.current = PickerCellMovingStatus.normal;
setTransitionDuration('');
cb();
}, transDuration);
}, []);
function scrollToIndex(itemIndex, transDuration) {
if (transDuration === void 0) { transDuration = 0; }
scrollTo(-1 * itemIndex * itemHeight, transDuration, function () {
scrollingComplete(itemIndex);
});
}
var handleColumnTouchStart = useCallback(function (e) {
movingStatusRef.current = PickerCellMovingStatus.moving;
var y = e.touches[0].screenY;
touchStartTimeRef.current = Number(new Date());
touchingRef.current = true;
touchStartYRef.current = y;
lastTransformYRef.current = transformYRef.current;
}, []);
var handleColumnTouchMove = useCallback(function (e) {
if (!touchingRef.current) {
return;
}
e.cancelable && e.preventDefault();
var lastTransformY = lastTransformYRef.current;
var touchMoveY = e.touches[0].screenY;
var distance = touchMoveY - touchStartYRef.current;
var newPos = lastTransformY + distance;
var maxPos = -1 * (data.length - 1) * itemHeight;
setTransformY((lastTransformY >= 0 && distance > 0) || (lastTransformY <= maxPos && distance < 0)
? lastTransformY + distance / 4
: newPos);
}, [data.length, itemHeight]);
useEffect(function () {
var _a, _b;
(_a = wrapRef.current) === null || _a === void 0 ? void 0 : _a.addEventListener('touchstart', handleColumnTouchStart);
(_b = wrapRef.current) === null || _b === void 0 ? void 0 : _b.addEventListener('touchmove', handleColumnTouchMove);
return function () {
var _a, _b;
(_a = wrapRef.current) === null || _a === void 0 ? void 0 : _a.removeEventListener('touchstart', handleColumnTouchStart);
(_b = wrapRef.current) === null || _b === void 0 ? void 0 : _b.removeEventListener('touchmove', handleColumnTouchMove);
};
}, [handleColumnTouchStart, handleColumnTouchMove]);
function handleScrollEnd() {
var maxIndex = data.length - 1;
var nowIndex = Math.max(0, Math.min(maxIndex, Math.round((-1 * transformY) / itemHeight)));
scrollToIndex(nowIndex, 100);
}
// 参考:https://juejin.im/post/6844904185121488910
//@ts-ignore
function momentum(current, start, duration, minY, maxY) {
var durationMap = {
noBounce: 800,
weekBounce: 800,
strongBounce: 400,
};
var bezierMap = {
noBounce: 'cubic-bezier(.17, .89, .45, 1)',
weekBounce: 'cubic-bezier(.25, .46, .45, .94)',
strongBounce: 'cubic-bezier(.25, .46, .45, .94)',
};
var type = 'noBounce';
var deceleration = 0.003;
var bounceRate = 5;
var bounceThreshold = 300;
var maxOverflowY = wrapperHeight / 6;
var overflowY;
var distance = current - start;
var speed = (2 * Math.abs(distance)) / duration;
var destination = current + (speed / deceleration) * (distance < 0 ? -1 : 1);
if (destination < minY) {
overflowY = minY - destination;
type = overflowY > bounceThreshold ? 'strongBounce' : 'weekBounce';
destination = Math.max(minY - maxOverflowY, minY - overflowY / bounceRate);
}
else if (destination > maxY) {
overflowY = destination - maxY;
type = overflowY > bounceThreshold ? 'strongBounce' : 'weekBounce';
destination = Math.min(maxY + maxOverflowY, maxY + overflowY / bounceRate);
}
return {
destination: destination,
duration: durationMap[type],
bezier: bezierMap[type],
};
}
function handleColumnTouchEnd() {
movingStatusRef.current = PickerCellMovingStatus.normal;
var lastTransformY = lastTransformYRef.current;
if (transformY === lastTransformY) {
return;
}
touchingRef.current = false;
var endTime = Number(new Date());
var scrollerHeight = (data.length + rowCount - 1) * itemHeight;
var duration = endTime - touchStartTimeRef.current;
var absDistY = Math.abs(transformY - lastTransformY);
if (duration < 300 && absDistY > 30) {
var momentumY = momentum(transformY, lastTransformY, duration, wrapperHeight - scrollerHeight, 0);
var newItemIndex = Math.max(0, Math.min(data.length - 1, Math.round((-1 * momentumY.destination) / itemHeight)));
setBezier(momentumY.bezier);
movingStatusRef.current = PickerCellMovingStatus.scrolling;
scrollToIndex(newItemIndex, momentumY.duration);
}
else {
handleScrollEnd();
}
}
function handleItemClick(itemIndex) {
scrollToIndex(itemIndex, 100);
}
useLayoutEffect(function () {
if ('selectedValue' in props) {
var curIndex = data.findIndex(function (item) { return item.value === selectedValue; });
setCurrentIndex(curIndex);
if (curIndex >= 0) {
scrollToIndex(curIndex, 0);
}
}
//@ts-ignore
}, [selectedValue, itemHeight]);
return (data === null || data === void 0 ? void 0 : data.length) ? (React.createElement("div", __assign({ className: styles.pickerViewColumn }, restProps),
React.createElement("div", { className: styles.pickerViewColumnItemWrap, style: colStyle, ref: wrapRef, onTouchEnd: handleColumnTouchEnd, onTouchCancel: handleColumnTouchEnd }, data.map(function (item, index) {
return (React.createElement("div", { key: "".concat(item.value).concat(index), style: { height: itemHeight }, onClick: function () { return handleItemClick(index); } }, item.label));
})))) : null;
};
export default PickerCell;
//# sourceMappingURL=pickerCell.js.map