如何实现一个丝滑的放大镜效果
思路
- 一个容器包裹两张图片
- 一份静态图片一直放着
- 一份动态移动位置的图片 外面包裹一层作为放大镜 设置溢出隐藏
- 绑定移动事件 让图片和图片放大镜移动位置相反即可
效果
由于gif帧率问题,看着卡,实际丝滑无比
实现
配置文件
const config = {
w: 640,
h: 360,
moveHeight: 60,
moveWidth: 60,
scale: 2.2
};
分别是图片大小,放大镜大小,缩放大小
获取DOM
const container = document.querySelector('.container'),
staticImg = container.querySelector('.static-img img'),
move = container.querySelector('.move'),
moveImg = move.querySelector('img');
初始化配置
function setSize() {
staticImg.style.width = config.w + 'px';
staticImg.style.height = config.h + 'px';
container.style.height = config.h + 'px';
container.style.width = config.w + 'px';
move.style.width = config.moveWidth + 'px';
move.style.height = config.moveHeight + 'px';
move.style.transform = `scale(${config.scale})`;
moveImg.style.width = config.w + 'px';
moveImg.style.height = config.h + 'px';
}
事件
- 首先需要显示和隐藏放大镜
function setVisible() {
const flag = getComputedStyle(move).display === 'none'
? 'block'
: 'none';
move.style.display = flag;
}
container.addEventListener('mouseover', setVisible);
container.addEventListener('mouseout', setVisible);
通过取反的方式,复用函数
- 接下来是重点了,处理移动逻辑,先写俩辅助函数
function getRect(el) {
return el.getBoundingClientRect();
}
function getStyle(el, key) {
return parseInt(getComputedStyle(el)[key]);
}
const { left: _left, top: _top } = getRect(container),
halfWidth = getStyle(move, 'width') / 2,
halfHeight = getStyle(move, 'height') / 2;
container.addEventListener('mousemove', (e) => {
const { clientX, clientY } = e;
const x = clientX - _left - halfWidth,
y = clientY - _top - halfHeight;
move.style.transform = `translate(scale(${config.scale}) ${x}px, ${y}px)`;
moveImg.style.transform = `translate(${-x}px, ${-y}px)`;
});
经典问答环节
你为什么要搞个
getBoundingClientRect
,很帅吗?offsetLeft
不能用吗??
- 因为
getBoundingClientRect
是获取全部矩形的属性,包括left、translate等等那你为什么要搞个
getComputedStyle
,很帅吗?el.style.width
不能用吗??
el.style.width
不能只能获取行内样式,不包括类的样式el.style.width
不能获取隐藏元素的样式
这里用鼠标坐标 - 容器偏移位置得到移动位置
注意,还要减去图片一半的大小,因为坐标在中心
现在能用吗??
不能,让你看看先
为什么偏移越来越多呢?
因为transform
属性是有顺序的,这里先放大再移动,所以改一下即可
move.style.transform = `translate(${x}px, ${y}px) scale(${config.scale})`;
能用了吗??
还是不能,超出边界了怎么办
function judge(x, y) {
return (
x < -halfWidth || y < -halfHeight ||
x > container.offsetWidth - halfWidth ||
y > container.offsetHeight - halfHeight
);
}
container.addEventListener('mousemove', (e) => {
const { clientX, clientY } = e;
const x = clientX - _left - halfWidth,
y = clientY - _top - halfHeight;
if (judge(x, y)) {
return;
}
move.style.transform = `translate(${x}px, ${y}px) scale(${config.scale})`;
moveImg.style.transform = `translate(${-x}px, ${-y}px)`;
});
加个判断边界即可,允许超出一半,大功告成
转载自:https://juejin.cn/post/7255131194165821498