uniapp 实现自定义缩略滚动条

<template>
  <view class="container-scroll">
    <!-- 文字导航 -->
    <scroll-view class="scroll-view-text" scroll-x="true" v-if="type === 1">
      <navigator
        :url="item.url"
        class="scroll-view-item"
        :style="`width:${itemWidth}px`"
        v-for="(item, index) in tarbarList"
        :key="index"
      >
        <!-- 必须得多包一层 -->
        <view class="text-container" :style="`width:${itemWidth}px`">
          <view class="text-nav">
            {{ item.title }}
          </view>
        </view>
      </navigator>
    </scroll-view>

    <!-- 图文导航 -->
    <scroll-view
      class="img-scroll-view"
      scroll-x="true"
      @scroll="handleScroll"
      ref="scrollView"
      v-if="type === 2"
    >
      <view class="scroll-view-container">
        <navigator
          :url="item.url"
          class="scroll-view-item"
          :style="`width:${itemWidth}px`"
          v-for="(item, index) in tarbarList"
          :key="index"
        >
          <view class="nav-item">
            <view class="image-container" :style="imageStyle">
              <image
                class="image"
                :src="item.image"
                mode="widthFix"
                lazy-load="false"
                :style="imageStyle"
              >
              </image>
            </view>
            <view class="text-title">
              {{ item.title }}
            </view>
          </view>
        </navigator>
      </view>
    </scroll-view>
  </view>

  <!-- 图文导航自定义滚动条 -->
  <view
    class="custom-scroll-container"
    v-if="type === 2 && showNumber < tarbarList.length"
  >
    <view class="custom-scroll">
      <view
        class="scroll"
        :style="`transform:translateX(${translateXValue}px)`"
      />
    </view>
  </view>
</template>

<script setup name="imageTextNavigator">
import { onMounted, ref, getCurrentInstance, computed } from "vue";
const props = defineProps({
  type: {
    // 1 图文导航 2 文字导航
    type: Number,
    default: 2,
  },
  // 首屏展示数量
  showNumber: {
    type: Number,
    default: 4,
  }, // 一屏显示的数量
  tarbarList: {
    type: Array,
    default: () => [
      {
        title: "导航导航导航导航",
        image: "",
        url: "",
      },
      {
        title: "导航导航导航导航",
        image: "",
        url: "",
      },
      {
        title: "导航导航导航导航",
        image: "",
        url: "",
      },
      {
        title: "导航导航导航导航",
        image: "",
        url: "",
      },
      {
        title: "导航5",
        image: "",
        url: "",
      },
      {
        title: "导航6",
        image: "",
        url: "",
      },
    ],
  },
});

const instance = getCurrentInstance();

// 活动距离
const translateXValue = ref(0);
const itemWidth = ref(0);

// 计算滚动距离
function handleScroll(event) {
  // 30 滑块父盒子的宽度
  // 8 滑块的宽度
  // scrollLeft 是 scroll-view  滚动的距离
  const scrollLeft = event.detail.scrollLeft; // 获取scroll-view滚动的距离
  const scrollWidth = event.detail.scrollWidth; // 获取scroll-view内容的总宽度
  const viewWidth = 375; // scroll-view的视口宽度

  // 滑块父容器的宽度为30,滑块的宽度为8,计算出滑块可移动的最大距离
  const maxTranslateX = 30 - 8; // 最大滑动距离 22

  // 计算滚动条的移动比例,将内容滚动的比例映射到滑块的移动范围内
  const moveRatio = maxTranslateX / (scrollWidth - viewWidth); // 滑块能够移动距离 比上 大容器能够滑动的距离,映射出 比率,最高就是 100% 嘛,等比换算

  // 计算滑块的实际位置,确保不会超过最大移动距离
  translateXValue.value = Math.min(scrollLeft * moveRatio, maxTranslateX);
}

// 图片样式
const imageStyle = computed(() => {
  return {
    width: props.showNumber === 4 ? `44px` : `36px`,
    height: props.showNumber === 4 ? `44px` : `36px`,
  };
});

function getElementInfo(id, context) {
  return new Promise((resolve, reject) => {
    let query = uni.createSelectorQuery().in(context);
    query
      .select(id)
      .boundingClientRect((rect) => {
        if (rect) {
          // 获取的元素都是 px 需要乘以 2
          resolve(rect);
        } else {
          reject();
        }
      })
      .exec();
  });
}

async function setNavItemWidth() {
  const eleInfo = await getElementInfo(".container-scroll", instance);
  const phoneWidth = eleInfo.width;
  itemWidth.value = phoneWidth / props.showNumber;
}

onMounted(async () => {
  await setNavItemWidth();
});
</script>

<style lang="scss" scoped>
.container-scroll {
  width: 750rpx;
  position: relative;
  .scroll-view-text {
    white-space: nowrap;
    width: 750rpx;
    display: flex;
    .scroll-view-item {
      display: inline-block;
      background: #ffffff;
      .text-container {
        width: 150rpx;
        height: 100%;
        display: flex;
        justify-content: center;
        align-items: center;
        .text-nav {
          width: 112rpx;
          font-weight: 400;
          font-size: 28rpx;
          color: #b2945e;
          line-height: 28rpx;
          font-style: normal;
          text-transform: none;
          white-space: break-spaces;
        }
      }
    }
  }
  .img-scroll-view {
    display: flex;
    flex-wrap: nowrap;
    white-space: nowrap;
    box-sizing: border-box;
    width: 750rpx;
    .scroll-view-container {
      //   padding-left: 64rpx;
      display: inline-block;
      .scroll-view-item {
        display: inline-block;
        min-height: 154rpx;
        background: #ffffff;
        text-align: center;
        .nav-item {
          width: 100%;
          height: 100%;
          display: flex;
          justify-content: center;
          align-items: center;
          flex-direction: column;
          .text-title {
            width: 112rpx;
            font-weight: 400;
            font-size: 28rpx;
            color: #333333;
            line-height: 28rpx;
            white-space: break-spaces;
            margin-top: 24rpx;
          }
          .image-container {
            display: flex;
            justify-content: center;
            align-items: center;
            .image {
              background: #f3f3f3;
              border-radius: 0px 0px 0px 0px;
            }
          }
        }
      }
    }
  }
}
// 两种方案 绝对定位 \ 使用translateX
.custom-scroll-container {
  width: 100%;
  display: flex;
  justify-content: center;
  align-items: center;
  padding-top: 24rpx;
  .custom-scroll {
    width: 60rpx;
    overflow: hidden;
    height: 6rpx;
    background: #f3f3f3;
    border-radius: 4px 4px 4px 4px;
    position: relative;
    .scroll {
      width: 16rpx;
      height: 6rpx;
      background: #b2945e;
      border-radius: 4px 4px 4px 4px;
      /* 初始位置 */
      transform: translateX(0rpx);
    }
  }
}
</style>

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

清云随笔

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值