如何优雅的实现图片预览

130 阅读1分钟

1.效果图

image.png

2.代码实现

## 1.utils下建立img-preview
## 2.img-preview下建立index.js img-preview.vue
## 3.main.js引用

main.js

import imgPreviewPlugin from "@/utils/img-preview/index";
Vue.use(imgPreviewPlugin);

index.js

import imgPreview from "./img-preview.vue";

const imgPreviewPlugin = {
    install(Vue) {
        const imgPreviewConstructor = Vue.extend(imgPreview);

        let imgPreviewInstance;

        Vue.prototype.$imgShow = function (url) {
            if (!imgPreviewInstance) {
                imgPreviewInstance = new imgPreviewConstructor({
                    el: document.createElement("div"),
                    data() {
                        return {
                            imgUrl: url 
                        };
                    }
                });
                document.body.appendChild(imgPreviewInstance.$el);
            }

        };

        Vue.prototype.$imgClose = function () {
            if (imgPreviewInstance) {
                imgPreviewInstance.$destroy();
                imgPreviewInstance.$el.parentNode.removeChild(imgPreviewInstance.$el);
                imgPreviewInstance = null;
            }
        };
    }
};

export default imgPreviewPlugin;

img-preview.vue


<template>
    <transition appear>
        <div class="img-prevew" v-show="imgPrevewShow">
            <div class="main-content" @click.self="closeImg">
                <div class="close-icno">
                    <a-icon type="close-circle" class="icon" @click.stop="closeImg"/>
                </div>

                <img 
                    class="img-content"
                    :src="imgUrl" 
                    alt=""         
                    :style="`transform: scale(${imgScale})`"
                >

                <div class="navbtns">
                    <a-icon type="zoom-in" @click.stop="changeSize(0.15)" class="icon" />

                    <a-icon type="zoom-out" @click.stop="changeSize(-0.15)"  class="icon" />

                    <a-icon :type="imgScale > 1 ? 'fullscreen-exit' : 'fullscreen'" @click.stop="recoverSize" class="icon"/>
                </div>
            </div>
        </div>
    </transition>
</template>

<script>
export default {
    name: '',
    data() {
        return {
            imgUrl:'',
            imgScale:1,
            imgPrevewShow:true,
        }
    },

    methods: {

        closeImg(){
            this.imgPrevewShow = false;
            setTimeout(()=>{
                this.$imgClose();
            },500)
        },

        changeSize(num){
            if (this.imgScale <= 0.2 && num < 0) return
            this.imgScale += num
        },

        recoverSize(){
            if( this.imgScale === 1){
                this.imgScale = 1.4;
            }else{
                this.imgScale = 1;
            }
        },

        // 鼠标滚轮缩放
        scrollFunc(e) {
            e = e || window.event
            // e.returnValue = false // ie
            // 火狐下没有wheelDelta,用detail代替,由于detail值的正负和wheelDelta相反,所以取反
            e.delta = e.wheelDelta || -e.detail

            e.preventDefault()
            if (e.delta > 0) {
                //当滑轮向上滚动时
                this.changeSize(0.05)
            }
            if (e.delta < 0) {
                //当滑轮向下滚动时
                this.changeSize(-0.05)
            }
        },
    },

    mounted() {
        window.addEventListener('mousewheel', this.scrollFunc,{passive:false})
    },

    beforeDestroy() {
        window.removeEventListener('mousewheel', this.scrollFunc);
    },
    


}
</script>

<style lang="less" scoped>

    /* 进入:始状态 */
    .v-enter {
        transform: translateY(-100%);
    }
    /* 进入:末状态 */
    .v-enter-to {
        transform: translateY(0);
    }
    /* 进入动画 */
    .v-enter-active {
        transition: 0.5s;
    }

    /* 离开:始状态 */
    .v-leave {
        transform: translateY(0);
    }
    /* 离开:末状态 */
    .v-leave-to {
        transform: translateY(-100%);
    }
    /* 离开动画 */
    .v-leave-active {
        transition: 0.5s;
    }

    .img-prevew{
        position: fixed;
        top: 0;
        bottom: 0;
        right: 0;
        left: 0;
        user-select: none;
        -moz-user-select: none;
        -webkit-user-select: none;
        -ms-user-select: none;
        z-index: 9999;
        color: rgba(255,255,255,0.7);

    }

    .main-content{
        height: 100%;
        width: 100%;
        position: relative;
        background-color: rgba(0,0,0,0.7);
        display: flex;
        align-items: center;
        justify-content: center;

        .img-content{
            transition: all 0.2s;
            max-width: 100vw !important;
        }
    }

    .close-icno{
        z-index: 10000;
        position: absolute;
        top: 50px;
        right: 50px;
        .icon{
            font-size: 30px;
            cursor: pointer;
            transition: all 0.3s;
        }

        .icon:hover{
            color: white;
            transform: rotate(180deg);
        }
    }


    .navbtns{
        position: absolute;
        bottom: 100px;
        left: 0 ;
        right: 0;
        margin: 0 auto;
        height: 50px;
        width: 200px;
        background-color: black;
        opacity: 0.8;
        border-radius: 25px ;
        z-index: 10000;
        display: flex;
        align-items: center;
        justify-content: center;   
        
        .icon{
            font-size: 30px;
            transition: all 0.3s;
            margin: 0 10px;
            cursor: pointer;
        }
        .icon:hover{
            color: white;
            transform: scale(1.1);
        }
    }


</style>

3.优雅的调用

    //封装后
    owner.getHtmlImgSrc = (e)=>{
        if (e.target.tagName.toUpperCase() === 'IMG') {
            Vue.prototype.$imgShow(e.target.src)
        }
    }
    
    //直接使用
     this.$imgShow('图片的URL')