feat: tab 的点击交互
This commit is contained in:
Binary file not shown.
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 4.4 KiB |
@@ -21,7 +21,7 @@ import SpriteAnimator from "@/components/Sprite/SpriteAnimator.vue";
|
|||||||
import NoticeMessage from "../NoticeMessage/index.vue";
|
import NoticeMessage from "../NoticeMessage/index.vue";
|
||||||
import { getLocalWeather } from "@/request/api/MainPageDataApi";
|
import { getLocalWeather } from "@/request/api/MainPageDataApi";
|
||||||
|
|
||||||
const weatherText = ref("26°C ☀️");
|
const weatherText = ref('');
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
mainPageDataModel: {
|
mainPageDataModel: {
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 822 B After Width: | Height: | Size: 3.3 KiB |
@@ -1,11 +1,17 @@
|
|||||||
<template>
|
<template>
|
||||||
<view class="find-tabs-wrapper">
|
<view class="find-tabs-wrapper">
|
||||||
<scroll-view class="tabs-scroll" scroll-x="true" :scroll-into-view="'tab-' + modelValue" scroll-with-animation="true">
|
<scroll-view
|
||||||
|
class="tabs-scroll"
|
||||||
|
scroll-x="true"
|
||||||
|
:scroll-left="scrollLeft"
|
||||||
|
scroll-with-animation="true"
|
||||||
|
@scroll="handleScroll"
|
||||||
|
>
|
||||||
<view class="tabs-list">
|
<view class="tabs-list">
|
||||||
<view
|
<view
|
||||||
v-for="(tab, idx) in tabs"
|
v-for="(tab, idx) in tabs"
|
||||||
:key="idx"
|
:key="idx"
|
||||||
:id="'tab-' + idx"
|
:id="getTabId(idx)"
|
||||||
class="tab-item"
|
class="tab-item"
|
||||||
:class="{ active: modelValue === idx }"
|
:class="{ active: modelValue === idx }"
|
||||||
@tap="handleSwitch(tab, idx)"
|
@tap="handleSwitch(tab, idx)"
|
||||||
@@ -23,10 +29,13 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
|
import { getCurrentInstance, nextTick, onMounted, ref, watch } from "vue";
|
||||||
import { isZhiNian } from "@/constant/base";
|
import { isZhiNian } from "@/constant/base";
|
||||||
import indicatorSrc from "./images/selected_tabs_icon.png";
|
import indicatorSrc from "./images/selected_tabs_icon.png";
|
||||||
import indicatorSrcB from "./images/selected_tabs_icon_b.png";
|
import indicatorSrcB from "./images/selected_tabs_icon_b.png";
|
||||||
|
|
||||||
|
const instance = getCurrentInstance();
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
modelValue: { type: Number, default: 0 },
|
modelValue: { type: Number, default: 0 },
|
||||||
tabs: {
|
tabs: {
|
||||||
@@ -37,11 +46,56 @@ const props = defineProps({
|
|||||||
|
|
||||||
const emit = defineEmits(['update:modelValue', 'change']);
|
const emit = defineEmits(['update:modelValue', 'change']);
|
||||||
|
|
||||||
|
const scrollLeft = ref(0);
|
||||||
|
let currentScrollLeft = 0;
|
||||||
|
|
||||||
|
const getTabId = (idx) => `find-tab-${idx}`;
|
||||||
|
|
||||||
|
const handleScroll = (e) => {
|
||||||
|
currentScrollLeft = e.detail?.scrollLeft || 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
const centerActiveTab = async () => {
|
||||||
|
await nextTick();
|
||||||
|
|
||||||
|
const activeIndex = props.modelValue;
|
||||||
|
if (activeIndex < 0 || activeIndex >= props.tabs.length) return;
|
||||||
|
if (!instance || typeof uni === "undefined" || !uni.createSelectorQuery) return;
|
||||||
|
|
||||||
|
const query = uni.createSelectorQuery().in(instance);
|
||||||
|
query.select(".tabs-scroll").boundingClientRect();
|
||||||
|
query.select(".tabs-scroll").scrollOffset();
|
||||||
|
query.select(`#${getTabId(activeIndex)}`).boundingClientRect();
|
||||||
|
query.exec((res) => {
|
||||||
|
const [scrollRect, scrollOffset, activeRect] = res || [];
|
||||||
|
if (!scrollRect || !activeRect) return;
|
||||||
|
|
||||||
|
const currentLeft = scrollOffset?.scrollLeft ?? currentScrollLeft;
|
||||||
|
const targetScrollLeft =
|
||||||
|
currentLeft +
|
||||||
|
activeRect.left - scrollRect.left -
|
||||||
|
(scrollRect.width - activeRect.width) / 2;
|
||||||
|
|
||||||
|
scrollLeft.value = Math.max(0, targetScrollLeft);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const handleSwitch = (tab, idx) => {
|
const handleSwitch = (tab, idx) => {
|
||||||
emit('update:modelValue', idx);
|
emit('update:modelValue', idx);
|
||||||
emit('change', { tab, idx });
|
emit('change', { tab, idx });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => [props.modelValue, props.tabs.length],
|
||||||
|
() => {
|
||||||
|
centerActiveTab();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
centerActiveTab();
|
||||||
|
});
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
|||||||
@@ -46,10 +46,11 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.tab-text {
|
.tab-text {
|
||||||
|
position: relative;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
color: rgba(128, 140, 153, 0.9);
|
color: rgba(128, 140, 153, 0.9);
|
||||||
z-index: 5;
|
z-index: 2;
|
||||||
padding: 0 4px;
|
padding: 0 4px;
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
}
|
}
|
||||||
@@ -66,5 +67,5 @@
|
|||||||
transform: translateX(-50%);
|
transform: translateX(-50%);
|
||||||
width: 56px;
|
width: 56px;
|
||||||
height: auto;
|
height: auto;
|
||||||
z-index: 6;
|
z-index: 1;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user