pink老师-APIs总结实战训练(小兔鲜儿商品页面)
1)顶部导航模块
需求:
- 顶部导航开始不显示(css样式中top初始为-80px)
- 等页面滑到主导航栏,这个新顶部导航栏滑动下拉显示,并且改为固定定位
- 等页面滑到上面,新顶部导航栏隐藏
代码:
// 1. 顶部导航模块
(function () {
const sticky = document.querySelector('.sticky')
const header = document.querySelector('.xtx_header .wrapper')
// 目标位置 = 主导航栏的位置 + 主导航栏的高度
const top = header.offsetTop + header.clientHeight
window.addEventListener('scroll', function () {
const n = document.documentElement.scrollTop
if (n >= top) {
sticky.style.top = '0px';
} else {
sticky.style.top = '-80px';
}
})
})()
2)图片切换及放大镜效果
需求:
- 鼠标经过对应小盒子,左侧中等盒子显示对应中等图片
- 鼠标经过中盒子,右侧会显示放大镜效果的大盒子
- 黑色遮罩盒子跟着鼠标来移动
- 鼠标在中等盒子上移动,大盒子上的图片也跟着显示对应位置
1. 鼠标经过对应小盒子,左侧中等盒子显示对应中等图片
a. 点击小盒子高亮,排它思想
b. 根据小盒子的src换中盒子的src,大盒子也跟着更换背景图片
c. 为什么用mouseover,因为需要用到事件委托,它可以冒泡,而mouseenter不行
注意:
javascript中mouseover
和mouseenter
的区别主要在于监听对象的子元素是否触发事件。
mouseover
:鼠标移入监听对象中,或者从监听对象的一个子元素移入另一个子元素中时触发该事件。
mouseenter
:鼠标移入监听对象时触发,在监听对象内移动不会触发
代码:
// 2.1 切换图片
// 获取三个盒子
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') {
// 排他思想
small.querySelector('.active').classList.remove('active')
e.target.parentNode.classList.add('active')
// 根据小盒子的src更换中盒子,大盒子的src
middle.querySelector('img').src = e.target.src
large.style.backgroundImage = `url(${e.target.src})`
}
})
2. 鼠标经过中盒子,右侧会显示放大镜效果的大盒子
a. 用到鼠标经过和离开,鼠标经过中盒子,大盒子 利用 display 来显示和隐藏
b. 鼠标离开不会立马消失,而是有200ms的延时,用户体验更好,所以尽量使用定时器做个延时 setTimeout,显示时需要关闭延时器,可能会出现
c. 显示和隐藏也尽量定义一个函数,因为鼠标经过离开中等盒子,会显示隐藏,同时,鼠标经过大盒子,也会显示和隐藏
e. 给大盒子里面的背景图片一个默认的第一张图片
f. 鼠标经过中盒子,显示黑色遮罩层
代码:
// 2.2 经过中盒子,大盒子显示
middle.addEventListener('mouseover', show)
middle.addEventListener('mouseleave', hide)
let timeId = 0
function show() {
clearTimeout(timeId)
large.style.display = 'block'
}
function hide() {
timeId = setTimeout(function () {
large.style.display = 'none'
}, 200)
}
// 这里还需要到样式中给大盒子添加默认背景图片
large.addEventListener('mouseover', show)
large.addEventListener('mouseleave', hide)
3. 黑色遮罩盒子跟着鼠标来移动
a. 先做鼠标经过 中等盒子,显示隐藏 黑色遮罩 的盒子
b. 让黑色遮罩跟着鼠标来走, 需要用到鼠标移动事件 mousemove
c. 让黑色盒子的移动的核心思想:不断把鼠标在中等盒子内的坐标给黑色遮罩层 let top 值,这样遮罩层就可以跟着移动了
代码:
// 2.3 黑色遮罩层跟着鼠标来移动
middle.addEventListener('mousemove', function (e) {
// 鼠标在middle 盒子里面的坐标 = 鼠标在页面中的坐标 - middle 中等盒子的坐标
let x = e.pageX - middle.getBoundingClientRect().left
let y = e.pageY - middle.getBoundingClientRect().top - document.documentElement.scrollTop
// 限定黑色遮罩在 middle 盒子内移动的距离
if (x >= 0 && x <= 400 && y >= 0 && y <= 400) {
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'
// 大盒子的背景图片要跟随 中等盒子移动 存在的关系是 2倍
large.style.backgroundPositionX = -2 * mx + 'px'
large.style.backgroundPositionY = -2 * my + 'px'
}
})
注意:
a. 相当于在中间一个小正方形中移动
b. 竖直方向需要减掉被卷去的头部
c. 为什么不用 box.offsetLet 和 box.offsetTop 因为这俩属性跟带有定位的父级有关系,很容被父级影响,而getBoundingClientRect() 不受定位的父元素的影响
d. 确定遮罩盒子的位置时,需要减去盒子的一半