案例简介
在京东等网站当中,有这种商品放大的功能,就是把鼠标放在图片上,会在旁边出现放大的效果。
具体分析:
- 鼠标经过对应小盒子,左侧中等盒子显示对应中等图片
- 鼠标经过中盒子,右侧会显示放大镜效果的大盒子
- 黑色遮罩盒子跟着鼠标来移动
- 鼠标在中等盒子上移动,大盒子的图片跟着显示对应位置
实现过程
基础样式
首先先实现基本的样式,再配合JavaScript实现功能,具体代码在后面全部代码部分
JS实现部分
鼠标经过对应小盒子,左侧中等盒子显示对应中等图片:
- 获取对应的元素
- 采取事件委托的形式,监听鼠标经过小盒子里面的图片, 注意此时需要使用
mouseover
事件,因为需要事件冒泡触发small
- 让鼠标经过小图片的爸爸
li
盒子,添加外选择框类,其余的li
移除类(注意先移除,后添加)
- 鼠标经过小图片,可以拿到小图片的src, 可以做两件事
- 让中等盒子的图片换成这个小图片的
src
- 让大盒子的背景图片,也换成这个小图片的
src
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| const small = document.querySelector('.small') const middle = document.querySelector('.middle') const large = document.querySelector('.large')
small.addEventListener('mouseover', function(e) { if(e.target.tagName === 'IMG') { this.querySelector('.active').classList.remove('active') e.target.classList.add('active') middle.querySelector('img').src = e.target.src large.style.backgroundImage = `url(${e.target.src})` } })
|
鼠标经过中盒子,右侧会显示放大镜效果的大盒子:
- 用到鼠标经过和离开,鼠标经过中盒子,大盒子利用
display
来显示和隐藏
- 鼠标离开不会立马消失,而是有200ms的延时,用户体验更好,所以尽量使用定时器做个延时
setTimeout
- 显示和隐藏也尽量定义一个函数,因为鼠标经过离开中等盒子,会显示隐藏,同时,鼠标经过大盒子,也会显示和隐藏
- 给大盒子里面的背景图片一个默认的第一张图片
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| middle.addEventListener('mouseenter', show) middle.addEventListener('mouseleave', hide) large.addEventListener('mouseenter', show) large.addEventListener('mouseleave', hide)
let timeId = null
function show() { clearTimeout(timeId) large.style.display = 'block' }
function hide() { timeId = setTimeout(function () { large.style.display = 'none' }, 200) }
|
- 为什么需要在显示时清除计时器,因为如果没有清除计时器,在鼠标离开的200ms之内再次进入盒子,在时间到时,大盒子仍然会被隐藏。
黑色遮罩盒子跟着鼠标来移动:
- 先做鼠标经过中等盒子,显示隐藏黑色遮罩的盒子
- 让黑色遮罩跟着鼠标来走, 需要用到鼠标移动事件
mousemove
- 让黑色盒子的移动的核心思想:不断把鼠标在中等盒子内的坐标给黑色遮罩层
let
、top
值,这样遮罩层就可以跟着移动了
- 得到鼠标在页面中的坐标 利用事件对象的
pageX
- 得到中等盒子在页面中的坐标
middle.getBoundingClientRect()
- 鼠标在中盒子里面的坐标 = 鼠标在页面中的坐标 - 中等盒子的坐标
- 黑色遮罩层不断得到鼠标在中盒子中的坐标,就可以移动起来了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| middle.addEventListener('mouseenter', function() { layer.style.display = 'block' }) middle.addEventListener('mouseleave', function() { layer.style.display = 'none' })
middle.addEventListener('mousemove', function(e) { let x = e.pageX - middle.getBoundingClientRect().left let y = e.pageY - middle.getBoundingClientRect().top let mx = 0, my = 0; if(x < 100) mx = 0 if(x >= 100 && x <= 300) mx = x - 100 if(x > 300) mx = 200 if(y < 100) my = 0 if(y >= 100 && y <= 300) my = y - 100 if(y > 300) my = 200 layer.style.left = mx + 'px' layer.style.top = my + 'px' })
|
- 注意:
- 限定遮罩的盒子只能在middle 内部移动,需要添加判断
- 因为基础样式设定为400 x 400的中盒子
- 限定水平方向 大于等于0 并且小于等于 400
- 限定垂直方向 大于等于0 并且小于等于 400
- 遮罩盒子移动的坐标:
- 声明一个 mx 作为移动的距离
- 水平坐标 x 如果小于100 ,则移动的距离
mx
就是 0,不应该移动
- 水平坐标如果大于等于100 并且小于等于300,移动的距离就是 mx - 100 (100是遮罩盒子自身宽度的一半)
- 水平坐标 如果大于300,移动的距离就是
mx
就是200,不应该在移动了
- 垂直同理
- y坐标特殊,需要减去页面被卷去的头部
- 为什么不用
box.offsetLeft
和box.offsetTop
因为这俩属性跟带有定位的父级有关系,很容被父级影响,而getBoundingClientRect()
不受定位的父元素的影响
鼠标在中等盒子上移动,大盒子的图片跟着显示对应位置:
- 设置大盒子的背景图片尺寸为800 x 800,大盒子为400 x 400,所以实现放大效果
- 鼠标在中等盒子中的移动坐标,赋值给大盒子的图片,需要放大效果,所以需要两倍赋值
- 大盒子的图片需要反向移动,所以需要赋的值为负值
1 2 3
| large.style.backgroundPositionX = -2 * mx + 'px' large.style.backgroundPositionY = -2 * my + 'px'
|
全部代码
HTML部分
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
|
<div class="box"> <div class="pic"> <div class="middle"> <img src="img/blue.jpg" alt=""> <div class="layer"></div> </div> <div class="small"> <ul> <li><img src="img/blue.jpg" alt="" class="active"></li> <li><img src="img/green.jpg" alt=""></li> <li><img src="img/pink.jpg" alt=""></li> <li><img src="img/yellow.jpg" alt=""></li> <li><img src="img/black.jpg" alt=""></li> </ul> </div> <div class="large"></div> </div> </div>
|
CSS部分
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73
| * { margin: 0; padding: 0; box-sizing: border-box; } li{ list-style: none; } .box { display: flex; width: 100%; height: 420px; background-color: #bfbfbf; margin: 0 auto; padding-top: 10px; } .pic{ display: flex; width: 480px; height: 400px; margin: 0 auto; } .middle{ width: 400px; height: 400px; display: flex; position: relative; } .middle img { width: 400px; height: 400px; } .small{ width: 70px; margin-left: 10px; } .small ul{ padding: 0; margin: 0; width: 70px; height: 400px; } .small li{ width: 70px; height: 70px; margin-bottom: 12.5px; } .small img { width: 70px; height: 70px; } .active { border: 3px solid green; } .large { width: 400px; height: 400px; background-color: rgb(255, 255, 255, 0.5); position: absolute; left: 1194px; display: none; background-image: url(img/blue.jpg); background-size: 800px 800px; } .layer { width: 200px; height: 200px; background-color: rgba(0, 0, 0, 0.3); position: absolute; display: none; left: 0; top: 0; }
|
JavaScript部分
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
| const small = document.querySelector('.small') const middle = document.querySelector('.middle') const large = document.querySelector('.large') const layer = document.querySelector('.layer')
small.addEventListener('mouseover', function(e) { if(e.target.tagName === 'IMG') { this.querySelector('.active').classList.remove('active') e.target.classList.add('active') middle.querySelector('img').src = e.target.src large.style.backgroundImage = `url(${e.target.src})` } }) middle.addEventListener('mouseenter', show) middle.addEventListener('mouseleave', hide) large.addEventListener('mouseenter', show) large.addEventListener('mouseleave', hide) let timeId = null
function show() { clearTimeout(timeId) large.style.display = 'block' } function hide() { timeId = setTimeout(function () { large.style.display = 'none' }, 200) }
middle.addEventListener('mouseenter', function() { layer.style.display = 'block' }) middle.addEventListener('mouseleave', function() { layer.style.display = 'none' }) middle.addEventListener('mousemove', function(e) { let x = e.pageX - middle.getBoundingClientRect().left let y = e.pageY - middle.getBoundingClientRect().top let mx = 0, my = 0; if(x < 100) mx = 0 if(x >= 100 && x <= 300) mx = x - 100 if(x > 300) mx = 200 if(y < 100) my = 0 if(y >= 100 && y <= 300) my = y - 100 if(y > 300) my = 200 layer.style.left = mx + 'px' layer.style.top = my + 'px' large.style.backgroundPositionX = -2 * mx + 'px' large.style.backgroundPositionY = -2 * my + 'px' })
|