【图片】卷帘对比

391 阅读3分钟

之前参与的项目做过基于Cesium的地图影像卷帘对比,正好最近负责的项目也有相关功能(普通图片版),就挑战一下不用Cesium原生api该如何实现卷帘对比。

​编辑

其主要实现思路:

  1. “本图”、“原图”图片宽高固定为容器的大小
  2. “原图”容器大小随分屏轴拖拽改变,裁剪超出容器大小的图片

裁剪图片需要设置CSS样式为:overflow-clip-margin: content-box; overflow: clip; 其中:

  1. overflow-clip-margin: content-box;

    • overflow-clip-margin 是一个用于调整剪裁区域边缘的 CSS 属性。它定义了元素的内容在被剪裁时的额外外边距。
    • content-box 是一个值,表示剪裁区域的边缘与内容框相同。这意味着剪裁时不会为外边距、边框或内边距增加额外的空间。
  2. overflow: clip;

    • overflow 是一个控制元素内容溢出行为的 CSS 属性。
    • clip 是 overflow 的一个值,它表示任何溢出的内容都将被简单地剪裁掉,而不会显示滚动条。这意味着超出元素边界的内容将不可见。

话不多说,放码过来。

template渲染

<!-- 卷帘 -->
      <div ref="rollerRef" class="roller-wrapper" @dragstart.prevent>
        <div class="roller-wrapper-current-image">
          <!-- 本图 -->
          <img :style="{ width: `${contentWidth}px` }" class="h-100%" :src="currentImage" draggable="false" />
        </div>
        <div class="roller-wrapper-origin-image" ref="originImgRef">
          <!-- 原图 -->
          <img :style="{ width: `${contentWidth}px` }" class="h-100%" :src="originalImage" draggable="false" />
        </div>
        <div class="roller-wrapper-slider" ref="draggable">
          <el-image class="roller-wrapper-slider-btn" :src="sliderIcon" />
          <div class="roller-wrapper-slider-text right-10px">原图</div>
          <div class="roller-wrapper-slider-text left-10px">本图</div>
        </div>
      </div>

相关CSS样式:(注意 lang="less")

// 卷帘部分样式
.roller-wrapper {
  width: 100%;
  height: 100%;
  min-height: 600px;
  overflow: hidden;
  position: relative;

  &-current-image,
  &-origin-image {
    width: 100%;
    height: 100%;
    background-repeat: no-repeat;
    background-size: cover;
    background-position: center;
    position: absolute;
    top: 0;
    left: 0;
    pointer-events: none;
    overflow: hidden;

    img {
      max-width: none;
      overflow-clip-margin: content-box;
      overflow: clip;
      object-fit: contain;
    }
  }
  &-origin-image {
    width: 50%;
  }

  &-slider {
    position: absolute;
    left: 50%;
    background-color: #f2f2f3;
    width: 2px;
    height: 100%;
    z-index: 9999;
    cursor: move;

    &-btn {
      width: 12px;
      height: 27px;
      position: absolute;
      left: -5px;
      top: 50%;
      transform: translateY(-50%);
      z-index: 9999;
    }
    &-text {
      position: absolute;
      top: 14px;
      z-index: 9999;
      width: 52px;
      height: 33px;
      padding: 0 12px;
      color: #373e4c;
      background: #f0f0f2;
      border-radius: 17px;
      font-weight: 500;
      color: #373e4c;
      line-height: 33px;
      text-align: center;
    }

    &:before,
    &:after {
      content: ' ';
      display: block;
      width: 2px;
      height: 9999px;
      position: absolute;
      left: 50%;
      margin-left: -3.5px;
      z-index: 30;
      transition: 0.1s;
      background: #fff;
    }

    &:before {
      top: 100%;
    }
    &:after {
      bottom: 100%;
    }
  }
}

接下来是实现鼠标拖拽

  1. 监听卷帘相关事件,注意在页面 onBeforeUnmount 生命周期时需要移除事件监听。
/** 卷帘相关监听事件 */
const addEventListeners = () => {
  draggable.value?.addEventListener('mousedown', startDrag)
  document.body.addEventListener('mouseup', endDrag)
  document.body.addEventListener('mouseleave', endDrag)

  document.body.addEventListener('mousemove', onDrag)
  window.addEventListener('resize', onResize)
  document.addEventListener('fullscreenchange', onResize)

  contentWidth.value = rollerRef.value?.offsetWidth || 900
}

onMounted(() => {
  addEventListeners()
})

 2. 拖拽分屏轴改变容器大小,利用 Math.max 限制分屏轴的拖拽范围。

// 卷帘容器
const rollerRef = ref<HTMLElement>()
// 卷帘原图容器
const originImgRef = ref<HTMLElement>()
/** 图片容器宽度 */
const contentWidth = ref(0)
/** 是否拖拽 */
const isDragging = ref(false)
const draggable = ref<HTMLElement | null>(null)

/** 拖拽开始 */
const startDrag = () => {
  isDragging.value = true
}
/** 拖拽事件 */
const onDrag = (e: MouseEvent) => {
  if (isDragging.value) {
    let x = e.pageX
    x -= rollerRef.value!.getBoundingClientRect().left

    let transform = Math.max(0, Math.min(x, rollerRef.value!.offsetWidth))
    originImgRef.value!.style!.width = transform + 'px'
    draggable.value!.style!.left = transform + 'px'
  }
}
/** 拖拽结束 */
const endDrag = () => {
  isDragging.value = false
}

​编辑

细节处理

最后就是需要注意细节的问题,比如窗口大小改变时,卷帘的交互。此处我的处理方式是重新赋值容器宽度并将分屏轴恢复居中状态。

/** 窗口大小变化 */
const onResize = () => {
  if (draggable.value) {
    contentWidth.value = rollerRef.value?.offsetWidth || 900
    originImgRef.value!.style!.width = '50%'
    draggable.value.style.left = '50%'
  }
}

以上就是图片卷帘对比的实现啦^-^ (AI生成的小猫好诡异啊哈哈)