💥特效与知识共存 之 🖱️鼠标操作

668 阅读5分钟

我正在参加 码上掘金体验活动,详情:show出你的创意代码块

💥特效与知识共存 之 🖱️鼠标操作

介绍

  先看下gif图效果

20220420_102214.gif

  如上图所示,能够看到我们通过操作鼠标,移动推拉线,展示左右对比图。

  突然想做这个效果,是因为看到许多的AI型公司的页面,一般都会存在有这种功能。而且本能反应会想到通过鼠标操作,却如何实现效果呢?

分析

  虽然说看着简单,但是涉及到的知识点确实 杂且多,这里我会细节讲述,所用到的技术及相关知识点。我们下面进行拆解和分析

1.页面搭建

  首先是两张大的 优化前 Img1 和 优化后 的 Img2 图片,将其居中且重合放置,这一块通过 flex 布局 及 position 定位可以轻松解决。

  接下来是我们的 推拉线,这个可以通过 position 定位也可以轻松的处理。

  此时我们布局完后,页面效果应该下图这样

image.png

2.效果实现

  可以看到 优化前的 Img1 图 会将 优化后的 Img2 图进行覆盖。

  那么如何让被覆盖的 Img2 图进行部分展示,且保证 Img1 在位置不变的情况下,不将其覆盖。

clip

  这个时候需要用到一个 css 属性,叫 clip,即 裁剪,此时我们给 优化前的 Img1图进行设置 clip 属性。

clip: rect(auto, auto, auto, 420px),分别对应的是 rect(<top>, <right>, <bottom>, <left>)

  • 而我们只用到了 left,即相对于盒子的左边框边界的偏移。

  • auto 则代表元素不裁剪

<div id="old" style="clip: rect(auto, auto, auto, 420px);">
  <img width="840" height="524" src='https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/cf0407e5da0d472aa253ee1aa36311b5~tplv-k3u1fbpfcp-watermark.image?' />
</div>

  此时可以实现我们想要的效果了🎉,如下图。

image.png

  但是在我看完 MDN 对它的讲解后,发现该特性已经从 Web 标准中删除。并提示尽量不要使用该特性。哭了😭😭😭

clip-path

  幸好,MDN 提示了,可以使用新的属性 clip-path 来替换这个 Clip,我们辛苦在 MDN 上再学习下这个新属性,再实现上面的结果。

clip-path: polygon(420px 0%,100% 0%,100% 100%,420px 100%); 可以看到这是四个部分,即为四边形,分别代表着左上,右上,右下,左下,每个有两个参数,即坐标系的 x 的长度 和 y 的高度。

注意点:如果有六个部分,则是就是六边形。

  好了,此时我们的效果也重新出现了🎉🎉🎉

    <div id="new">
      <img width="840" height="524"
        src="https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/1c7ba67cdd174a9daf0ede7731d7c4d2~tplv-k3u1fbpfcp-watermark.image?"
        alt="">
    </div>
    <div id="old" style="clip-path: polygon(420px 0%,100% 0%,100% 100%,420px 100%);">
      <img width="840" height="524"
        src='https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/cf0407e5da0d472aa253ee1aa36311b5~tplv-k3u1fbpfcp-watermark.image?' />
    </div>

image.png

推拉线

  可以看到我们的 推拉线 还未设置。这里有个知识点,既然要操作 推拉线,那么是选择 position 呢,还是 transform

  由于 position 会改变我们的盒模型,会引起进行重排(回流)操作。而重排所需的成本会比重绘高很多,所以我们这里用到的是 transform 属性✨✨✨

<div class="tech-drag" style="transform: translate(400px, 0);">
  <div class="tech-drag-line"></div>
  <img width="32" height="32"
    src='https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/ed41b9b0aaae461d960c922090fbb23c~tplv-k3u1fbpfcp-watermark.image?' />
</div>

image.png

元素与浏览器的距离

  因为后续要进行鼠标操作,所以这里先放上 盒子元素与浏览器的相关距离

