likes
comments
collection
share

如何实现一个丝滑的放大镜效果

作者站长头像
站长
· 阅读数 10

思路

  • 一个容器包裹两张图片
  • 一份静态图片一直放着
  • 一份动态移动位置的图片 外面包裹一层作为放大镜 设置溢出隐藏
  • 绑定移动事件 让图片和图片放大镜移动位置相反即可

效果

由于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)`;
});

加个判断边界即可,允许超出一半,大功告成

源码 gitee.com/cjl2385/dig…