案例--放大镜

放大镜的构造函数

类名:

  box: 整个放大镜区域的盒子

  show: 一个 div, 内部是展示的图片

  mask: 在 show 的子级元素, 是图片上的遮罩层

  list: 下面切换图片的列表盒子

    => list > p > img

  enlarge: box 的子级元素, 就是承载放大图片的盒子

需求:

  0. 调整比例(选做)

    => 可以自己使用计算器计算, 去调整 css

    => 也可以通过 js 代码来计算出比例, 算出一个尺寸去赋值

  1. 移入移出

    => 移入 show 的时候, mask 和 enlarge 显示

    => 移出 show 的时候, mask 和 enlarge 消失

  2. 点击切换

    => 点击 list 下面的 p 标签的时候

    => 切换 show 下面 img 标签的 图片

    => 切换 enlarge 盒子的 背景图片

  3. 鼠标跟随

    => 当鼠标在 show 内移动的时候

    => mask 跟随移动

    => enlarge 盒子的 backgroundPosition 成比例跟随移动

自己不作为事件目标    // pointer-events: none;

属性:

  ele: 范围元素

  show: 显示图片的盒子

  list: 切换列表的盒子

  mask: 遮罩层盒子

  enlarge: 放大镜盒子

  show 盒子的尺寸

  mask 盒子的尺寸

  背景图的尺寸

方法:

  调整比例

  移入移出

  列表切换

  跟随移动


function Enlarge(select) {
  // select 表示范围元素的 选择器
  this.ele = document.querySelector(select)
  // show 盒子
  this.show = this.ele.querySelector('.show')
  // mask 盒子
  this.mask = this.ele.querySelector('.mask')
  // list 盒子
  this.list = this.ele.querySelector('.list')
  // enlarge 盒子
  this.enlarge = this.ele.querySelector('.enlarge')
  // show 的宽度
  // 元素.offsetWidth 获取的是元素 内容 + padding + border 区域的尺寸
  // 注意: 当元素 display: none; 的时候获取不到尺寸
  this.show_width = this.show.offsetWidth
  // show 的高度
  this.show_height = this.show.offsetHeight
  // mask 的宽度
  // 获取元素非行内样式
  // window.getComputedStyle(元素).样式名
  this.mask_width = parseInt(window.getComputedStyle(this.mask).width)
  // mask 的高度
  this.mask_height = parseInt(window.getComputedStyle(this.mask).height)
  // 背景图 的宽度
  // 获取元素非行内样式
  this.bg_width = parseInt(window.getComputedStyle(this.enlarge).backgroundSize.split(' ')[0])
  this.bg_height = parseInt(window.getComputedStyle(this.enlarge).backgroundSize.split(' ')[1])

  // 调用启动器就可以了
  this.init()
}

// 0. 启动器方法
Enlarge.prototype.init = function () {
  this.setScale()
  this.overOut()
  this.changeList()
  this.move()
}

// 1. 调整比例
/*
  已知:
    show 的尺寸
    mask 的尺寸
    背景图 的尺寸

      show 盒子尺寸         背景图尺寸
    --------------- = ------------------
      mask 盒子尺寸      enlarge 盒子尺寸
    公式变形: 背景图尺寸 * mask 盒子尺寸 = enlarge 盒子尺寸 * show 盒子尺寸
    公式变形: enlarge 盒子尺寸 = 背景图尺寸 * mask 盒子尺寸 / show 盒子尺寸
*/
Enlarge.prototype.setScale = function () {
  // 1-1. 计算出 enlarge 盒子的尺寸
  this.enlarge_width = this.bg_width * this.mask_width / this.show_width
  this.enlarge_height = this.bg_height * this.mask_height / this.show_height

  // 1-2. 给 this.enlarge 盒子赋值
  this.enlarge.style.width = this.enlarge_width + 'px'
  this.enlarge.style.height = this.enlarge_height + 'px'
}

// 2. 移入移出
/*
  this.show 绑定移入移出事件, 操作两个元素显示隐藏
*/
Enlarge.prototype.overOut = function () {
  // 2-1. 绑定鼠标移入事件
  this.show.addEventListener('mouseover', () => {
    this.mask.style.display = 'block'
    this.enlarge.style.display = 'block'
  })

  // 2-2. 绑定鼠标移出事件
  this.show.addEventListener('mouseout', () => {
    this.mask.style.display = 'none'
    this.enlarge.style.display = 'none'
  })
}

// 3. 列表切换
/*
  问题1: 给谁绑定事件 ?
    => 事件委托, 委托给谁 ?
    => 委托给 list
  问题2: 如何判断 target ?
    => 判断 target 是 img 标签
    => 才进行切换
  问题3: 把 show 盒子下面的 img 换成什么 ?
    => 把需要用到的图片地址, 以自定义属性的形式写在 img 标签身上
    => 当你点击的时候, 拿到 img 标签身上记录的两个自定义属性就好了
*/
Enlarge.prototype.changeList = function () {
  this.list.addEventListener('click', e => {
    // 处理事件对象兼容
    e = e || window.event
    // 处理事件目标兼容
    const target = e.target || e.srcElement

    // 判断点击的是 img 标签
    // 节点属性: 元素节点的 nodeName: 大写标签名
    // target是准确触发事件的元素, 如果准确触发事件的元素的标签名是 IMG, 那么证明我点击的是 要切换的图片
    // 执行切换的操作
    if (target.nodeName === 'IMG') {
      const show_url = target.dataset.show
      const enlarge_url = target.dataset.enlarge

      // 更换地址
      this.show.firstElementChild.src = show_url
      this.enlarge.style.backgroundImage = `url(${ enlarge_url })`

      // 更换 p 标签的 active 类名
      // 给 list 的所有子元素取消 active
      for (let i = 0; i < this.list.children.length; i++) {
        this.list.children[i].classList.remove('active')
      }
      target.parentElement.classList.add('active')
    }
  })
}