image.png

  这张图是借鉴菜鸟教程

  这里还有一个重要的 API,就是在我们去算当前元素到浏览器距离时,可以通过 getBoundingClientRect,这是它的 MDN介绍

  在这个项目中,用到了当前元素的宽度 offsetWidth,以及当前元素距离浏览器左侧的值 getBoundingClientRect().left

鼠标操作

  这里把项目中用到的鼠标操作的四种 API 说一下

  • mousedown:鼠标落下事件
  • mouseup:鼠标松开事件
  • mouseleave:鼠标离开 所监听的元素 的事件
  • mousemove:鼠标移动事件

推拉线

  这里我们通过获取这个 推拉线 元素,针对 推拉线mousedown 即鼠标 落下 的监听操作。

  const techDrag = document.querySelector('.tech-drag')
  
   techDrag.addEventListener('mousedown', (e) => {
    active = true
    techDrag.classList.add('dis-mouse')
  })

  可以看到这里有个 active 值,这个值的主要作用在后续我们移动鼠标时,判断是否是鼠标一直按着的状态。

  而添加的类名,即为

    .dis-mouse {
      pointer-events: none;
    }

  代表,pointer-events: none;,它的作用就是让这个元素无法进行鼠标操作。为什么要使用这个属性呢?

  因为如果不加上这个属性,我们在拖动 推拉线 元素 时,如果是图片,就出现图片的虚影,如下图所示,相信大家都有见过。为了防止这种情况,我们将该元素进行所谓的隐藏,即让改元素暂时不接受鼠标事件。等鼠标 离开 或者 松开 时再解除这个属性即可。

image.png

外层 wrap

  我们对外层包裹的 wrap 元素,进行鼠标 mouseup, mouseleave松开离开 的监听操作。

  // 鼠标松开
  wrap.addEventListener('mouseup', (e) => {
    active = false
    techDrag.classList.remove('dis-mouse')
  })

  // 鼠标离开
  wrap.addEventListener('mouseleave', (e) => {
    active = false
    techDrag.classList.remove('dis-mouse')
  })

移动操作

  最后就是关键的移动操作逻辑,给我们的 body 元素监听 mousemove 鼠标移动事件。

 // 鼠标移动事件
  document.body.addEventListener('mousemove', (e) => {
    if (active) {
      useMouse(e)
    }
  })

  接受当前参数值e,来获取当前鼠标位置距离浏览器左侧距离 e.pageX,与我们的 推拉线 距离鼠标左侧距离进行计算。

    // 计算鼠标距离浏览器左侧的距离
    let mousePageX = e.pageX;

    // techDrag 距离浏览器左侧的距离
    const techDragLeft = techDrag.getBoundingClientRect().left;

  实时更新 推拉线 及 图片的切割,transformclip-path 的值。

  const getTechDragTransformX = () => {
    return parseInt(techDrag.style.transform.split('(')[1].split('px')[0])
  }

  const setTechDragTransform = (val) => {
    techDrag.style.transform = `translate(${val}px, 0px)`
  }

  const setOldImgClip = (val) => {
    oldImg.style.clipPath = ` polygon(${val}px 0%,100% 0%,100% 100%,${val}px 100%)`
  }

  setTechDragTransform(newValue)
  setOldImgClip(newValue + 20)

  最终进行临界值判断,只有在临界值内,才进行移动,否则将其设置为临界值。

    // 临界值判断
    if (newValue < 0) return setTechDragTransform(0)
    if (newValue > wrapWidth - techDragWidth) return setTechDragTransform(wrapWidth - techDragWidth)

总结

  通过上面的一系列流程,我们的联动效果即可完成。这里把源码放到 码上掘金,小伙伴们可以去试试玩玩看😄,可能不是那么完美,还需改进。

code.juejin.cn/pen/7086329…

  这里用到的知识点,还是很杂的,得多总结,后续可能不止一次会用到💖

小知识点

  为了在 码上掘金,图片加载速度快,这里将相关素材图片放置如下。小伙伴们记得点击玩玩,查看效果哦!

download.jpg

download.jpg

download.png