鼠标移动区域,图片局部放大、点击左右按钮可以进行图片切换、鼠标悬停到图片上面,下面小图和主图自动切换

功能:

1、鼠标移动区域,图片局部放大
2、点击左右按钮可以进行图片切换
3、鼠标悬停到图片上面,下面小图和主图自动切换

效果图:

 

完整代码:

<template>
    <div
        class="productCarousels"
        :style="width"
        @mouseleave="flag = false"
    >
        <div
            class="exhibitionArea"
            :class="{noPadding:productImgArr.length===0}"
            @mouseenter="animation"
            @mouseleave="mouseleave"
        >
            <img v-if="productImgArr.length!==0 && !props.localAmplifica" :src="productImgArr[currentIndex]" class="nolocalAmplifica">
            <div
                v-if="props.localAmplifica && isHaveData"
                class="imgBigShow"
                :style="{width:imgBox.width+'px',height:imgBox.height+'px'}"
                ref="moveDom"
            >
                <img
                    v-if="productImgArr.length!==0" :src="productImgArr[currentIndex]"
                    :class="{ imgBorder: flag }"
                    class="moveImgDom"
                >
                <div
                    v-if="flag"
                    class="moveBorder"
                    :style="{
                        width:moveBorder.width+'px',
                        height:moveBorder.height+'px',
                        left: styleObj.left + 'px',
                        top: styleObj.top + 'px',
                    }"
                ></div>
            </div>
            <h4 v-if="isShowItem&&productImgArr.length===0" style="margin: 60px auto;color: #909399;">暂无图片</h4>
            <div class="noImgData" v-if="!isShowItem">
                <div>{{props.title}}</div>
                <img :src="bj_t" alt="">
            </div>
            <div class="leftNav" v-if="productImgArr.length>1" title="上一张" @click="last"><img :src="zb"></div>
            <div class="rightNav" v-if="productImgArr.length>1" title="下一张" @click="next"><img :src="yb"></div>
        </div>
        <div class="imgItem" v-if="isShowItem">
            <div
                v-if="productImgArr.length!==0"
                v-for="(item,index) in productImgArr"
                :key="index"
                class="imgItemBox"
                :class="{currentItem: currentIndex==index,lastImg:animate}"
                @mouseenter="currentPto(index)"
            ><img :src="item"></div>
            <h4 v-if="productImgArr.length===0" style="margin: 0 auto;color: #909399;">该商家尚未上传设备!</h4>
        </div>
        <div class="pop-image" v-if="flag" :style="{width:popImageBox.width + 'px',height:popImageBox.height + 'px',left: popImageBox.left + 'px'}">
            <img
                :src="productImgArr[currentIndex]" alt=""
                :style="{
                    width:popImageStyle.width + 'px',
                    height:popImageStyle.height + 'px',
                    top:'-'+(styleObj.top*4) + 'px',
                    left:'-'+(styleObj.left*4) + 'px',
                }"
            >
        </div>
    </div>
</template>

<script setup name="productCarousels">
import zb from '@/assets/images/device/zb.png'
import yb from '@/assets/images/device/yb.png'
import bj_t from '@/assets/images/device/bj_t.png'
import {nextTick, reactive, watch} from "vue";
import {onMounted, onBeforeUnmount, ref} from "vue";
import {useRouter} from "vue-router";
const router = useRouter();
let props = defineProps({
    height:{
        type:String,
        default:'240'
    },
    productImgArr:{
        type:Array,
        default: [] // 图片链接的数组
    },
    isShowItem:{ // 是否显示下面的子项图片列表
        type:Boolean,
        default:true
    },
    title:{
        type:String,
        default:''
    },
    localAmplifica:{ // 图片是否局部放大
        type:Boolean,
        default:false
    },
    autoSwitch:{ // 鼠标放上去时,图片是否自动轮播
        type:Boolean,
        default:true
    }
})
let width = {
    width: '355px',
    margin: '0 auto'
}
let currentIndex = ref(0)
let animate = ref(false)
let timer1 = ref('')
let timer2 = ref('')
let timer3 = ref('')
let timer4 = ref('')
let animateORclick = ref(0)

function last(){
    mouseleave()
    animation()
    if (currentIndex.value===0){

        // 处理当图片大于6张时,点击下一张导致看不到当前是哪一项图片问题,做循环处理
        currentIndex.value = 0
        props.productImgArr.unshift(props.productImgArr[props.productImgArr.length-1])
        props.productImgArr.pop()

        return
    }
    currentIndex.value--
}

function next(){
    mouseleave()
    animation()
    if (currentIndex.value===props.productImgArr.length-1){
        currentIndex.value = 0
        return
    }
    currentIndex.value++
    if (currentIndex.value>5){
        // 处理当图片大于6张时,点击下一张导致看不到当前是哪一项图片问题,做循环处理
        currentIndex.value = 5
        controlOrder(props.productImgArr)
    }
}
const currentPto = (index) => currentIndex.value = index

