html+css+JavaScript实现商品放大镜效果
思路:有三部分,小图框,蒙版,大图框。小图框里放商品的小图片和蒙版,大图框里面放商品的大图片。
一、先做鼠标移入移出效果
1.获取小图框元素对象,并且设置移入事件mouseenter
2.动态的创建蒙版元素以及大图框和大图片元素
3.移出时(mouseleave)需要移除蒙版元素和大图框
二、设置鼠标移动事件
蒙版跟随鼠标移动(设置蒙版的绝对定位):
1.蒙版的left值 = 鼠标点距离浏览器左侧X轴的值(e.clientX)- 小图框元素距离浏览器左侧可视left值(smallPic.getBoundingClientRect().left)- 蒙版元素占位宽度/2
2.蒙版的top值 = 鼠标点距离浏览器左侧Y轴的值(e.clientY)- 小图框元素距离浏览器上侧可视top值(smallPic.getBoundingClientRect().top)- 蒙版元素占位高度/2
3.控制蒙版元素边界
left值小于0或者大于小图框可视宽度-蒙版占位宽度时蒙版会超出小图框的范围。所以left<0时将它设置为0,left值大于小图框可视宽度-蒙版占位宽度时就将它设置为小图框可视宽度-蒙版占位宽度。top值同理。
大图框里的大图片和蒙版等比例移动
1.移动比例=(小图框宽度 – 蒙版元素的宽度)/(大图片宽度 – 大图框元素的宽度)
2.大图片水平移动距离 = -蒙版元素的left / 移动比例
3.大图片垂直移动距离 = -蒙版元素的top / 移动比例
html代码:
<div id="lefttop">
<!-- 小图框 -->
<div id="smallPic">
<!-- 小图片 -->
<img src="images/s1.png" alt="">
<!-- 蒙版元素-->
<!-- <div class="mask"></div> -->
</div>
<!-- 大图框 -->
<!-- <div id="bigPic">
<img src="images/b1.png" alt="">
</div> -->
</div>
less代码:
#lefttop {
width: 400px;
position: relative;
// 小图框
#smallPic {
width: 400px;
height: 400px;
border: 1px solid #dfdfdf;
position: relative;
// 小图片
img {}
// 蒙版元素
.mask {
width: 200px;
height: 200px;
background-color: rgba(255, 255, 255, .5);
border: 1px solid #ddd;
position: absolute;
left: 0px;
top: 0px;
}
}
// 大图框
#bigPic {
width: 400px;
height: 400px;
border: 1px solid #ddd;
position: absolute;
left: 420px;
top: 0px;
overflow: hidden;
img {
width: 800px;
height: 800px;
position: absolute;
left: 0;
top: 0;
}
}
}
JavaScript代码:
function bigClassBing() {
// 放大镜的移入移出效果
/**
* 思路:
* 1、获取小图框元素对象,并且设置移入事件(onmouseenter无冒泡,onmouseover有冒泡)
* 2、动态的创建蒙版元素以及大图框和大图片元素
* 3、移出时(onmouseleave)需要移除蒙版元素和大图框
*/
//1.获取小图框元素
const smallPic = document.querySelector('#smallPic')
//获取leftTop元素
const leftTop = document.querySelector('#lefttop')
//2.设置移入事件
smallPic.addEventListener('mouseenter', () => {
// 3.创建蒙版元素
const maskDiv = document.createElement('div')
maskDiv.className = 'mask'
// 4.创建大图框元素
const bigPic = document.createElement('div')
bigPic.id = 'bigPic'
// 5.创建大图片元素
const bigImg = document.createElement('img')
bigImg.src = 'images/b1.png'
// 6.大图框来追加大图片
bigPic.appendChild(bigImg)
// 7.小图框追加蒙版元素
smallPic.appendChild(maskDiv)
// 8.leftTop追加大图框元素
leftTop.appendChild(bigPic)
// 设置移动事件
smallPic.addEventListener('mousemove', function (e) {
//e.clientX: 鼠标点距离浏览器左侧X轴的值
//getBoundingClientRect().left:小图框元素距离浏览器左侧可视left值
//offsetWidth:为元素的占位宽度
let left = e.clientX - smallPic.getBoundingClientRect().left - maskDiv.offsetWidth / 2
let top = e.clientY - smallPic.getBoundingClientRect().top - maskDiv.offsetHeight / 2
// 蒙版元素边界控制
// smallPic.clientWidth为小图框的可视宽度
// left大于小图框的可视宽度-蒙版的占位宽度时,蒙版就会超出小图框
if (left < 0) {
left = 0
} else if (left > smallPic.clientWidth - maskDiv.offsetWidth) {
left = smallPic.clientWidth - maskDiv.offsetWidth
}
if (top < 0) {
top = 0
} else if (top > smallPic.clientHeight - maskDiv.offsetHeight) {
top = smallPic.clientHeight - maskDiv.offsetHeigh
}
// 设置蒙版的left和top
maskDiv.style.left = left + 'px'
maskDiv.style.top = top + 'px'
// 大图框里的大图片和蒙版等比例移动
// 移动的比例关系 = 蒙版元素移动的距离 / 大图片元素移动的距离
//蒙版元素移动的距离 = 小图框宽度 – 蒙版元素的宽度
//大图片元素移动的距离 = 大图片宽度 – 大图框元素的宽度
const scale = (smallPic.clientWidth - maskDiv.offsetWidth) / (bigImg.offsetWidth - bigPic.clientWidth)
bigImg.style.left = -left / scale + 'px'
bigImg.style.top = -top / scale + 'px'
})
// 设置移出事件
smallPic.addEventListener('mouseleave', () => {
// 小图框移除蒙版元素
smallPic.removeChild(maskDiv)
// leftTop移除大图框
leftTop.removeChild(bigPic)
})
})
}
增加底部缩略图效果
结构为前后两头代表左右箭头的a标签,一个中间可视部分的div,div里面是一个装下所有缩略图的ul->li结构
点击缩略图效果
1、将点击事件绑定在ul上,利用事件冒泡完成对每个缩略图的点击效果
2、点击缩略图需要确定其下标位置来找到对应小图路径和大图路径替换现有src的值。可以通过在缩略图的img标签里加自定义属性data-id实现。
点击缩略图左右箭头的效果
缩略图的切换实际是包含着所有缩略图的ul框相对于可视框div的移动
1、先获取左右两端的箭头按钮
2、再获取可视的div以及ul元素和所有的li元素
3、计算(发生起点、步长、最大距离值)
- 发生起点start为ul右边框离div右边框的距离,起始值是0。
- 步长step=两(li的宽度+间隙)* 2
- 最大距离值= ul的宽度 - div框的宽度 = (图片的总数 - div中显示的数量) * (li的宽度 + 间隙)
- 点击右箭头时代表ul向左移,发生起点加一次步长,点击左箭头时代表ul向右移,发生起点减一次步长。将ul的left值赋值为发生起点的负值来实现它的移动。在这过程中要判断发生起点的临界范围,小于0 时让它等于0,大于最大值时就让它等于最大值。
4、然后在两个箭头按钮上绑定点击事件
完整代码如下:
html代码:
<div id="left">
<!-- 上边 -->
<div id="leftTop">
<!-- 小图框 -->
<div id="smallPic">
<!-- 小图片 -->
<img src="images/s1.png" alt="">
<!-- 蒙版元素-->
<!-- <div class="mask"></div> -->
</div>
<!-- 大图框 -->
<!-- <div id="bigPic">
<img src="images/b1.png" alt="">
</div> -->
</div>
<!-- 下边 -->
<div id="leftBottom">
<a href="javascript:;" class="prev">
< </a>
<div id="piclist">
<ul>
<!-- <li><img src="images/s1.png" alt=""></li>
<li><img src="images/s1.png" alt=""></li>
<li><img src="images/s1.png" alt=""></li>
<li><img src="images/s1.png" alt=""></li>
<li><img src="images/s1.png" alt=""></li>
<li><img src="images/s1.png" alt=""></li>
<li><img src="images/s1.png" alt=""></li>
<li><img src="images/s1.png" alt=""></li>
<li><img src="images/s1.png" alt=""></li>
<li><img src="images/s1.png" alt=""></li> -->
</ul>
</div>
<a href="javascript:;" class="next"> > </a>
</div>
</div>
less代码:
#left {
width: 400px;
float: left;
// 上边
#leftTop {
width: 400px;
position: relative;
// 小图框
#smallPic {
width: 400px;
height: 400px;
border: 1px solid #dfdfdf;
position: relative;
// 小图片
img {}
// 蒙版元素
.mask {
width: 200px;
height: 200px;
background-color: rgba(255, 255, 255, .5);
border: 1px solid #ddd;
position: absolute;
left: 0px;
top: 0px;
}
}
// 大图框
#bigPic {
width: 400px;
height: 400px;
border: 1px solid #ddd;
position: absolute;
left: 420px;
top: 0px;
overflow: hidden;
img {
width: 800px;
height: 800px;
position: absolute;
left: 0;
top: 0;
}
}
}
// 下边缩略图
#leftBottom {
width: 400px;
margin-top: 5px;
a {
width: 10px;
height: 54px;
border: 1px solid #ccc;
background-color: #ebebeb;
text-align: center;
line-height: 54px;
float: left;
&:first-child {
margin-right: 4px;
}
}
#piclist {
width: 372px;
height: 56px;
float: left;
overflow: hidden;
position: relative;
ul {
// 小li强制不换行
white-space: nowrap;
font-size: 0px;
position: absolute;
left: 0px;
transition: .5s;
li {
width: 50px;
height: 50px;
border: 1px solid #ccc;
padding: 2px;
margin-right: 20px;
display: inline-block;
img {
width: 50px;
height: 50px;
}
}
}
}
}
}
JavaScript代码:
// 放大镜
bigClassBing()
function bigClassBing() {
// 放大镜的移入移出效果
/**
* 思路:
* 1、获取小图框元素对象,并且设置移入事件(onmouseenter无冒泡,onmouseover有冒泡)
* 2、动态的创建蒙版元素以及大图框和大图片元素
* 3、移出时(onmouseleave)需要移除蒙版元素和大图框
*/
//1.获取小图框元素
const smallPic = document.querySelector('#smallPic')
//获取leftTop元素
const leftTop = document.querySelector('#leftTop')
//获取数据
var imagessrc = goodData.imagessrc;
//2.设置移入事件
smallPic.addEventListener('mouseenter', () => {
// 3.创建蒙版元素
const maskDiv = document.createElement('div')
maskDiv.className = 'mask'
// 4.创建大图框元素
const bigPic = document.createElement('div')
bigPic.id = 'bigPic'
// 5.创建大图片元素
const bigImg = document.createElement('img')
bigImg.src = imagessrc[bigimgIndex].b
// 6.大图框来追加大图片
bigPic.appendChild(bigImg)
// 7.小图框追加蒙版元素
smallPic.appendChild(maskDiv)
// 8.leftTop追加大图框元素
leftTop.appendChild(bigPic)
// 设置移动事件
smallPic.addEventListener('mousemove', function (e) {
//e.clientX: 鼠标点距离浏览器左侧X轴的值
//getBoundingClientRect().left:小图框元素距离浏览器左侧可视left值
//offsetWidth:为元素的占位宽度
let left = e.clientX - smallPic.getBoundingClientRect().left - maskDiv.offsetWidth / 2
let top = e.clientY - smallPic.getBoundingClientRect().top - maskDiv.offsetHeight / 2
// 蒙版元素边界控制
// smallPic.clientWidth为小图框的可视宽度
// left大于小图框的可视宽度-蒙版的占位宽度时,蒙版就会超出小图框
if (left < 0) {
left = 0
} else if (left > smallPic.clientWidth - maskDiv.offsetWidth) {
left = smallPic.clientWidth - maskDiv.offsetWidth
}
if (top < 0) {
top = 0
} else if (top > smallPic.clientHeight - maskDiv.offsetHeight) {
top = smallPic.clientHeight - maskDiv.offsetHeigh
}
// 设置蒙版的left和top
maskDiv.style.left = left + 'px'
maskDiv.style.top = top + 'px'
// 大图框里的大图片和蒙版等比例移动
// 移动的比例关系 = 蒙版元素移动的距离 / 大图片元素移动的距离
//蒙版元素移动的距离 = 小图框宽度 – 蒙版元素的宽度
//大图片元素移动的距离 = 大图片宽度 – 大图框元素的宽度
const scale = (smallPic.clientWidth - maskDiv.offsetWidth) / (bigImg.offsetWidth - bigPic.clientWidth)
bigImg.style.left = -left / scale + 'px'
bigImg.style.top = -top / scale + 'px'
})
// 设置移出事件
smallPic.addEventListener('mouseleave', () => {
// 小图框移除蒙版元素
smallPic.removeChild(maskDiv)
// leftTop移除大图框
leftTop.removeChild(bigPic)
})
})
}
// 动态渲染放大镜缩略图的数据
thumbnailData()
function thumbnailData() {
/**
* 思路:
* 1、先获取piclist元素下的ul
* 2、在获取data.js文件下的goodData->imagessrc
* 3、遍历数组,根据数组的长度来创建li元素
* 4、让ul遍历追加li元素
*/
// 1.获取piclist下的ul
const ul = document.querySelector('#piclist ul')
//2.获取imagessrc数据
const imagessrc = goodData.imagessrc
//3.遍历数组
for (let i = 0; i < imagessrc.length; i++) {
const newLi = document.createElement('li')
newLi.innerHTML = `<img data-id=${i} src=${imagessrc[i].s} alt="">`
ul.appendChild(newLi)
}
}
// 点击缩略图效果
thumbnailClick()
function thumbnailClick() {
/* 思路:
1.将点击事件绑定在ul上,利用事件冒泡完成对每个缩略图的点击效果
2.点击缩略图需要确定其下标位置来找到对应小图路径和大图路径替换现有src的值
*/
// 1.获取ul元素
const ul = document.querySelector('#piclist ul')
//2.获取imagessrc数据
const imagessrc = goodData.imagessrc
// 获取小图框里的img
const smallImg = document.querySelector('#smallPic img')
// 设置小图框图片的默认值
console.log(smallImg);
smallImg.src = imagessrc[0].s
// 2.ul绑定点击事件
ul.addEventListener('click', function (e) {
const { tagName, dataset } = e.target
// 判断是否点击的是img元素
if (tagName === 'IMG') {
smallImg.src = imagessrc[dataset.id].s
bigimgIndex = dataset.id
}
})
}
// 点击缩略图左右箭头的效果
thumbnailLeftRightClick()
function thumbnailLeftRightClick() {
/**
* 思路:
* 1、先获取左右两端的箭头按钮
* 2、再获取可视的div以及ul元素和所有的li元素
* 3、计算(发生起点、步长、总体运动的距离值)
* 4、然后再发生点击事件
*/
//1、获取元素
const prev = document.querySelector('#leftBottom a.prev')
const next = document.querySelector('#leftBottom a.next')
console.log(prev);
const piclist = document.querySelector('#piclist')
const ul = document.querySelector('#piclist ul')
const liNodes = document.querySelectorAll('#piclist ul li')
// 2.计算
// 发生起点start为ul右边框离div右边框的距离,起始值是0
let start = 0;
// 步长为两个li的宽度(包含间隙)
const step = (liNodes[0].offsetWidth + 20) * 2
// 最大能运动的距离 = ul的宽度 - div框的宽度 = (图片的总数 - div中显示的数量) * (li的宽度 + 20)
const endPosition = (liNodes.length - 5) * (liNodes[0].offsetWidth + 20)
// 3.绑定事件
prev.addEventListener('click', () => {
start -= step
start = start < 0 ? 0 : start
console.log(start);
ul.style.left = -start + 'px'
})
next.addEventListener('click', () => {
start += step
start = start > endPosition ? endPosition : start
console.log(start);
ul.style.left = -start + 'px'
})
}