【Web APIs】元素偏移量 offset 系列属性 ④ ( offset 属性案例 - 放大镜效果 )

文章目录

  • [一、offset 属性案例 - 放大镜效果](#一、offset 属性案例 - 放大镜效果)
    • [1、 需求说明](#1、 需求说明)
    • [2、HTML 结构](#2、HTML 结构)
    • [3、CSS 样式](#3、CSS 样式)
      • [① 清除内外边距](#① 清除内外边距)
      • [② 小图容器样式](#② 小图容器样式)
      • [③ 遮挡层样式](#③ 遮挡层样式)
      • [④ 大图容器样式](#④ 大图容器样式)
      • [⑤ 大图容器内的图片样式](#⑤ 大图容器内的图片样式)
    • [4、设置 window 对象 onload 事件](#4、设置 window 对象 onload 事件)
    • [5、显示 / 隐藏 遮挡层和大图容器](#5、显示 / 隐藏 遮挡层和大图容器)
    • 6、计算鼠标在小图容器中的坐标
    • 7、计算小图容器中遮挡层的位置
    • 8、大图片显示设置
  • [二、 完整代码示例](#二、 完整代码示例)
    • [1、 代码示例](#1、 代码示例)
    • 2、图片资源
    • [3、 执行效果](#3、 执行效果)

一、offset 属性案例 - 放大镜效果


1、 需求说明

实现如下放大镜效果 :

  • 小图容器 盒子 : 左侧是 小图容器 盒子 , 显示 小图片 ;
  • 遮挡层 : 在 小图容器 盒子上方 , 有一个 黄色的 遮挡层 ;
    • 遮挡层显示与隐藏 : 鼠标 进入 小图容器 盒子区域 显示遮挡层 , 鼠标 离开 小图容器 盒子区域 隐藏遮挡层 ;
    • 遮挡层移动 : 遮挡层 随鼠标 移动 , 且 遮挡层 移动区域 被限制在 小图容器 盒子 范围内 ;
  • 大图容器 盒子 : 右侧显示 大图容器盒子 , 其中显示大图片 , 并且只能 显示一部分大图片 ;
    • 大图容器显示范围 : 大图容器盒子 中 显示的大图片 , 随着 遮挡层 移动而移动 ;

2、HTML 结构

小图容器 <div class="small"> 用于 承载小图、遮罩层、大图容器的父容器 , 对应 CSS 中的 .small 类 ;

小图图片 <img src="small.png" alt=""> 充满整个 小图容器 , 展示需要放大的原始小图 ;

遮罩层 <div class="mask"></div> 是 鼠标拖动时选中小图的放大区域 , 对应 CSS 中的 .mask 类 ;

大图容器 <div class="big"> 展示遮罩层对应区域的放大图 , 对应 CSS 中的 .big 类 , 该容器 实际上 在 小图容器 右侧 ;

大图图片 <img src="big.jpg" alt="" class="bigImg"> 需要放大的高清图 , 对应CSS中的 .big img 样式 , 该盒子 大于 大图容器 盒子大小 ;

HTML 结构 部分代码示例 :

html 复制代码
    <!-- 小图容器:承载小图、遮罩层、大图容器的父容器,对应CSS中的.small类 -->
    <div class="small">
        <!-- 小图图片:展示需要放大的原始小图 -->
        <img src="small.png" alt="">
        <!-- 遮罩层:鼠标拖动时选中小图的放大区域,对应CSS中的.mask类 -->
        <div class="mask"></div>
        <!-- 大图容器:展示遮罩层对应区域的放大图,对应CSS中的.big类 -->
        <div class="big">
            <!-- 大图图片:需要放大的高清图,对应CSS中的.big img样式 -->
            <img src="big.jpg" alt="" class="bigImg">
        </div>
    </div>

3、CSS 样式

① 清除内外边距

使用 通配符选择器 * 选中页面中所有 HTML 元素 , 清除所有元素默认的 外边距 和 内边距 ;

css 复制代码
        * {
            /* 通配符选择器:选中页面中所有HTML元素 */
            /* 清除所有元素默认的外边距 */
            margin: 0;
            /* 清除所有元素默认的内边距 */
            padding: 0;
        }

② 小图容器样式

小图容器 是 放大镜效果的 小图展示区域 ,

该容器 作为内部 绝对定位元素 ( mask、big ) 的 定位父级 , 根据 " 子绝父相 " 原则 , 该容器 应该设置 相对定位 属性 ;

代码示例 :

css 复制代码
        .small {
            /* 小图容器样式:放大镜效果的小图展示区域 */
            /* 相对定位:作为内部绝对定位元素(mask、big)的定位父级 */
            position: relative;
            /* 设置小图容器的宽度为398像素 */
            width: 398px;
            /* 设置小图容器的高度为398像素 */
            height: 398px;
            /* 设置容器边框:1像素、实线、浅灰色 */
            border: 1px solid #ccc;
        }

③ 遮挡层样式

遮挡层 是 鼠标在小图上拖动的半透明遮罩 , 默认是 隐藏的 ;

遮挡层 的 位置 需要像素级移动 , 因此需要 设置为 绝对定位 或 固定定位 , 由于是 相对父元素 移动 , 而不是 相对浏览器页面 移动 , 因此这里 优先使用 绝对定位 , 父容器 设置 相对定位 ;

遮挡层 在 鼠标经过 small 小图容器 元素时 才显示 , 在 鼠标离开 small 小图容器 元素时 隐藏 ;

CSS 样式代码示例 :

css 复制代码
        .mask {
            /* 遮挡层样式:鼠标在小图上拖动的半透明遮罩 */
            /* 默认隐藏遮罩层:初始状态不显示 */
            display: none;
            /* 绝对定位:相对于父元素.small进行定位 */
            position: absolute;
            /* 遮罩层初始顶部偏移量:0像素(与父元素顶部对齐) */
            top: 0;
            /* 遮罩层初始左侧偏移量:0像素(与父元素左侧对齐) */
            left: 0;
            /* 遮罩层宽度:300像素 */
            width: 300px;
            /* 遮罩层高度:300像素 */
            height: 300px;
            /* 遮罩层背景色:淡黄色(放大镜选中区域的标识色) */
            background: #FEDE4F;
            /* 遮罩层透明度:0.5(半透明效果,能看到下方的小图) */
            opacity: .5;
            /* 遮罩层边框:1像素、实线、浅灰色 */
            border: 1px solid #ccc;
            /* 鼠标悬浮在遮罩层上时的光标样式:移动光标(提示可拖动) */
            cursor: move;
        }

④ 大图容器样式

大图容器 是 放大镜效果的大图展示区域 ;

该 大图容器 需要 使用 绝对定位 , 需要 相对于父元素 .small 进行定位 ;

大图容器 相对于 小图容器 设置 左侧偏移量 410 像素 , 小图容器 398 像素 , 与 小图容器 右侧 留出12 像素间距 ;

部分代码示例 :

css 复制代码
        .big {
            /* 大图容器样式:放大镜效果的大图展示区域 */
            /* 默认隐藏大图容器:初始状态不显示 */
            display: none;
            /* 绝对定位:相对于父元素.small进行定位 */
            position: absolute;
            /* 大图容器左侧偏移量:410像素(与小图容器右侧留出10像素间距) */
            left: 410px;
            /* 大图容器顶部偏移量:0像素(与小图容器顶部对齐) */
            top: 0;
            /* 大图容器宽度:500像素 */
            width: 500px;
            /* 大图容器高度:500像素 */
            height: 500px;
            /* 大图容器背景色:粉色(仅占位,实际会被大图覆盖) */
            background-color: pink;
            /* 层级设置:999(确保大图容器在其他元素上方显示) */
            z-index: 999;
            /* 大图容器边框:1像素、实线、浅灰色 */
            border: 1px solid #ccc;
            /* 溢出隐藏:大图超出容器的部分不显示(仅展示对应遮罩区域的放大内容) */
            overflow: hidden;
        }

⑤ 大图容器内的图片样式

大图容器内的图片 很大 , 大图容器 只能显示 部分图片 , 默认显示状态 只能显示 左上角 部分 图片区域 ;

代码示例 :

css 复制代码
        .big img {
            /* 大图容器内的图片样式 */
            /* 绝对定位:方便通过top/left属性调整大图位置,实现跟随遮罩移动 */
            position: absolute;
            /* 大图初始顶部偏移量:0像素 */
            top: 0;
            /* 大图初始左侧偏移量:0像素 */
            left: 0;
        }

4、设置 window 对象 onload 事件

为 BOM 的 window 对象添加 load 事件 , 等页面中所有的资源加载完毕后 , 才触发 onload 事件回调函数 ;

javascript 复制代码
window.addEventListener('load', function() {

5、显示 / 隐藏 遮挡层和大图容器

鼠标经过 small 小图容器 元素时 才显示 mask 遮挡层 和 big 大盒子 , 鼠标离开 small 小图容器 元素时 隐藏 mask 遮挡层 和 big 大盒子 ;

javascript 复制代码
            // 鼠标经过 small 小图容器 元素时 才显示  mask 遮挡层 和 big 大盒子
            small.addEventListener('mouseover', function() {
                mask.style.display = 'block';
                big.style.display = 'block';
            });

            // 鼠标离开 small 小图容器 元素时 隐藏  mask 遮挡层 和 big 大盒子
            small.addEventListener('mouseout', function() {
                mask.style.display = 'none';
                big.style.display = 'none';
            });

6、计算鼠标在小图容器中的坐标

鼠标 在 下图容器 中 移动的时候 , 让 遮挡层 盒子跟着鼠标来走 , 需要为 小图容器 设置 mousemove 事件 ;

无法直接获取 鼠标指针 相对于 指定盒子内部 的 指针位置 ;

参考 【Web APIs】元素偏移量 offset 系列属性 ③ ( offset 属性案例 - 计算鼠标指针在盒子内位置 | offset 属性案例 - 鼠标拖动盒子移动 ) 博客 , 计算 鼠标指针 在 指定 盒子模型 中的 位置 ;

  • 首先 , 计算 鼠标在页面中的坐标 ( e.pageX, e.pageY ) ;
  • 然后 , 计算 盒子在页面中的偏移 ( this.offsetLeft, this.offsetTop ) ;
  • 最后 , 计算 鼠标在盒子内的坐标 ( e.pageX - this.offsetLeft, e.pageY - box.this ) ;

部分代码示例 :

javascript 复制代码
            // 鼠标移动的时候 , 让 遮挡层 盒子跟着鼠标来走
            small.addEventListener('mousemove', function(e) {

                // 2. 计算出 鼠标 在 小图容器 盒子内的坐标
                var x = e.pageX - this.offsetLeft;
                var y = e.pageY - this.offsetTop;

7、计算小图容器中遮挡层的位置

鼠标 在 小图容器 中 , 鼠标指针的位置 对应的事 遮挡层 的 中心位置 ;

计算 遮挡层 左上角位置 , 需要 鼠标 位置 减去 遮挡层一半宽高 ;

javascript 复制代码
                var maskX = x - mask.offsetWidth / 2;
                var maskY = y - mask.offsetHeight / 2;

此外 , 还需要 处理 遮挡层 越界问题 , 遮挡层 移动 , 需要再 小图容器 范围内进行 , 不能超出边界 ;

遮挡层 的 水平方向最小值 left 肯定是 0 ,

然后 计算 遮挡层 的 水平方向 和 垂直方向 的 最大移动距离 ,

由于 遮挡层 和 外层盒子 是正方形 , 因此 该值也可是 垂直方向 的 最大移动距离 ;

javascript 复制代码
var maskMax = small.offsetWidth - mask.offsetWidth;

处理水平方向上的越界问题 ;

javascript 复制代码
                if (maskX <= 0) {
                    maskX = 0;
                } else if (maskX >= maskMax) {
                    maskX = maskMax;
                }

处理垂直方向上的越界问题 ;

javascript 复制代码
                if (maskY <= 0) {
                    maskY = 0;
                } else if (maskY >= maskMax) {
                    maskY = maskMax;
                }

最终通过 mask.style 设置 遮挡层 的 位置 ;

javascript 复制代码
                mask.style.left = maskX + 'px';
                mask.style.top = maskY + 'px';

部分代码示例 :

javascript 复制代码
               // 3. 计算 遮挡层 位置

                // 如果直接将 x, y 坐标设置给 mask.style.left 和 mask.style.top , 遮挡层 左上角 就是鼠标位置 
                // 这里需要设置 鼠标位置 是 遮挡层 中心位置 

                // 计算 遮挡层左上角位置 , 需要 鼠标 位置 减去 遮挡层一半宽高
                var maskX = x - mask.offsetWidth / 2;
                var maskY = y - mask.offsetHeight / 2;

                // 4. 处理 遮挡层越界问题 

                // 遮挡层 的 水平方向 的 最大移动距离
                // 由于 遮挡层 和 外层盒子 是正方形 , 因此 该值也可是 垂直方向 的 最大移动距离
                var maskMax = small.offsetWidth - mask.offsetWidth;

                // 处理水平方向上的越界问题
                if (maskX <= 0) {
                    maskX = 0;
                } else if (maskX >= maskMax) {
                    maskX = maskMax;
                }

                // 处理垂直方向上的越界问题
                if (maskY <= 0) {
                    maskY = 0;
                } else if (maskY >= maskMax) {
                    maskY = maskMax;
                }

                // 5. 设置遮挡层的偏移距离
                mask.style.left = maskX + 'px';
                mask.style.top = maskY + 'px';

8、大图片显示设置

使用如下公式 , 计算 大图片 的 移动距离 :

遮挡层移动距离 / 遮挡层的最大移动距离 = 大图片的移动距离 / 大图片最大移动距离

原理分析 :

鼠标指针 的位置 , 就是 遮挡层 的 中心位置 , 此时该中心位置 的比例是 " 遮挡层移动距离 / 遮挡层的最大移动距离 " , 对应 显示的 大图片 的中心位置 也是这个比例 ,

" 遮挡层移动距离 / 遮挡层的最大移动距离 " 比例值 是 确定的 , 可以计算出来 ;

大图片显示的 比例值 " 大图片的移动距离 / 大图片最大移动距离 " 等于 上面的 比例 ;

大图片最大移动距离 是确定的 , 现在需要计算出 大图片的移动距离 , 因此就有了如下 最终计算公式 ;

最终的计算公式 :

大图片的移动距离 = 遮挡层移动距离 * 大图片最大移动距离 / 遮挡层的最大移动距离

部分代码示例 :

javascript 复制代码
                // 大图元素
                var bigImg = document.querySelector('.bigImg');

                // 大图片最大移动距离
                var bigMax = bigImg.offsetWidth - big.offsetWidth;

                // 大图片的移动距离 X Y 计算 

                // 大图片的水平方向移动距离 = 遮挡层水平移动距离 * 大图片最大移动距离 / 遮挡层的最大移动距离
                var bigX = maskX * bigMax / maskMax;
                // 大图片的垂直方向移动距离 = 遮挡层垂直移动距离 * 大图片最大移动距离 / 遮挡层的最大移动距离
                var bigY = maskY * bigMax / maskMax;

                // 大图的移动距离是反方向的
                bigImg.style.left = -bigX + 'px';
                bigImg.style.top = -bigY + 'px';

二、 完整代码示例


1、 代码示例

html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">

<head>
    <meta charset="UTF-8">
    <title>放大镜效果</title>
    <style>
        * {
            /* 通配符选择器:选中页面中所有HTML元素 */
            /* 清除所有元素默认的外边距 */
            margin: 0;
            /* 清除所有元素默认的内边距 */
            padding: 0;
        }
        
        .small {
            /* 小图容器样式:放大镜效果的小图展示区域 */
            /* 相对定位:作为内部绝对定位元素(mask、big)的定位父级 */
            position: relative;
            /* 设置小图容器的宽度为398像素 */
            width: 398px;
            /* 设置小图容器的高度为398像素 */
            height: 398px;
            /* 设置容器边框:1像素、实线、浅灰色 */
            border: 1px solid #ccc;
        }
        
        .mask {
            /* 遮罩层样式:鼠标在小图上拖动的半透明遮罩 */
            /* 默认隐藏遮罩层:初始状态不显示 */
            display: none;
            /* 绝对定位:相对于父元素.small进行定位 */
            position: absolute;
            /* 遮罩层初始顶部偏移量:0像素(与父元素顶部对齐) */
            top: 0;
            /* 遮罩层初始左侧偏移量:0像素(与父元素左侧对齐) */
            left: 0;
            /* 遮罩层宽度:300像素 */
            width: 300px;
            /* 遮罩层高度:300像素 */
            height: 300px;
            /* 遮罩层背景色:淡黄色(放大镜选中区域的标识色) */
            background: #FEDE4F;
            /* 遮罩层透明度:0.5(半透明效果,能看到下方的小图) */
            opacity: .5;
            /* 遮罩层边框:1像素、实线、浅灰色 */
            border: 1px solid #ccc;
            /* 鼠标悬浮在遮罩层上时的光标样式:移动光标(提示可拖动) */
            cursor: move;
        }
        
        .big {
            /* 大图容器样式:放大镜效果的大图展示区域 */
            /* 默认隐藏大图容器:初始状态不显示 */
            display: none;
            /* 绝对定位:相对于父元素.small进行定位 */
            position: absolute;
            /* 大图容器左侧偏移量:410像素(与小图容器右侧留出10像素间距) */
            left: 410px;
            /* 大图容器顶部偏移量:0像素(与小图容器顶部对齐) */
            top: 0;
            /* 大图容器宽度:500像素 */
            width: 500px;
            /* 大图容器高度:500像素 */
            height: 500px;
            /* 大图容器背景色:粉色(仅占位,实际会被大图覆盖) */
            background-color: pink;
            /* 层级设置:999(确保大图容器在其他元素上方显示) */
            z-index: 999;
            /* 大图容器边框:1像素、实线、浅灰色 */
            border: 1px solid #ccc;
            /* 溢出隐藏:大图超出容器的部分不显示(仅展示对应遮罩区域的放大内容) */
            overflow: hidden;
        }
        
        .big img {
            /* 大图容器内的图片样式 */
            /* 绝对定位:方便通过top/left属性调整大图位置,实现跟随遮罩移动 */
            position: absolute;
            /* 大图初始顶部偏移量:0像素 */
            top: 0;
            /* 大图初始左侧偏移量:0像素 */
            left: 0;
        }
    </style>
    <script>
        // 为 BOM 的 window 对象添加 load 事件 , 等页面中所有的资源加载完毕后 , 才触发 onload 事件回调函数
        window.addEventListener('load', function() {

            // 事件源 , 需要再该 元素 上添加 鼠标移动事件
            var small = document.querySelector('.small');

            // 图像上可移动的半透明 遮挡层 
            var mask = document.querySelector('.mask');

            // 右侧显示大图的元素
            var big = document.querySelector('.big');

            // 鼠标经过 preview_img 元素时 才显示  mask 遮挡层 和 big 大盒子
            small.addEventListener('mouseover', function() {
                mask.style.display = 'block';
                big.style.display = 'block';
            });

            // 鼠标离开 preview_img 元素时 隐藏  mask 遮挡层 和 big 大盒子
            small.addEventListener('mouseout', function() {
                mask.style.display = 'none';
                big.style.display = 'none';
            });

            // 2. 鼠标移动的时候,让 遮挡层 盒子跟着鼠标来走
            small.addEventListener('mousemove', function(e) {
                // (1). 先计算出鼠标在盒子内的坐标
                var x = e.pageX - this.offsetLeft;
                var y = e.pageY - this.offsetTop;

                // 如果直接将 x, y 坐标设置给 mask.style.left 和 mask.style.top , 遮挡层 左上角 就是鼠标位置 
                // 这里需要设置 鼠标位置 是 遮挡层 中心位置 

                // 计算 遮挡层左上角位置 , 需要 鼠标 位置 减去 遮挡层一半宽高
                // (2) 减去盒子高度 300的一半 是 150 就是我们mask 的最终 left 和top值了
                // (3) 我们mask 移动的距离
                var maskX = x - mask.offsetWidth / 2;
                var maskY = y - mask.offsetHeight / 2;

                // 处理 遮挡层越界问题 
                // (4) 如果x 坐标小于了0 就让他停在0 的位置

                // 遮挡层 的 水平方向 的 最大移动距离
                // 由于 遮挡层 和 外层盒子 是正方形 , 因此 该值也可是 垂直方向 的 最大移动距离
                var maskMax = small.offsetWidth - mask.offsetWidth;

                // 处理水平方向上的越界问题
                if (maskX <= 0) {
                    maskX = 0;
                } else if (maskX >= maskMax) {
                    maskX = maskMax;
                }

                // 处理垂直方向上的越界问题
                if (maskY <= 0) {
                    maskY = 0;
                } else if (maskY >= maskMax) {
                    maskY = maskMax;
                }

                // 设置遮挡层的偏移距离
                mask.style.left = maskX + 'px';
                mask.style.top = maskY + 'px';

                // 3. 大图片的移动距离 = 遮挡层移动距离 * 大图片最大移动距离 / 遮挡层的最大移动距离

                // 大图元素
                var bigImg = document.querySelector('.bigImg');

                // 大图片最大移动距离
                var bigMax = bigImg.offsetWidth - big.offsetWidth;

                // 大图片的移动距离 X Y 计算 

                // 大图片的水平方向移动距离 = 遮挡层水平移动距离 * 大图片最大移动距离 / 遮挡层的最大移动距离
                var bigX = maskX * bigMax / maskMax;
                // 大图片的垂直方向移动距离 = 遮挡层垂直移动距离 * 大图片最大移动距离 / 遮挡层的最大移动距离
                var bigY = maskY * bigMax / maskMax;

                // 大图的移动距离是反方向的
                bigImg.style.left = -bigX + 'px';
                bigImg.style.top = -bigY + 'px';

            });

        });
    </script>
</head>

<body>
    <!-- 小图容器:承载小图、遮罩层、大图容器的父容器,对应CSS中的.small类 -->
    <div class="small">
        <!-- 小图图片:展示需要放大的原始小图 -->
        <img src="small.png" alt="">
        <!-- 遮罩层:鼠标拖动时选中小图的放大区域,对应CSS中的.mask类 -->
        <div class="mask"></div>
        <!-- 大图容器:展示遮罩层对应区域的放大图,对应CSS中的.big类 -->
        <div class="big">
            <!-- 大图图片:需要放大的高清图,对应CSS中的.big img样式 -->
            <img src="big.jpg" alt="" class="bigImg">
        </div>
    </div>
</body>

</html>

2、图片资源

小图片:

大图片 :

3、 执行效果

执行效果 :

进入界面后 , 展示如下效果 :

完整执行效果如下 :

相关推荐
宁雨桥1 小时前
前端网页加载进度条实现指南:Vue3+Vite工程化场景
前端·javascript·性能优化
Mike_jia1 小时前
ZabbixWatch:打造现代化运维监控大屏,让数据掌控触手可及
前端
John_ToDebug1 小时前
深入探索 Chrome 中渲染进程与浏览器进程之间的 Mojo IPC 通信机制
前端·chrome·mojo
m0_471199631 小时前
【JavaScript】forEach 和 map 核心区别(附示例+选型)
开发语言·前端·javascript
chilavert3182 小时前
技术演进中的开发沉思-235 Ajax:动态数据(上)
javascript·ajax·okhttp
克喵的水银蛇2 小时前
Flutter 通用搜索框:SearchBarWidget 一键实现搜索、清除与防抖
前端·javascript·flutter
CHANG_THE_WORLD2 小时前
Python 可变参数详解与代码示例
java·前端·python
鹏多多2 小时前
flutter-屏幕自适应插件flutter_screenutil教程全指南
android·前端·flutter
m0_471199632 小时前
【JavaScript】Map对象和普通对象Object区别
开发语言·前端·javascript