仿淘宝商品详情页的轮播

实现放淘宝详情页商品介绍

博客园不支持上传视频,或者我没找到上传视频的位置,只能放几张图片配上注释介绍下实现的效果。

 蓝色文字,左侧轮播写错了,应该是右侧轮播,依次在左侧放大显示。

 

一、轮播下的按钮,比较简单也说下怎么做的

 

 <div class="indicators-bottom">
     <div class="select-tabs">
        <div :class="['common-tab', activeTab === 'video' ? 'acvtive-tab' : 'normal-tab']" @click="selectTab('video')">视频</div>
        <div :class="['common-tab', activeTab === 'pic' ? 'acvtive-tab' : 'normal-tab']" @click="selectTab('pic')">图文</div>
     </div>
 </div>

样式

 .indicators-bottom{
                display: flex;
                justify-content: center;
                margin-top: 16px;
                width: 460px;
            }
            .select-tabs{
                @extend .flex-center;
                width: 123px;
                height: 36px;
                border-radius: 6px;
                background-color: #EAEFF5;
                .common-tab{
                    width: 60px;
                    height: 30px;
                    border-radius: 6px;
                    text-align: center;
                    line-height: 30px;
                    font-size: 14px ;
                    cursor: pointer;
                }
                .acvtive-tab{
                    background-color: #fff;
                    color: #161B2C;
                    font-weight: 500;
                }
                .normal-tab{
                    color: #86909C;
                    font-weight: 400;
                }
            }
tabs的scss样式

方法

// 选择活跃方式
const selectTab = (val) => {
    activeTab.value = val
    if(val === 'pic') {
        resStartAutoPlay()  // 如果选择图文,开始轮播
    } else if(val === 'video') {
        scrollY.value = 0  // 滚回最上面
        currentIndex.value = -1  // 左侧的活跃index为-1,图文从0开始
        stopAutoPlay() // 如果是视频,停止轮播
    }
}

二、左侧回显视频以及图片的代码

 <div class="carousel-inner" :style="innerStyle">
       <div v-if="activeTab === 'video'">
              <video :src="workDetailData.fileVideo" controls autoplay class="video"></video>
       </div>
       <template v-else-if="activeTab === 'pic'">
            <div
               class="carousel-item"
               v-for="(item, index) in imgList"
               :key="`carousel${item.id}${index}`"
           >
                <img v-if="item.showGoodsCover" :src="item.url" class="carousel-item-img" @error="imgRecommondLoad($event, item)"/>
                <view class="default-goodspic-img" v-else>
                     <img src="/images/common/no-cover-img.png" class="img" />
                </view>
          </div>
      </template>
</div>

样式

.carousel-inner {
                display: flex;
                width: 460px;
                height: 460px;
                .carousel-item {
                    min-width: 100%;
                    box-sizing: border-box;
                    background-size: 100% 100%;
                    .carousel-item-img{
                        width: 460px;
                        height: 460px;
                        object-fit: cover;
                        border-radius: 20px;
                    }
                    .line {
                        width: 70px;
                        height: 16px;
                        background: #fff;
                    }
                    .carousel-index {
                        font-family: DIN;
                        font-size: 130px;
                        font-weight: 600;
                        color: #fff;
                        margin: 20px 0 40px 0;
                    }
                    .carousel-label {
                        font-family: Source Han Sans SC;
                        font-weight: 500;
                        color: #fff;
                    }
                    .default-goodspic-img{
                        width: 460px;
                        height: 460px;
                        background-color: #E6E9F0;
                        @extend .flex-center;
                        .img{
                             width: 160px;
                            height: 104px;
                            object-fit: cover;
                        }
                    }
                }
            }
View Code

元素滚动的关键配置!!!!

const innerStyle = computed(() => {
    return {
        transform: `translateX(-${currentIndex.value == -1 ? 0 : currentIndex.value * 100}%)`,
        transition: `transform 0.3s ease-in-out`,
    };
});

左侧显示涉及到的方法

// 轮播作品封面加载错误提示
const imgRecommondLoad = (e, data) => {
    data.showGoodsCover = false;
}