function mouseleave(){
    window.clearInterval(timer1.value)
    window.clearInterval(timer2.value)
    window.clearInterval(timer4.value)
    stopTimer3()
}
function animation() {
    if (!props.autoSwitch) return;
    if (props.productImgArr.length==0) return;
    timer3.value = window.setInterval(()=>{
        animateORclick.value++
    },1000)
}

function stopTimer3(){
    animateORclick.value = 0
    window.clearInterval(timer3.value)
}

function controlOrder(data){
    data.push(data[0])
    data.shift()
    animate.value = false
}

// 监听数据详情切换,重置图片下标
watch(()=>router.currentRoute.value.params.mixid,newValue=>{ currentIndex.value = 0 })

watch(()=>animateORclick.value,(newValue)=>{
    if (newValue==3&&props.productImgArr.length>6){
        animate.value = true
        timer2.value = setTimeout(() => {
            controlOrder(props.productImgArr)
        }, 900)
    }else if (newValue>3&&props.productImgArr.length>6){
        mouseleave()
        timer1.value = setInterval(() => {
            animate.value = true
            timer2.value = setTimeout(() => {
                controlOrder(props.productImgArr)
            }, 1200)
        }, 2000)
    } else if (props.productImgArr.length<=6){
        mouseleave()
        timer4.value = window.setInterval(()=>{
            if (currentIndex.value===props.productImgArr.length-1){
                currentIndex.value = 0
                return
            }
            currentIndex.value++
        },2000)
    }
})


// -----------------移动局部放大图片逻辑 start

const moveDom = ref(null) // 需要放大的图片
const flag = ref(false)
const styleObj = ref({ left: 0, top: 0 })
let imgBox = ref({ width: 275, height: 191.781 })
let moveBorder = ref({ width: 0, height: 0 })
let popImageBox = ref({ width: 0, height: 0, left: 0 })
let popImageStyle = ref({ width: 0, height: 0 })
let timerD1 = ref(null)
let timerD2 = ref(null)
let isHaveData = ref(false)
watch(()=>props.productImgArr,(newValue) => {
    isHaveData.value = newValue&&newValue.length>0
} ,{ deep: true })


onMounted(() => {
    timerD1.value =  setTimeout(()=>{
        if (props.localAmplifica && isHaveData.value){
            moveDom.value.addEventListener('mousemove', moveEvent)
            moveDom.value.addEventListener('mouseleave', mouseleaveEvent)
        }
    },500)
})
onBeforeUnmount(() => {
    window.clearTimeout(timer2.value)
    window.clearTimeout(timerD1.value)
    window.clearTimeout(timerD2.value)
    if (moveDom.value){
        moveDom.value.removeEventListener('mouseleave', mouseleaveEvent)
        moveDom.value.removeEventListener('mousemove', moveEvent)
    }
})

const rafThrottle = (fn) => {
    let locked = false;
    return function (...args) {
        if (locked) return;
        locked = true;
        window.requestAnimationFrame(_ => {
            fn.apply(this, args);
            locked = false;
        });
    };
}

// 计算每个框元素的大小
function calculationDate(){
    let moveImgDom = window.getComputedStyle(document.querySelector('.moveImgDom'))
    let exhibitionArea = window.getComputedStyle(document.querySelectorAll('.exhibitionArea')[0]).width.split('px')[0]
    imgBox.value = {
        width: moveImgDom.width.split('px')[0],
        height: moveImgDom.height.split('px')[0]
    }
    moveBorder.value = {
        width: moveImgDom.width.split('px')[0] / 2,
        height: moveImgDom.height.split('px')[0] / 2
    }
    popImageBox.value = {
        width: imgBox.value.width*2,
        height: imgBox.value.height*2,
        left:Number(exhibitionArea) + 25
    }
    popImageStyle.value = {
        width: popImageBox.value.width*2,
        height: popImageBox.value.height*2,
    }
}

function resetFDdata(){
    imgBox.value = { width: 275, height: 191.781 }
    moveBorder.value = { width: 0, height: 0 }
    popImageBox.value = { width: 0, height: 0, left:0 }
    popImageStyle.value = { width: 0, height: 0 }
    styleObj.value = { left: 0, top: 0 }
}

watch(()=>currentIndex.value,async ()=>{
    // 每切换一张图片,数据就得重新计算,为了更好的进行局部放大效果
    if (props.localAmplifica){ // 只有支持图片局部放大功能才做如下处理
        await resetFDdata();
        await calculationDate();
    }
})

