@morjs/runtime-web
Version:
mor runtime for web
222 lines (220 loc) • 8.09 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const tslib_1 = require("tslib");
const lit_element_1 = require("lit-element");
const class_map_1 = require("lit-html/directives//class-map");
const style_map_1 = require("lit-html/directives/style-map");
const baseElement_1 = require("../../baseElement");
const rpx_1 = require("../../rpx");
const constants_1 = require("./constants");
const index_style_1 = require("./index.style");
const utils_1 = require("./utils");
class Picker extends baseElement_1.BaseElement {
constructor() {
super(...arguments);
this.startY = 0;
this.movedY = 0;
this.lastMovedY = 0;
this.lastIndex = 0;
this.showPicker = false;
// 通过切换transform属性实现滚动效果
this.styles = { transform: 'translate3d(0, 0, 0)' };
this.title = '';
this.show = false;
this.range = [];
this['range-key'] = '';
this.value = 0;
this.disabled = false;
this.confirmText = '确定';
this.cancelText = '取消';
}
connectedCallback() {
super.connectedCallback();
// 默认打开逻辑
if (this.show) {
this.togglePicker(true);
}
this.lastIndex = this.value;
}
static get styles() {
return index_style_1.PickerStyles;
}
onPickerClick() {
if (this.disabled)
return;
this.togglePicker(true);
}
togglePicker(isShow = false) {
if (isShow) {
// 解决滚动穿透
(0, utils_1.fixedBody)(document);
// 计算传入的index所在位置,提前记录位置并滚动到该区域
this.lastMovedY = -((this.value * constants_1.ITEM_HEIGHT) / 2);
this.move();
}
else {
// picker关闭时解除滚动传动限制
(0, utils_1.looseBody)(document);
}
this.showPicker = isShow;
}
onTouchStart(event) {
// 记录起始位置,方便后续计算滚动距离
const { y } = (0, utils_1.getPosition)(event);
this.startY = y;
}
onTouchMove(event) {
const { y } = (0, utils_1.getPosition)(event);
this.movedY = y - this.startY;
this.move(this.movedY);
}
onTouchEnd() {
this.startY = 0;
// 计算最新的偏移距离
const lastMovedY = (this.lastMovedY = this.lastMovedY + this.movedY);
const height = (constants_1.ITEM_HEIGHT / 2) * (this.range.length - 1);
// 如果滚动距离超出范围,做边界处理
if (Math.abs(lastMovedY) > height || lastMovedY > 0) {
this.lastMovedY = lastMovedY > 0 ? 0 : -height;
this.move();
}
else {
// 计算当前滚动距离离哪个元素更近
const index = this.getClosestIndex(lastMovedY);
// 计算出下标并滚动以确保滚动距离始终为 ITEM_HEIGHT 的倍数
this.lastMovedY = -((index * constants_1.ITEM_HEIGHT) / 2);
this.move();
}
}
move(moveY = 0) {
// 将当前计算的距离转换成rem保证各分辨率的适配
const value = (0, rpx_1.rpxToRem)(2 * (moveY + this.lastMovedY));
this.styles = { transform: `translate3d(0, ${value}, 0)` };
}
// 根据当前滚动距离获取最近的节点
getClosestIndex(distance) {
// 将 rpx 单位转换成rem做计算用于适配
const itemHeight = parseFloat((0, rpx_1.rpxToRem)(constants_1.ITEM_HEIGHT));
const currentHeight = Math.abs(parseFloat((0, rpx_1.rpxToRem)(2 * distance)));
const num = parseInt(currentHeight / itemHeight + '');
const extra = (currentHeight - itemHeight * num) / itemHeight;
// 判断多出的距离是否大于边界因子(0.4效果比较好),如果大于就滚动到下一个元素
if (extra >= constants_1.RATE)
return num + 1;
return num;
}
dispatch(eventName, data) {
this.dispatchEvent(new CustomEvent(eventName, {
detail: data,
bubbles: true,
composed: true
}));
}
onConfirm() {
const index = this.getClosestIndex(this.lastMovedY);
if (index === this.lastIndex) {
this.dispatch('no-change', { value: index });
return;
}
this.value = index;
this.lastIndex = index;
this.dispatch('change', { value: index });
this.togglePicker();
}
onCancel() {
this.dispatch('cancel', { value: this.value });
this.togglePicker();
}
getName(item) {
return typeof item === 'object' ? item[this['range-key']] : item;
}
render() {
const classes = { 'picker-container--show': this.showPicker };
return (0, lit_element_1.html) `
<slot ="${this.onPickerClick}"></slot>
<slot name="picker"> </slot>
<div class="picker-container ${(0, class_map_1.classMap)(classes)}">
<div class="picker-mask"></div>
<div
class="picker-main ${(0, utils_1.isIos)()
? 'picker-main--ios'
: 'picker-main--android'}"
>
<div class="picker-main-header">
${(0, utils_1.isIos)()
? (0, lit_element_1.html) `<p class="picker-btn" ="${this.onCancel}">
${this.cancelText}
</p>`
: null}
${this.title
? (0, lit_element_1.html) `<p class="picker-title">${this.title}</p>`
: null}
${(0, utils_1.isIos)()
? (0, lit_element_1.html) `<p class="picker-btn" ="${this.onConfirm}">
${this.confirmText}
</p>`
: null}
</div>
<div
class="picker-main-content"
="${this.onTouchStart}"
="${this.onTouchMove}"
="${this.onTouchEnd}"
>
<div class="picker-content-mask"></div>
<div class="picker-content-indicator"></div>
<div class="picker-content-list" style="${(0, style_map_1.styleMap)(this.styles)}">
${this.range.map((item) => (0, lit_element_1.html) `
<div class="picker-content-item">${this.getName(item)}</div>
`)}
</div>
</div>
${!(0, utils_1.isIos)()
? (0, lit_element_1.html) `
<footer class="picker-footer-group">
<p class="picker-btn-android" ="${this.onCancel}">
取消
</p>
<p class="picker-btn-android" ="${this.onConfirm}">
确定
</p>
</footer>
`
: null}
</div>
</div>
`;
}
}
tslib_1.__decorate([
(0, lit_element_1.internalProperty)()
], Picker.prototype, "showPicker", void 0);
tslib_1.__decorate([
(0, lit_element_1.internalProperty)()
], Picker.prototype, "styles", void 0);
tslib_1.__decorate([
(0, lit_element_1.property)({ type: String })
], Picker.prototype, "title", void 0);
tslib_1.__decorate([
(0, lit_element_1.property)({ type: Boolean })
], Picker.prototype, "show", void 0);
tslib_1.__decorate([
(0, lit_element_1.property)({ type: Array })
], Picker.prototype, "range", void 0);
tslib_1.__decorate([
(0, lit_element_1.property)({ type: String })
], Picker.prototype, 'range-key', void 0);
tslib_1.__decorate([
(0, lit_element_1.property)({ type: Number, reflect: true })
], Picker.prototype, "value", void 0);
tslib_1.__decorate([
(0, lit_element_1.property)({ type: Boolean })
], Picker.prototype, "disabled", void 0);
tslib_1.__decorate([
(0, lit_element_1.property)()
], Picker.prototype, "confirmText", void 0);
tslib_1.__decorate([
(0, lit_element_1.property)()
], Picker.prototype, "cancelText", void 0);
exports.default = Picker;
//# sourceMappingURL=index.js.map