先上效果图,模仿的功能是淘宝的商品图片细节展示
功能描述
鼠标滑过左侧图片区域smallCanvas
会显示一个半透明遮罩mask
,同时右侧区域bigCanvas
会展示遮罩遮住区域的放大图
功能实现思路
1.加载图片然后将图片绘制drawImage
到smallCanvas
上,这一步也可以直接用原图片代替smallCanvas
然后不用绘制,具体原因,可能因为是闲的吧。
2.创建一个mask
小遮罩,根据smallCanvas
绝对定位然后隐藏display:none;
3.显示mask
块。思路就是当触发smallCanvas
所在位置的mouseenter
事件时,显示mask
。根据事件参数event
拿到鼠标相对于当前元素的位置event.offsetX
和event.offsetY
,我是用这两个参数来改变mask
的绝对定位位置。这个时候mask
块已经出现在我们鼠标的位置。
4.让mask
块跟随鼠标移动。当mask
显示后再移动鼠标,这个时候触发的mousemove
事件的对象就是mask
了,所以这个时候再拿offsetX
就已经不能直接用于mask
的绝对定位了,这里我借助了event.clientX
定位鼠标的绝对位置,然后根据鼠标移动前后clientX clientY
的相对关系重新定位mask
(复杂又离谱),相信大家动手画一画就明白了,
nowLeft = beforeLeft + nowClientX - beforeClientX
重新赋值给mask
这个时候就可以跟随鼠标移动了
5.将mask
区域的图片放大展示到bigCanvas
。这里也是用到了drawImage,不过这次我们不是将整张图片放到画布上,而是根据我们mask
的位置将原图片的一部分绘制到画布上。drawImage(image, sx, sy, sw, sh, dx, dy, dw, dh)
具体的参数解释如图
图中Source Image中的绿色区域就相当于我们需要绘制的图片
很显然,这里
sx
就是我们mask
绝对定位中的left
,同理sy
就是top
,sw sh
就是我们mask
元素的width height
,我们要把放大后的图片铺满整个bigCanvas
在图片上就是Destination Canvas(目标画布),显然这里dx dy
就都是0
,然后dw dh
毫无疑问了,我们需要填满整个画布,那就是我们bigCanvas
的width hegiht
了。
6.改变mask
位置后,调用drawImage(image, sx, sy, sw, sh, dx, dy, dw, dh)
将我们mask
遮住的区域绘制在bigCanvas
就完成了,至此局部放大功能就完成了。
7.善后工作。当鼠标移出smallCanvas
时我们可能需要隐藏bigCanvas
或者清空它,还有各种我们自己想要的操作,可以放到mouseleave
的监听函数中。下面我放一下demo的代码,供参考
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div class="small-box">
<div class="canvas-box">
<canvas id="smallCanvas" width="400" height="400"></canvas>
</div>
<div class="mask"></div>
</div>
<div class="big-box">
<div class="canvas-box">
<canvas id="bigCanvas" width="400px" height="400px"></canvas>
</div>
</div>
<img id="myimg" src="./erweima.jpg" alt="" style="display: none;">
<img id="getimg" src="" alt="" style="display: none;">
<style>
* {
margin: 0;
padding: 0;
}
body {
display: flex;
flex-wrap: wrap;
}
.small-box {
display: inline-block;
width: 400px;
height: 400px;
position: relative;
overflow: hidden;
}
#smallCanvas {
width: 400px;
height: 400px;
}
.mask {
width: 100px;
height: 100px;
cursor: move;
position: absolute;
background-color: rgba(0, 0, 0, 0.3);
display: none;
}
.big-box {
display: inline-block;
width: 400px;
height: 400px;
margin-left: 20px;
background-color: aliceblue;
}
</style>
<script>
let smallCanvas = document.getElementById('smallCanvas')
let bigCanvas = document.getElementById('bigCanvas')
let smallBox = document.getElementsByClassName('small-box')[0]
let mask = document.getElementsByClassName('mask')[0]
let myimg = document.getElementById('myimg')
//let getimg = document.getElementById('getimg')
let ctx1 = smallCanvas.getContext("2d");
let ctx2 = bigCanvas.getContext("2d");
// 鼠标是否在mask块上
let isOnMask = false
// 上一次的clientX
let beforeX;
// 上一次的clientY
let beforeY;
// mask块的上一个左定位
let beforeleft;
// mask块的上一个上定位
let beforetop;
myimg.onload = function () {
// 当图片加载完成后调用 drawImage方法将图片绘制到canvas上
ctx1.drawImage(myimg, 0, 0);
}
smallBox.onmousemove = function (e) {
// 如果mask块已经显示则开始根据事件的clientX,clientY定位mask元素位置
if (isOnMask) {
const X = e.clientX
const Y = e.clientY
let nowLeft = beforeleft + X - beforeX
let nowTop = beforetop + Y - beforeY
if(nowLeft >= 360|| nowTop >= 360){
return mask.style.display = 'none'
}
mask.style.left = nowLeft + 'px'
mask.style.top = nowTop + 'px'
// 将图片根据鼠标位置重绘到bigCanvas上
//getimg.src = smallCanvas.toDataURL()
ctx2.drawImage(myimg, beforeleft, beforetop, 100, 100, 0, 0, 400, 400);
//更新位置信息 为下一次重绘做准备
beforeleft = nowLeft
beforetop = nowTop
beforeX = X
beforeY = Y
//console.log(beforeX,beforeY)
return
}
//console.log(beforeX,beforeY)
}
smallBox.onmouseenter = function (e) {
console.log('进入')
// 初始化mask的定位
isOnMask = true
beforeX = e.clientX
beforeY = e.clientY
beforeleft = e.offsetX - 50
beforetop = e.offsetY - 50
// console.log(X, Y, e)
mask.style.left = beforeleft + 'px'
mask.style.top = beforetop + 'px'
mask.style.display = "block"
}
smallBox.onmouseleave = function () {
console.log('离开')
mask.style.display = "none"
isOnMask = false
// 清空bigCanvas
ctx2.clearRect(0,0,400,400)
}
</script>
</body>
</html>
如果看到最后
1.关于鼠标位置和mask
定位位置的逻辑我这里显得臃肿了,可以采取很多优化的方法,比如在smallCanvas
的上面创建一个100%覆盖的遮罩,形成一个三层结构,mask
在中间,smasllCanvas
在底部,这样事件的目标元素就是固定的最上层遮罩,我们就可以用offsetX offsetY
一把梭出来mask
的相对位置,不用再进行换算了。
2.canvas绘制出来的感觉好糊,将来有机会看看怎么优化
3.设计参考了一位前辈的思路,他的思路不是绘制到画布上,而是根据遮罩的相对位置,在bigCanvas
的位置从新加载图片,图片会采取移动和放大来进行展示,具体移动多少,放大多少,肯定是根据mask
的位置和大小来做的,具体地址忘了,有兴趣可以自己试一下
4.新的一年,祝大家都有新的收获