js实现简单放大镜

157 阅读4分钟

效果图

效果就是下面这张图啦~gif图上的一些模糊点是视频转gif文件时产生的,请忽略它。

1.gif

放大原理

这个放大的效果并不是真正的像放大镜一样放大到多少倍,而是准备好了一张放大图片比例的大图显示在展示的图片右侧,然后通过遮罩层在展示图片上的移动控制大图的位置来实现放大效果,所以遮罩层的大小和展示图片与大图比例有关。

代码思路

页面设计

首先,我们需要一个盒子box放所有的标签(如展示的图片、放大的大图、下面的选项卡);
然后,在box里,首先是展示的图片,下面是切换图片的选项卡,在展示图片的右侧加一个绝对定位的盒子存放大图。
HTML代码:

<div class="box">
    <div class="big" id="big"><img src="images3/large1.jpg"></div>
    <div class="show" id="show">
        <img src="images3/middle1.jpg" />
        <div class="shadow" id="shadow"></div>
    </div>
    <div class="tab">
        <ul id="list">
            <li><img src="images3/small1.jpg"></li>
            <li><img src="images3/small2.jpg"></li>
            <li><img src="images3/small3.jpg"></li>
            <li><img src="images3/small4.jpg"></li>
            <li><img src="images3/small5.jpg"></li>
        </ul>
    </div>
</div>

CSS代码:

* {
    padding: 0;
    margin: 0;
    list-style: none;
}

.box {
    position: relative;
    width: 400px;
    height: 490px;
    margin: 50px 0 0 50px;
    background-color: #FFC0CB;
}

.box .show {
    width: 400px;
    height: 400px;
}

/*图片标签设置绝对定位以及z-index属性值,确保遮罩层浮于图片上方*/
.box .show img {
    position: absolute;
    z-index: 1;
}

/*遮罩层设置z-index属性值*/
.box .show .shadow {
    position: absolute;
    display: none;
    width: 200px;
    height: 200px;
    background-color: cornflowerblue;
    opacity: .4;
    z-index: 2;
    pointer-events: none;
}

.box .tab {
    width: 400px;
    height: 90px;
}

.box .tab ul {
    display: flex;
    justify-content: space-evenly;
    width: 100%;
    height: 100%;
    margin-top: 20px;
}

.box .tab ul li {
    width: 50px;
    height: 50px;
}

.box .tab ul li img {
    border-radius: 10px;
}

/*鼠标移入某选项卡时该选项卡增加样式current*/
.current {
    border: solid 3px lightpink;
}

.box .big {
    position: absolute;
    width: 400px;
    height: 400px;
    left: 400px;
    overflow: hidden;
}

.box .big img {
    position: absolute;
    display: none;
}

遮罩层的大小及大图的移动

遮罩层的大小:
我选用的素材中,展示图片(选项卡上方图片)宽高是400px,大图宽高均是800px,即大图的宽高是展示图片的两倍,所以遮罩层的宽高应该是展示图片的一半,因为放大的原理即将遮罩层中的内容放大,如果遮罩层/展示图片和展示图片/大图的比例不一致,则遮罩层中的图案与大图显示的图案会不一致。
大图的移动:
遮罩层的左上角和中图的比例应等于大图显示的左上角和整个大图的比例,即curTop/middle.offsetHeight=大图.top/大图.高度,所以大图.top = curTop * 大图.高度 /middle.offsetHeight,left值同理。
js代码如下:

//封装函数,返回id对应的DOM对象
function $(id) {
    return document.getElementById(id)
}

