import { VantComponent } from '../common/component'; import { touch } from '../mixins/touch'; import { getAllRect, getRect, groupSetData, nextTick, requestAnimationFrame, } from '../common/utils'; import { isDef } from '../common/validator'; VantComponent({ mixins: [touch], classes: ["nav-class", "tab-class", "tab-active-class", "line-class"], relation: { name: "tab", type: "descendant", current: "tabs", linked(target) { target.index = this.children.length - 1; this.updateTabs(); }, unlinked() { this.children = this.children.map((child, index) => { child.index = index; return child; }); this.updateTabs(); } }, props: { sticky: Boolean, border: Boolean, swipeable: Boolean, titleActiveColor: String, titleInactiveColor: String, color: String, animated: { type: Boolean, observer() { this.children.forEach((child, index) => child.updateRender(index === this.data.currentIndex, this) ); } }, lineWidth: { type: [String, Number], value: 40, // observer: "resize" }, lineLengthType: { type: [String, Number], value: 1 // 1默认自适应 2短自适应 3 固定宽度取值lineWidth }, linePosition: { type: [String, Number], value: 1 // 1默认右对齐 2居中 3铺满 }, lineHeight: { type: [String, Number], value: -1 }, active: { type: [String, Number], value: 0, observer(name) { if (name !== this.getCurrentName()) { this.setCurrentIndexByName(name); } } }, type: { type: String, value: "line" }, ellipsis: { type: Boolean, value: true }, duration: { type: Number, value: 0.3 }, zIndex: { type: Number, value: 1 }, swipeThreshold: { type: Number, value: 5, observer(value) { this.setData({ scrollable: this.children.length > value || !this.data.ellipsis }); } }, offsetTop: { type: Number, value: 0 }, lazyRender: { type: Boolean, value: true } }, data: { tabs: [], scrollLeft: 0, scrollable: false, currentIndex: 0, container: null, skipTransition: true, lineOffsetLeft: 0 }, mounted() { this.getElementInfo(); requestAnimationFrame(() => { this.setData({ container: () => this.createSelectorQuery().select(".van-tabs") }); this.resize(true); this.scrollIntoView(); }); }, methods: { getElementInfo() { let that = this; wx .createSelectorQuery() .in(this) .selectAll(".van-tab") .boundingClientRect() .exec((rect = []) => that.triggerEvent("getEleInfo", { rect, vanTabsThis: this }) ); }, updateTabs() { const { children = [], data } = this; this.setData({ tabs: children.map(child => child.data), scrollable: this.children.length > data.swipeThreshold || !data.ellipsis }); this.setCurrentIndexByName(data.active || this.getCurrentName()); }, trigger(eventName, child) { const { currentIndex } = this.data; const currentChild = child || this.children[currentIndex]; if (!isDef(currentChild)) { return; } this.$emit(eventName, { index: currentChild.index, name: currentChild.getComputedName(), title: currentChild.data.title }); }, onTap(event) { const { index } = event.currentTarget.dataset; const child = this.children[index]; if (child.data.disabled) { this.trigger("disabled", child); } else { this.setCurrentIndex(index); nextTick(() => { this.trigger("click"); }); } }, // correct the index of active tab setCurrentIndexByName(name) { const { children = [] } = this; const matched = children.filter( child => child.getComputedName() === name ); if (matched.length) { this.setCurrentIndex(matched[0].index); } }, setCurrentIndex(currentIndex) { const { data, children = [] } = this; if ( !isDef(currentIndex) || currentIndex >= children.length || currentIndex < 0 ) { return; } groupSetData(this, () => { children.forEach((item, index) => { const active = index === currentIndex; if (active !== item.data.active || !item.inited) { item.updateRender(active, this); } }); }); if (currentIndex === data.currentIndex) { return; } const shouldEmitChange = data.currentIndex !== null; this.setData({ currentIndex }); nextTick(() => { this.resize(); this.scrollIntoView(); this.trigger("input"); if (shouldEmitChange) { this.trigger("change"); } }); }, getCurrentName() { const activeTab = this.children[this.data.currentIndex]; if (activeTab) { return activeTab.getComputedName(); } }, resize(skipTransition = false) { console.log("resize") if (this.data.type !== "line") { return; } const { currentIndex, ellipsis } = this.data; Promise.all([ getAllRect(this, ".van-tab"), getRect(this, ".van-tabs__line") ]).then(([rects = [], lineRect]) => { const rect = rects[currentIndex]; if (rect == null) { return; } let lineWidths = 0; let lineOffsetLeft = 0; if (this.data.lineLengthType == 1) { if (this.data.linePosition == 2) { lineOffsetLeft = rect.left; } else { lineOffsetLeft = rects .slice(0, currentIndex) .reduce((prev, curr) => prev + curr.width, 0); lineOffsetLeft += 8; } lineWidths = rect.width; } else if (this.data.lineLengthType == 2) { // 宽度*80% if (this.data.linePosition == 2) { lineOffsetLeft = rect.left+rect.width*0.2; } else { lineOffsetLeft = rects .slice(0, currentIndex) .reduce((prev, curr) => prev + curr.width, 0); lineOffsetLeft += rect.width * 0.2; lineOffsetLeft += 8; } lineWidths = rect.width * 0.6; } else { // 有问题暂不支持固定宽度 // lineOffsetLeft = rects // .slice(0, currentIndex) // .reduce((prev, curr) => prev + curr.width, 0); // lineOffsetLeft += this.data.lineWidth / 2; // lineWidths = this.data.lineWidth; } this.setData({ lineWidth: lineWidths, lineOffsetLeft, skipTransition }); }); }, // scroll active tab into view scrollIntoView() { const { currentIndex, scrollable } = this.data; if (!scrollable) { return; } Promise.all([ getAllRect(this, ".van-tab"), getRect(this, ".van-tabs__nav") ]).then(([tabRects, navRect]) => { const tabRect = tabRects[currentIndex]; const offsetLeft = tabRects .slice(0, currentIndex) .reduce((prev, curr) => prev + curr.width, 0); this.setData({ scrollLeft: offsetLeft - (navRect.width - tabRect.width) / 2 }); }); }, onTouchScroll(event) { this.$emit("scroll", event.detail); }, onTouchStart(event) { if (!this.data.swipeable) return; this.touchStart(event); }, onTouchMove(event) { if (!this.data.swipeable) return; this.touchMove(event); }, // watch swipe touch end onTouchEnd() { if (!this.data.swipeable) return; const { direction, deltaX, offsetX } = this; const minSwipeDistance = 50; if (direction === "horizontal" && offsetX >= minSwipeDistance) { const index = this.getAvaiableTab(deltaX); if (index !== -1) { this.setCurrentIndex(index); } } }, getAvaiableTab(direction) { const { tabs, currentIndex } = this.data; const step = direction > 0 ? -1 : 1; for ( let i = step; currentIndex + i < tabs.length && currentIndex + i >= 0; i += step ) { const index = currentIndex + i; if ( index >= 0 && index < tabs.length && tabs[index] && !tabs[index].disabled ) { return index; } } return -1; } } });