<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.middleBox {
width: 400px;
height: 400px;
border: 1px solid #000;
position: relative;
}
.middleBox img {
width: 400px;
height: 400px;
}
.shade {
width: 100px;
height: 100px;
background: yellow;
position: absolute;
left: 0;
top: 0;
opacity: 0.5;
display: none;
}
.smallBox {
margin-top: 10px;
}
.smallBox img {
border: 1px solid #000;
margin-left: 5px;
width: 50px;
height: 50px;
}
.smallBox img.active {
border-color: red;
}
.box {
margin: 50px;
width: 402px;
position: relative;
}
.bigBox {
width: 400px;
height: 400px;
border: 1px solid #000;
position: absolute;
top: 0;
left: 105%;
display: none;
background-image: url(./images/big1.jpg);
background-position: 0 0;
background-size: 1600px 1600px;
background-repeat: no-repeat;
}
.shade:hover {
cursor: move;
}
</style>
</head>
<body>
<div class="box">
<div class="middleBox">
<img src="./images/big1.jpg" width="400" height="400">
<div class="shade"></div>
</div>
<div class="smallBox">
<img class="active" src="./images/big1.jpg">
<img src="./images/big2.jpg" alt="">
</div>
<div class="bigBox"></div>
</div>
</body>
</html>
<script src="common.js"></script>
<script>
/*
思路分析
1、用户点击小图
a、获取到当前小图的地址 赋值给中图
b、当前点击的添加特殊样式
c、大图的背景图片需要修改
2、用户在中图上移动
a)绑定鼠标划入事件 设置划入之后显示遮罩与大图
b)绑定鼠标移动事件 需要根据遮罩距离左侧的距离与中图的宽度计算出百分比,根据百分比设置大图背景的位置
c)绑定鼠标的划出事件 设置移动事件失效并且将其他的内容隐藏
*/
// 1、获取常用的dom对象
const boxDom = document.querySelector('.box');
const middleBoxDom = document.querySelector('.middleBox');
const shadeDom = document.querySelector('.shade');
const smallBoxDom = document.querySelector('.smallBox');
const bigBoxDom = document.querySelector('.bigBox');
// 2、实现小图的点击事件
smallBoxDom.onclick = function (event) {
if (event.target.tagName == 'IMG') {
// 1、设置特殊样式
smallBoxDom.querySelector('.active').className = '';
event.target.className = 'active'
// 2、设置中图的图片 middleBoxDom.querySelector('img')表示在middleBoxDom对应的标签下寻找img标签
middleBoxDom.querySelector('img').src = event.target.src;
// 3、设置大图的图片
bigBoxDom.style.backgroundImage = `url(${event.target.src})`
}
}
// 3、处理中图的鼠标划入事件
middleBoxDom.onmouseover = function () {
// 显示遮罩
shadeDom.style.display = 'block';
// 显示大图
bigBoxDom.style.display = 'block';
// 绑定鼠标的移动事件
middleBoxDom.onmousemove = function (event) {
// 在获取鼠标坐标点时不能使用offsetX/offsetY,因为移动事件事件源会改变导致坐标点是错误的。所以可以使用clientX/clientY获取到相对于浏览器的坐标点(永远是准确的),减掉box左侧/上侧的距离,就可以得到坐标点相对图片的距离
let x = event.clientX;
let y = event.clientY;
// 遮罩宽度与高度的一半
let shadeWidthHalf = shadeDom.clientWidth / 2;
let shadeHeightHalf = shadeDom.clientHeight / 2;
// 需要限制xy的值 从而限制遮罩只能在中图中
// 如果鼠标坐标点 位置比 box的左侧距离+遮罩宽度的异步 还要要小 就需要限制在左侧
if (x < boxDom.offsetLeft + shadeWidthHalf) {
x = boxDom.offsetLeft + shadeWidthHalf
}
if (x > boxDom.offsetLeft + boxDom.clientWidth - shadeWidthHalf) {
x = boxDom.offsetLeft + boxDom.clientWidth - shadeWidthHalf
}
if (y < boxDom.offsetTop + shadeHeightHalf) {
y = boxDom.offsetTop + shadeHeightHalf
}
if (y > boxDom.offsetTop + middleBoxDom.clientHeight - shadeHeightHalf) {
y = boxDom.offsetTop + middleBoxDom.clientHeight - shadeHeightHalf
}
shadeDom.style.left = x - boxDom.offsetLeft - shadeWidthHalf + 'px';
shadeDom.style.top = y - boxDom.offsetTop - shadeHeightHalf + 'px';
// 计算出遮罩在中图中的百分比
// 以遮罩左上角的距离与中图的尺寸计算出百分比
let xPercentage = (x - boxDom.offsetLeft - shadeWidthHalf) / middleBoxDom.clientWidth;
let yPercentage = (y - boxDom.offsetTop - shadeHeightHalf) / middleBoxDom.clientHeight;
// 获取背景图的原始尺寸
let bigBoxSize = getStyle(bigBoxDom, 'backgroundSize');
let bigBoxWidth = parseInt(bigBoxSize.split(' ')[0])
let bigBoxHeight = parseInt(bigBoxSize.split(' ')[1])
// 计算背景移动的位置
let xMove = bigBoxWidth * xPercentage;
let yMove = bigBoxHeight * yPercentage;
bigBoxDom.style.backgroundPosition = `-${xMove}px -${yMove}px`
}
}
// 4、鼠标离开事件
middleBoxDom.onmouseout = function () {
shadeDom.style.display = 'none';
bigBoxDom.style.display = 'none';
middleBoxDom.onmousemove = null;
}
</script>
封装公共函数
/**
* 生成随机数
* @param {Number} min 开始数字
* @param {Number} max 结束数字
* @param {Boolean} hasEnd 是否包含结束位置 true包含 否则 不包含
* @returns {Number}
*/
function makeRandom(min, max, hasEnd) {
// return Math.floor(Math.random()*(max-min+hasEnd?1:0)+min);
if (hasEnd) {
return Math.floor(Math.random() * (max - min + 1) + min);
}
return Math.floor(Math.random() * (max - min) + min);
}
/**
* 获取元素的样式
* @param {Element} elem 需要获取样式元素的DOM对象
* @param {String} attr 需要获取的样式名称
*/
function getStyle(elem, attr) {
if (window.getComputedStyle) {
// 证明 window对象下存在 getComputedStyle属性的(方法) 所以就是满足w3c标准的浏览器
// 因为attr 是变量 window.getComputedStyle(elem)的到的是对象 所以 需要使用数组语法
return window.getComputedStyle(elem)[attr];
}
// 代码执行到这一行 绝对证明 是IE的低版本
return elem.currentStyle[attr];
}
/**
* 批量设置样式
* @param {Document} elem 元素dom对象
* @param {Object} options 对象格式 表示具体设置的样式信息 属性名称是样式名称,属性值是设置的样式值
* @returns {String}
*/
function setStyle(elem, options) {
for (var key in options) {
elem.style[key] = options[key];
}
}
/**
* 绑定事件
* @param {Document} elem 绑定事件的dom对象
* @param {String} eventName 事件名称 是没有on前缀的名称
* @param {Function} handler 事件处理程序
*/
function bindEvent(elem,eventName,handler){
if(window.addEventListener){
// 判断是w3c浏览器
return elem.addEventListener(eventName,handler);
}
return elem.attachEvent('on'+eventName,handler);
}
/**
* 解除事件监听的绑定
* @param {Document} elem 绑定事件的dom对象
* @param {String} eventName 事件名称 是没有on前缀的名称
* @param {Function} handler 事件处理程序
*/
function unbindEvent(elem,eventName,handler){
try {
elem.removeEventListener(eventName,handler);
} catch (error) {
elem.detachEvent('on'+eventName,handler);
}
}