//页面完全加载完成后执行脚本代码
window.onload = function () {

    //获取展示的大图/中图/小图选项卡/遮罩层对应的DOM对象
    let big = $("big").querySelector("img")
    let middle = $("show").querySelector("img")
    let tabs = $("list").querySelectorAll("li")
    let shadow = $("shadow")

    //遍历小图选项卡,为每个选项卡中的图片添加鼠标进入移出事件
    for (let i = 0; i < tabs.length; i++) {
        let cur_img = tabs[i].querySelector("img")
        cur_img.onmouseenter = () => {
            cur_img.className = "current"
            middle.src = "images3/middle" + (i + 1) + ".jpg"
            big.src = "images3/large" + (i + 1) + ".jpg"
        }
        cur_img.onmouseleave = () => {
            cur_img.className = ""
        }
    }

    //为中图添加鼠标进入事件,鼠标进入时大图和遮罩层显示
    middle.onmouseenter = () => {
        big.style.display = "block"
        shadow.style.display = "block"
    }
    //为中图添加鼠标进入事件,鼠标移出时大图和遮罩层隐藏
    middle.onmouseleave = () => {
        big.style.display = "none"
        shadow.style.display = "none"
    }

    //为中图添加鼠标移动事件
    //鼠标移动时保证遮罩层也随之移动,遮罩层移动原理:获取鼠标位置,遮罩层的top/left值应等于鼠标的坐标减去遮罩层宽高的一半
    //鼠标移动时大图也随之移动,大图移动原理:获取鼠标位置,
    //该法已弃用 middle.onmousemove = () => { let ev = event || window.event }
    middle.addEventListener("mousemove", (event) => {
        //遮罩层现在距离中图左侧/上侧的距离
        let curLeft = event.offsetX - shadow.offsetWidth / 2
        let curTop = event.offsetY - shadow.offsetHeight / 2;
        //立即执行函数,遮罩层移动函数
        (function () {
            //遮罩层能向右/下移动的最大距离
            let leftMax = middle.offsetWidth - shadow.offsetWidth
            let topMax = middle.offsetHeight - shadow.offsetHeight
            shadow.style.left = curLeft + "px"
            shadow.style.top = curTop + "px"
            //遮罩层的四角边界值限定
            if (curLeft < 0) {
                shadow.style.left = "0"
                curLeft = 0
            }
            if (curTop < 0) {
                shadow.style.top = "0"
                curTop = 0
            }
            if (curLeft > leftMax) {
                shadow.style.left = leftMax + "px"
                curLeft = leftMax
            }
            if (curTop > topMax) {
                shadow.style.top = topMax + "px"
                curTop = topMax
            }
        })();
        //立即执行函数,大图移动函数
        //大图移动原理:遮罩层的左上角和中图的比例应等于大图显示的左上角和整个大图的比例,即curTop/middle.offsetHeight=大图.top/大图.高度
        //所以 大图.top = curTop * 大图.高度  /middle.offsetHeight,left值同理
        (function () {
            big.style.top = -curTop * big.offsetHeight / middle.offsetHeight + "px"
            big.style.left = -curLeft * big.offsetWidth / middle.offsetWidth + "px"
        })()
    })
}

需要下载demo的小伙伴请点击github_magnifier

开发中犯的一些细节错误


1. 控制DOM对象的显隐要注意
要设置到准确的盒子上,比如div>img,js中为img添加显隐事件,若在css中将display:none写在div上,会出错,应写在img里
2. 直接用event获取鼠标当前位置已弃用
改用以下方法 middle.addEventListener("mousemove", (event) => { })
3. 层叠盒子上添加鼠标事件出现闪烁问题
问题:div1上叠加div2,给div1添加鼠标移动事件,鼠标在div1上移动时div2会不停闪烁
原因:鼠标在div1上移动时,其实鼠标也在div2上,div2成为了鼠标事件的target,但div2没有设置鼠标事件
解决方案:给div2添加pointer-events: none , MDN解释是,元素永远不会成为鼠标事件的target
4. 遮罩层大小
遮罩层/中图=中图/大图
5. 设置img的top/left值
那么该img对象一定不能是静态定位,即要设置它的position属性值,如果设置到它的父层定位没有用 ## 后记 这个思路是我在YouTube上看到的,上面有教学视频,讲的非常清楚,想看视频的请点击[YouTube](https://www.youtube.com/watch?v=nCtLDQxLGbE)