vue3自定义指令实现一个图片放大镜

该文章介绍了如何使用Vue3自定义指令实现图片在鼠标移入时放大的功能,通过创建一个覆盖原图的div并调整其位置和大小来达到放大效果。当图片部分在视口内时,放大镜会与图片底部对齐。源码已上传至npm,支持配置放大倍数。
摘要由CSDN通过智能技术生成

先看效果,鼠标移入就可以放大, 默认是在原图放大,也可以配置放大的窗口大小,如果图片靠左边, 放大的窗口就往右边靠, 图片在右边,就往左边靠

 b站演示:vue3 自定义指令实现图片放大_哔哩哔哩_bilibili

<template>
  <div class="flex">
    <img src="./assets/kai.jpg" v-zoom="{ zoom: 1.4 }">
    <img src="./assets/kai.jpg" v-zoom="{ zoom: 1.2 }">
    <img src="./assets/kai.jpg" v-zoom>
  </div>
</template>

<script setup>
import { zoompic } from 'tanyupeng'
const vZoom = zoompic
</script>

<style>
img {
  width: 200px;
  margin: 20px;
}
</style>
 

原理:在dom鼠标移入的时候 ,就添加一个div, 完全覆盖住原图, 并把原图的src设置为背景

已经上传npm

 

npm:tanyupeng - npm

源码如下

//用来判断一个dom元素在可视范围内是否显示完整,不完整放大镜就和img底部对齐

function isElementPartiallyInViewport(element) {

  let rect = element.getBoundingClientRect();

  let windowHeight = (window.innerHeight || document.documentElement.clientHeight);

  let vertInView = (rect.top <= windowHeight) && ((rect.top + rect.height) >= 0) && (rect.bottom > windowHeight);

  return vertInView;

}



const enterHandler = (e) => {

  const el = e.target

  //得到父盒子并加上相对定位,为什么?因为我的放大镜是通过父亲相对定位覆盖图片的

  const father = el.parentNode

  father.style.position = 'relative'

  //寻找放大镜div, 没有的话就创建

  let show = document.querySelector('.show')

  if (!show) {

    //如果没有放大镜蒙版就创建一个

    show = document.createElement('div')

    //宽高等于图片大小, 因为要覆盖,如果不等于的话就计算大小

    let width = el.width

    let height = el.height

    //给放大镜的宽高设置为原图大小,如果传入倍数就放大倍数

    show.style.width = width * el.zoom + 'px'

    show.style.height = height * el.zoom + 'px'

    //一定要把放大镜的优先级提高

    show.style.zIndex = 99

    //计算出img距离顶部和左边多少,从而div覆盖原图实现放大效果

    let top = el.offsetTop;

    let left = el.offsetLeft;

    //做一下放大倍数蒙版的适配,让放大镜和原图中心线对齐

    // console.log(top + height);

    // console.log(window.innerHeight);



    if (el.zoom != 1) {



      left = left - (width * el.zoom - width) / 2

      show.style.left = `${left}px`

      show.style.top = `${top}px`

      //如果超出左边就右靠

      if (left < 0) {

        left = el.offsetLeft;

        show.style.left = `${left}px`

        // show.style.top = `${top}px`

      }

      //如果超出右边就让他左靠

      else if ((left + width * el.zoom) > document.body.offsetWidth) {

        show.style.left = el.offsetLeft - (width * el.zoom - width) + 'px'

        // show.style.top = `${top}px`

      }

      //适配top

      //如果dom元素在y轴方向显示不完整, 也就是显示一半,那放大镜和原题img以底部对齐

      if (isElementPartiallyInViewport(el)) {

        show.style.top = top - (height * el.zoom - height) + 'px'

        // console.log('不完整');

      } else {

        // console.log("元素在y轴方向显示完整");

      }

    } else {

      //正常情况下,也就是不放大的情况

      show.style.left = `${left}px`

      show.style.top = `${top}px`

    }

    //把img的src取出来给放大镜div做背景

    show.style.backgroundImage = `url(${el.src})`

    show.style.backgroundRepeat = 'no-repeat'

    // show.style.backgroundColor = 'red'

    //稍微设置一下圆角

    show.style.borderRadius = '3%';

    //绝对定位

    show.style.position = 'absolute'

    // show.style.border = "1px solid rgba(44,163,221,.6)";

    //把放大镜添加到father里面 

    father.appendChild(show)

    //放大镜出现就把原来的隐藏掉,主要是放大镜有一个圆角, 不隐藏会不美观

    el.style.visibility = 'hidden'

    //放大镜移动

    show.addEventListener('mousemove', (e) => {

      //核心代码

      let rect = e.target.getBoundingClientRect()

      let x = e.offsetX / rect.width

      let y = e.offsetY / rect.height

      show.style.backgroundPosition = `calc(${x}*100%) calc(${y}*100%)`

    })

    show.addEventListener('mouseleave', (e) => {

      //让原图又出现

      el.style.visibility = 'visible'

      if (show) {

        //销毁dom, 事件也就不存在了

        father.removeChild(show);

      }

    })

  }

}

export default {

  mounted(el, bindings) {

    //如果有传入放大蒙版配置的话

    if (bindings.value && bindings.value.zoom) {

      el.zoom = bindings.value.zoom

    } else {

      //默认是原图放大

      el.zoom = 1

    }

    el.addEventListener('mouseenter', enterHandler)

  },

  beforeUnmount(el) {

    el.removeEventListener('mouseenter', enterHandler)

  }

}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值