Vue 实现商品详情多播图(点击轮播图)
公司有这种需求,搜了很多没找到,找到下面这个,但是这个链接不是很符合公司需求
部分内容也存在点问题,所以补充一下
经过不断对标各大厂商写出第二套方案
原文基础链接:https://blog.csdn.net/LONGYog/article/details/124341749
实现效果old:
点击左右按钮图片左右移动
点击小图片图片展示到大图
图片从第一张切换到最后一张、最后一张切换到第一张
实现效果new一:
点击左右按钮图片左右移动
点击小图片图片展示到大图
图片从第一张切换到最后一张、最后一张切换到第一张
修改计算方式
这个方案改造后存在一个问题,
当我切换之后点击之前的一张,之后再点击向右切换会继续移动,
这个目前还没去找解决办法,暂且搁置,
如果有好的想法,欢迎T我哈
屏幕录制 2024-09-05 093446
HTML代码:
<template>
<div class="head_flex">
<div class="head_flex_img">
<img :src="mainImgUrl" class="head_flex_img_top" />
<div class="head_flex_img_bottom">
<img src="@/assets/imgs/icon_left.png" alt="" class="arrow" @click="imgLeft()" />
<div class="goods_items">
<img
v-for="(item, index) in images"
:key="index"
:src="item"
class="goods_items_img"
:style="imgStyle"
@click="changeImg(item, index)"
:class="index == imgActiveIndex ? 'img_activeBorder' : ''"
/>
</div>
<img src="@/assets/imgs/icon_right.png" alt="" class="arrow" @click="imgRight()" />
</div>
</div>
</div>
</template>
JavaScript:
<script lang="ts" setup>
//记得替换下面图片的路径
import image from '@/assets/imgs/avatar.jpg'
import icon_success from '@/assets/imgs/icon_success.png'
import icon_error from '@/assets/imgs/icon_error.png'
import { ref, computed } from 'vue'
const images = ref([
image,
icon_success,
image,
icon_error,
image,
icon_success,
image,
icon_success,
image,
icon_error,
image,
icon_success,
image
])
const imgDistance = ref(0) // 当前移动图片的索引值
const imgActiveIndex = ref(0) // 移动的距离
const mainImgUrl = ref(images.value[imgDistance.value])
const imgStyle = computed(() => {
return {
transform: `translate3d(${imgDistance.value}px, 0, 0)` // 计算移动的距离(x,y,z)
}
})
const changeImg = (item, index) => {
mainImgUrl.value = item
imgActiveIndex.value = index
}
/*
先判断是否从第一张切换到最后一张 || 从最后一张切换到第一张
images.value.length - 4 这里减4是因为宽度限制一次只展示4张所以为4
newIndex < 42 这里42 是一张图片的占位数值
*/
const imgLeft = () => {
if (imgActiveIndex.value == 0) {
imgActiveIndex.value = images.value.length - 1
moveIndex('left', images.value.length - 4)
} else if (imgActiveIndex.value > 0) {
imgActiveIndex.value-- // 索引值-1
images.value.forEach((item, index) => {
if (imgActiveIndex.value === index) {
mainImgUrl.value = item
}
})
if (imgActiveIndex.value <= 9 && imgActiveIndex.value != 0) {
moveIndex('left')
}
}
}
const imgRight = () => {
if (imgActiveIndex.value < images.value.length - 1) {
imgActiveIndex.value++
images.value.forEach((item, index) => {
if (imgActiveIndex.value == index) {
mainImgUrl.value = item
}
})
if (imgActiveIndex.value >= 4) {
moveIndex('right')
}
} else if (imgActiveIndex.value == images.value.length - 1) {
imgActiveIndex.value = 0
mainImgUrl.value = images.value[0]
moveIndex('right', images.value.length - 4)
}
}
const moveIndex = (direction, val) => {
let newIndex = 0
const temp = window.setInterval(() => {
if (direction == 'right') {
if (val) {
if (newIndex < 42 * val) {
// 取绝对值再除
imgDistance.value += 1
newIndex++
return
} else {
window.clearInterval(temp)
}
} else {
if (newIndex < 42) {
// 取绝对值再除
imgDistance.value -= 1
newIndex++
return
} else {
window.clearInterval(temp)
}
}
} else {
if (val) {
if (newIndex < 42 * val) {
// 取绝对值再除
imgDistance.value -= 1
newIndex++
return
} else {
window.clearInterval(temp)
}
} else {
if (newIndex < 42) {
// 取绝对值再除
imgDistance.value += 1
newIndex++
return
} else {
window.clearInterval(temp)
}
}
}
}, 1)
}
</script>
css:
<style lang="less" scoped>
.head_flex {
display: flex;
justify-content: space-between;
margin-bottom: 50px;
&_img {
width: 198px;
height: 198px;
border-radius: 4px;
&_top {
width: 100%;
height: 100%;
background-color: #f00;
}
}
}
.head_flex_img_bottom {
width: 198px;
display: flex;
align-items: center;
justify-content: space-between;
margin-top: 12px;
}
.arrow {
width: 14px;
height: 14px;
}
.goods_items {
width: 300px;
height: 36px;
display: flex;
align-items: center;
overflow: hidden;
.goods_items_img {
width: 36px;
height: 36px;
margin-right: 6px;
border-radius: 4px;
padding: 2px;
box-sizing: border-box;
}
}
.img_activeBorder {
border: 1px solid #1762ff;
}
</style>
实现效果new二:
点击左右选中不切换,改为鼠标移入切换选中状态
点击左右,右侧隐藏图片展示
HTML代码:
<template>
<div class="head_flex">
<div class="head_flex_img">
<img :src="mainImgUrl" class="head_flex_img_top" />
<div class="head_flex_img_bottom">
<img src="@/assets/imgs/icon_left.png" alt="" class="arrow" @click="imgLeft()" />
<div class="goods_items">
<div class="goods_item_slick_width" :style="imgStyle">
<div
v-for="(item, index) in data.goods_photo"
:key="index"
:ariaHidden="index > 3 ? false : true"
class="goods_item_slick"
:class="{
'slick-current': noneActionIndex === index
}"
>
<img
:src="item"
@mouseover="handleMouseOver(item, index)"
class="goods_items_img"
:class="imgActiveIndex == index ? 'img_activeBorder' : ''"
:style="{}"
/>
</div>
</div>
</div>
<img src="@/assets/imgs/icon_right.png" alt="" class="arrow" @click="imgRight()" />
</div>
</div>
</div>
</template>
JavaScript:
<script lang="ts" setup>
import { ref, computed } from 'vue'
const imgDistance = ref(0) // 移动的距离
const imgStyle = computed(() => {
return {
opacity: 1,
transform: `translate3d(${imgDistance.value}px, 0, 0)`,
transition: ' -webkit-transform 200ms' // 计算移动的距离(x,y,z)
}
})
const handleMouseOver = (item, index) => {
mainImgUrl.value = item
imgActiveIndex.value = index
}
//先计算出向右点击能移动几次 -4 是一页设计只能展示4个
const onRightClickNum = computed(() => data.value.goods_photo.length - 4) //点击次数原始数值
const newonRightClickNum = ref(data.value.goods_photo.length - 4) //计算点击次数数值
const imgLeft = () => {
// 计算出可以点击的差额,再减去剩余次数,自增恢复到原始差额
//当前点击的次数 小于 点击总次数时 让其自增
if (newonRightClickNum.value < onRightClickNum.value) {
newonRightClickNum.value++
}
if (noneActionIndex.value === 0) return
noneActionIndex.value--
if (noneActionIndex.value <= onRightClickNum.value - 1 && noneActionIndex.value >= 0) {
imgDistance.value += 46
}
}
const imgRight = () => {
const lastIndex = data.value.goods_photo.length - 1
// 判断是否在最后一个隐藏节点
if (noneActionIndex.value === lastIndex) return
// 增加隐藏节点索引
if (noneActionIndex.value < lastIndex) {
noneActionIndex.value++
}
// 如果当前点击次数不为0且非最后一个节点,隐藏选中状态继续自增
if (newonRightClickNum.value !== 0 && noneActionIndex.value < lastIndex) {
newonRightClickNum.value--
imgDistance.value -= 46
}
}
</script>
css:
<style lang="less" scoped>
.head_flex {
display: flex;
justify-content: space-between;
margin-bottom: 50px;
&_img {
width: 198px;
height: 198px;
&_top {
width: 100%;
height: 100%;
border-radius: 4px;
}
}
}
.head_flex_img_bottom {
width: 198px;
display: flex;
align-items: center;
justify-content: space-between;
margin-top: 12px;
}
.arrow {
width: 14px;
height: 14px;
}
.goods_items {
width: 168px;
height: 36px;
white-space: nowrap;
overflow: hidden;
position: relative;
.goods_item_slick_width {
width: 900px;
}
.goods_item_slick {
display: inline-block;
width: 36px;
height: 36px;
margin-right: 8px;
}
.goods_items_img {
width: 36px !important;
height: 36px !important;
border-radius: 4px;
padding: 2px;
box-sizing: border-box;
}
}
.img_activeBorder {
border: 1px solid #1762ff;
}
.head_flex_content {
flex: 1;
padding-left: 20px;
box-sizing: border-box;
.flex_content_top_title {
font-weight: bold;
font-size: 18px;
color: #333333;
}
.flex_content_top_other {
display: flex;
align-items: center;
margin-top: 16px;
color: #333;
font-size: 16px;
}
.other_label {
color: #a4a4a4;
}
}
.flex_content_data {
display: flex;
align-items: center;
margin-top: 26px;
.flex_content_data_item {
margin-right: 48px;
.data_item_top {
font-size: 16px;
color: #a4a4a4;
}
.data_item_num {
display: flex;
align-items: center;
height: 22px;
line-height: 22px;
font-size: 21px;
color: #333333;
font-weight: bold;
margin-top: 16px;
}
}
}
.data_sku {
display: inline-block;
width: 44px;
background: #fff2dd;
border-radius: 4px;
text-align: center;
font-size: 12px;
color: #ffb842;
margin-left: 10px;
}
</style>