三、轮播图列表以及按钮控件的代码

 <div class="goodspic-right" >
       <div class="common-move up" @click="scrollUp" v-if="scrollY < 0">
            <img src="/images/marketplace/icon/up.png" class="move-img" />
       </div>
       <div ref="imageContainer" class="image-wrapper" :style="{ transform: `translateY(${scrollY}px)` }">
             <div v-if="workDetailData.fileVideo && workDetailData.fileVideo !== ''" :class="['right-item', currentIndex === -1 ? 'active-item' : '']">
                 <img :src="workDetailData.coverUrl" class="carousel-item-img" @mouseover="hoverfirstVideo"/>
             </div>
             <div
                v-for="(item, index) in imgList"
                :key="`right${item.id}${index}`"
                :class="['right-item', currentIndex === index ? 'active-item' : '']"
             >
                   <img v-if="item.showGoodsCover" :src="item.url" class="carousel-item-img" @error="imgRecommondLoad($event, item)" @mouseover="hoverGoodPic(index)" @mouseout="mouseoutGoodPic"/>
                   <div class="default-right-img" v-else>
                        <img src="/images/common/no-cover-img.png" class="img" />
                   </div>
              </div>
        </div>
       <div class="common-move down" @click.stop="scrollDown" v-if="!isLastImage()">
             <img src="/images/marketplace/icon/down.png" class="move-img" />
       </div>
  </div>

首先上下两个按钮的显示时机,当悬浮到列表上,列表超出当前范围高度时,显示向下的按钮,如果滚到底部则不显示;当列表向下滚动时,才会出现向上的按钮,滚到顶部,向上的按钮不出现。

// hover轮播图片 
const hoverGoodPic = (index) => {
    currentIndex.value = index;
    activeTab.value = 'pic'
    stopAutoPlay()
}
// out轮播图片
const mouseoutGoodPic = (index) => {
    startAutoPlay()
}

 向上或者向下按钮事件

const scrollDown = () => {
    
    const newScrollY = scrollY.value - 2 * imageHeightWithGap;
    const maxScroll = -(imgList.value.length - 2) * imageHeightWithGap;
    scrollY.value = Math.max(newScrollY, maxScroll);
    console.log('scrollDown',  scrollY.value);
}
const scrollUp = () => {
    const newScrollY = scrollY.value + 2 * imageHeightWithGap;
    scrollY.value = Math.min(newScrollY, 0);
}

判断是否最后一张图片

const isLastImage = () => {
    // console.log('?????????', -(imgList.value.length - 1) * imageHeightWithGap, scrollY.value <= -(imgList.value.length - 1) * imageHeightWithGap);
    if(imgList.value.length <= 4) {
        return true;
    }
    return scrollY.value <= -(imgList.value.length - 3) * imageHeightWithGap;
};

计算滚动的距离

const imageContainer = ref(null)
const scrollY = ref(0);
const showDownBtn = ref(false);
const showUpBtn = ref(false);
const imgList = ref([])
const imageHeightWithGap = 100 + 20;
// 检查元素距离底部的距离,计算需要滚动的距离
const checkScrollPosition = () => {
                    const scrollContainerHeight = 460;
                    const threshold = 120;
                    let imageBottom = 0;
                    
                    // 计算选中图片底部到容器底部的距离
                    if(workDetailData.value.fileVideo && workDetailData.value.fileVideo !== '') {
                        imageBottom = (currentIndex.value + 2) * imageHeightWithGap;
                    } else {
                        imageBottom = (currentIndex.value + 1) * imageHeightWithGap;
                    }
                    const distanceToBottom = scrollContainerHeight - imageBottom;
                    
                    // 如果选中图片距离底部小于阈值,则滚动
                    if (distanceToBottom < threshold) {
                        // 计算需要滚动的距离
                        scrollY.value = -(imageBottom - scrollContainerHeight + threshold);
                    } else if (currentIndex.value * imageHeightWithGap < threshold) {
                        // 如果选中图片距离顶部小于阈值,则向上滚动
                        scrollY.value = 0
                    }
}

function startAutoPlay() {
    autoPlayInterval.value = setInterval(() => {
        const len = imgList.value.length
        currentIndex.value = (currentIndex.value + 1) % len;
        checkScrollPosition()
    }, 3000);
}
const resStartAutoPlay = () => {
    currentIndex.value = 0
    startAutoPlay()
}

function stopAutoPlay() {
    clearInterval(autoPlayInterval.value);
}

 

posted @ 2025-07-02 20:39  king'sQ  阅读(26)  评论(0)    收藏  举报