// 鼠标移动事件
const moveEvent = rafThrottle(e => {
    timerD2.value =  setTimeout(()=>{
        nextTick( async ()=>{
            try {
                await calculationDate();
                let event = e || window.event;
                if (event.preventDefault) {
                    event.preventDefault();
                } else {
                    event.returnValue = false;
                }
                flag.value = true
                let { offsetX, offsetY } = e;
                let { height, width } = imgBox.value
                //计算边界条件
                styleObj.value = {
                    left: (offsetX - moveBorder.value.width / 2) > 0 ? (offsetX + moveBorder.value.width / 2) >= width ? width - moveBorder.value.width : offsetX - moveBorder.value.width / 2 : 0,
                    top: (offsetY - moveBorder.value.height / 2) > 0 ? (offsetY + moveBorder.value.height / 2) >= height ? height - moveBorder.value.height : offsetY - moveBorder.value.height / 2 : 0
                }
                // let { left, top } = styleObj.value;
                //根据比例计算放大的区域
            } catch (e) { console.log(e) }
        })
    })
})
// 鼠标移出
const mouseleaveEvent = rafThrottle(e => {
    flag.value = false
})


// -----------------移动局部放大图片逻辑 end

</script>

<style lang="scss" scoped>
* {
    margin: 0;
    padding: 0;
}
.productCarousels {
    box-sizing: border-box;
    position: relative;
    .exhibitionArea {
        width: 315px;
        height: 155px;
        border-radius: 6px;
        box-shadow: 0 3px 6px 1px rgb(0 0 0 / 16%);
        box-sizing: border-box;
        position: relative;
        padding: 0 20px;
        display: flex;
        justify-content: center;
        align-items: center;
        overflow: hidden;
        margin: 0 auto;
        &:hover {
            .leftNav,
            .rightNav {
                display: block;
            }
        }
        .noImgData {
            position: relative;
            width: 100%;
            height: 100%;
            margin: 0 -20px;
            user-select: none;
            div {
                text-align: center;
                position: absolute;
                width: 90%;
                height: 205px;
                line-height: 50px;
                overflow: hidden;
                left: 0;
                bottom: 0;
                right: 0;
                top: 0;
                margin: auto;
                z-index: 1;
                font-size: 32px;
                font-family: "Source Han Serif CN-Medium", "Source Han Serif CN";
                font-weight: 500;
                color: #fff;
            }
            img {
                position: absolute;
                width: 100%;
                height: 100%;
            }
        }
        .leftNav,
        .rightNav {
            width: 30px;
            height: 30px;
            position: absolute;
            top: 50%;
            transform: translateY(-50%);
            cursor: pointer;
            display: none;
            img {
                width: 100%;
                height: 100%;
            }
        }
        .leftNav {
            left: 0;
        }
        .rightNav {
            right: 0;
        }
        .nolocalAmplifica {
            position: absolute;
            max-width: 100%;
            max-height: 100%;
            top: 0;
            bottom: 0;
            left: 0;
            right: 0;
            margin: auto;
        }
        .imgBigShow {
            position: relative;
            width: 275px;
            height: 191.781px;
            .moveBorder {
                position: absolute;
                top: 0;
                left: 0;
                pointer-events: none;
                background-color: rgb(150 163 199 / 31.5%);
                display: flex;
                flex-wrap: wrap;
                overflow: hidden;
            }
            img {
                max-width: 100%;
                display: block;
                padding: 1px;
                flex-shrink: 0;
                &:hover {
                    cursor: move;
                }
            }
        }
    }
    .noPadding {
        padding: 0;
    }
    .imgItem {
        height: 60px;
        margin-top: 10px;
        display: flex;
        overflow: hidden;
        padding-left: 20px;
        padding-top: 7px;
        width: 94%;
        .imgItemBox {
            flex-shrink: 0;
            width: 43px;
            height: 43px;
            box-shadow: 0 3px 6px 1px rgb(0 0 0 / 20%);
            border-radius: 6px;
            margin-right: 10px;
            position: relative;
            border: 1px solid #fff;
            &:hover {
                transform: scale(1.18);
                transition-duration: 0.5s;
            }
            img {
                padding: 1px;
                max-width: 100%;
                max-height: 100%;
                position: absolute;
                top: 0;
                bottom: 0;
                left: 0;
                right: 0;
                margin: auto;
            }
        }
    }
    .pop-image {
        width: 315px;
        height: 155px;
        //border: 1px solid #0ba81a;
        position: absolute;
        top: 0;
        left: 315px;
        background-color: white;
        overflow: hidden;
        img {
            position: absolute;
        }
    }
}
.currentItem {
    transform: scale(1.18);
    border-radius: 6px;
    border-color: #aec9fe !important;
}
.lastImg {
    transition: all 2s;
    transform: translateX(-60px);
}

// 图片局部放大逻辑 start
.imgBorder {
    border: 1px solid #ddd;
}

// 图片局部放大逻辑 end

</style>

不理解,不懂的,欢迎留言

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

草样的年华

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

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

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

打赏作者

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

抵扣说明:

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

余额充值