// 4. 跟随移动
/*
  问题1: 给谁绑定鼠标移动事件 ?
    => this.show 盒子绑定 mousemove 事件
  问题2: 获取哪一组坐标 ?
    => page 一组: 相对于文档流左上角
    => client 一组: 相对于浏览器可视窗口左上角
    => offset 一组: 相对于事件目标左上角
    => mask 盒子是根据 show 盒子进行定位的
    => 原因: 因为当光标进入 mask 盒子以后, 事件目标变成了 mask 盒子, 导致拿到的坐标是相对于 mask 的
    => 解决: 当光标进入 mask 盒子以后, 事件目标不是 mask 盒子依旧还是 show 盒子
      -> 给 mask 盒子添加一个 css 样式, 叫做 pointer-events: none;
      -> 作用: 该元素永远不作为事件目标使用, 所有事件在自己身上发生的时候
      -> 直接穿透过自己, 到达结构父级身上
  问题3: 是否需要边界值判断 ?
    => 需要
    => this.show 的尺寸就是边界
  问题4: 如何设置背景图定位的值 ?
    => 如何计算
      mask 移动距离        mask 盒子尺寸
    ---------------- = ------------------
      背景图移动距离      enlarge 盒子尺寸
    公式变形: 背景图移动距离 * mask 盒子尺寸 = mask 移动距离 * enlarge 盒子尺寸
    公式变形: 背景图移动距离 = mask 移动距离 * enlarge 盒子尺寸 / mask 盒子尺寸
*/
Enlarge.prototype.move = function () {
  // 4-1. 给 this.show 绑定事件
  this.show.addEventListener('mousemove', e => {
    // 处理事件对象兼容
    e = e || window.event

    // 4-2. 拿到坐标点
    let x = e.offsetX - this.mask_width / 2
    let y = e.offsetY - this.mask_height / 2

    // 4-3. 边界值判断
    if (x <= 0) x = 0
    if (y <= 0) y = 0
    if (x >= this.show_width - this.mask_width) x = this.show_width - this.mask_width
    if (y >= this.show_height - this.mask_height) y = this.show_height - this.mask_height

    // 4-4. 给 this.mask 进行 left 和 top 的赋值
    this.mask.style.left = x + 'px'
    this.mask.style.top = y + 'px'

    // 4-5. 让 enlarge 盒子的背景图片定位跟随成比例联动
    // 公式: 背景图移动距离 = mask 移动距离 * enlarge 盒子尺寸 / mask 盒子尺寸
    const bgx = x * this.enlarge_width / this.mask_width
    const bgy = y * this.enlarge_height / this.mask_height

    // 4-6. 给 enlarge 盒子的 backgroundPosition 赋值
    this.enlarge.style.backgroundPosition = `-${ bgx }px -${ bgy }px`
  })
}

<!-- 整个的放大镜区域 -->
<div class="box" id="box">
  <!-- 正常展示的图片 -->
  <div class="show">
    <img src="./imgs/1.jpg" alt="">

    <!-- 遮罩层盒子 -->
    <div class="mask"></div>
  </div>

  <!-- 切换图片的列表 -->
  <div class="list">
    <p class="active">
      <img
        src="./imgs/1.small.jpg"
        data-show="./imgs/1.jpg"
        data-enlarge="./imgs/1.big.jpg"
        alt="">
    </p>
    <p>
      <img
        src="./imgs/2.small.jpg"
        data-show="./imgs/2.jpg"
        data-enlarge="./imgs/2.big.jpg"
        alt="">
    </p>
  </div>

  <!-- 放大镜的盒子 -->
  <div class="enlarge"></div>
</div>


<script src="./index.js"></script>
<script>
  // 引入文件以后 new 一下就实现了放大镜
  const e = new Enlarge('#box')
  console.log(e)
</script>

* {
  margin: 0;
  padding: 0;
}

img {
  width: 100%;
  height: 100%;
  display: block;
}

.box {
  width: 450px;
  height: 600px;
  border: 1px solid #333;
  margin: 50px;
  display: flex;
  flex-direction: column;
  position: relative;
}

.box > .show {
  width: 450px;
  height: 450px;
  position: relative;
}

.box > .show > .mask {
  width: 200px;
  height: 200px;
  background-color: yellow;
  opacity: 0.5;
  position: absolute;
  left: 100px;
  top: 100px;

  /* 自己不作为事件目标 */
  pointer-events: none;

  display: none;
}

.box > .list {
  flex: 1;
  border-top: 1px solid #333;
  box-sizing: border-box;
  padding: 20px;
  display: flex;
  align-items: center;
}

.box > .list > p {
  margin-right: 30px;
  border: 1px solid #333;
  width: 54px;
  height: 54px;
  cursor: pointer;
}

.box > .list > p.active {
  border-color: orange;
}

.box > .enlarge {
  width: 400px;
  height: 400px;
  position: absolute;
  left: 110%;
  top: 0;

  background-image: url(./imgs/1.big.jpg);
  background-size: 800px 800px;

  display: none;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值