Skip to content

图片滑动对比

code
text
查看代码
vue
<script setup>
import { ref, onMounted, onUnmounted } from 'vue'

const props = defineProps({
  img1: {
    type: String,
    required: true,
  },
  img2: {
    type: String,
    required: true,
  },
  height: {
    type: [Number, String],
    required: true,
  },
})

const container = ref(null)
const width = ref()
const transform = ref()

onMounted(() => {
  if (container.value) {
    const { width: w } = container.value.getBoundingClientRect()
    width.value = w
    transform.value = w / 2
  }
})

const onMousedown = () => {
  document.addEventListener('mousemove', onMousemove)
  document.addEventListener('mouseup', onMouseup)
}

const onMousemove = (evt) => {
  if (!container.value) return
  const { clientX } = evt
  const { left, width: w } = container.value.getBoundingClientRect()
  transform.value = Math.max(0, Math.min(clientX - left, w))
}

const onMouseup = () => {
  document.removeEventListener('mousemove', onMousemove)
  document.removeEventListener('mouseup', onMouseup)
}

const onTouchstart = () => {
  document.addEventListener('touchmove', onTouchmove)
  document.addEventListener('touchend', onTouchend)
}

const onTouchmove = (evt) => {
  if (!container.value) return
  const { clientX } = evt.touches[0]
  const { left, width: w } = container.value.getBoundingClientRect()
  transform.value = Math.max(0, Math.min(clientX - left, w))
}

const onTouchend = () => {
  document.removeEventListener('touchmove', onTouchmove)
  document.removeEventListener('touchend', onTouchend)
}

onUnmounted(() => {
  document.removeEventListener('mousemove', onMousemove)
  document.removeEventListener('mouseup', onMouseup)
  document.removeEventListener('touchmove', onTouchmove)
  document.removeEventListener('touchend', onTouchend)
})
</script>

<template>
  <div
    ref="container"
    class="container"
    :style="{ width: width + 'px', height: typeof height === 'number' ? height + 'px' : height }"
  >
    <div class="img1Wrap">
      <img :width="width" :src="props.img1" alt="code" />
    </div>
    <div class="img2Wrap" :style="{ width: transform + 'px' }">
      <img :width="width" :src="props.img2" alt="text" />
    </div>
    <div
      class="touchBar"
      :style="{ transform: `translate3d(${transform - 15}px, 0, 0)` }"
      @mousedown="onMousedown"
      @touchstart="onTouchstart"
    >
      <div class="centerHandle">
        <div class="left"></div>
        <div class="right"></div>
      </div>
    </div>
  </div>
</template>

<style scoped lang="scss">
.container {
  position: relative;
  user-select: none;
  overflow: hidden;
  width: 100%;
  height: 100%;

  .img1Wrap {
    overflow: hidden;
    width: 100%;
    height: 100%;
    position: absolute;
    top: 0;
    left: 0;
    pointer-events: none;

    img {
      height: 100%;
      max-width: none;
      object-fit: cover;
    }
  }
  .img2Wrap {
    overflow: hidden;
    height: 100%;
    top: 0;
    left: 0;
    position: absolute;
    pointer-events: none;

    img {
      height: 100%;
      max-width: none;
      object-fit: cover;
    }
  }
  .touchBar {
    height: 100%;
    top: 0;
    position: absolute;
    display: flex;
    align-items: center;
    justify-content: center;
    touch-action: none;

    &::before {
      content: '';
      width: 3px;
      position: absolute;
      top: 0;
      height: 50%;
      border-radius: 0.25rem;
      background: rgba(247, 248, 250, 1);
    }
    &::after {
      content: '';
      width: 3px;
      position: absolute;
      bottom: 0;
      height: 50%;
      border-radius: 0.25rem;
      background: rgba(247, 248, 250, 1);
    }

    .centerHandle {
      background: rgba(247, 248, 250, 1);
      border-radius: 0.125rem;
      cursor: pointer;
      display: flex;
      width: 30px;
      z-index: 9;
      padding: 0.125rem;
      box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);

      .left {
        background-color: currentColor;
        height: 1.2em;
        width: 1.2em;
        -webkit-mask-image: url("data:image/svg+xml;utf8,%3Csvg viewBox='0 0 512 512' width='1.2em' height='1.2em' xmlns='http://www.w3.org/2000/svg' %3E%3Cpath d='M327.3 98.9l-2.1 1.8-156.5 136c-5.3 4.6-8.6 11.5-8.6 19.2 0 7.7 3.4 14.6 8.6 19.2L324.9 411l2.6 2.3c2.5 1.7 5.5 2.7 8.7 2.7 8.7 0 15.8-7.4 15.8-16.6V112.6c0-9.2-7.1-16.6-15.8-16.6-3.3 0-6.4 1.1-8.9 2.9z' fill='currentColor'/%3E%3C/svg%3E");
        mask-image: url("data:image/svg+xml;utf8,%3Csvg viewBox='0 0 512 512' width='1.2em' height='1.2em' xmlns='http://www.w3.org/2000/svg' %3E%3Cpath d='M327.3 98.9l-2.1 1.8-156.5 136c-5.3 4.6-8.6 11.5-8.6 19.2 0 7.7 3.4 14.6 8.6 19.2L324.9 411l2.6 2.3c2.5 1.7 5.5 2.7 8.7 2.7 8.7 0 15.8-7.4 15.8-16.6V112.6c0-9.2-7.1-16.6-15.8-16.6-3.3 0-6.4 1.1-8.9 2.9z' fill='currentColor'/%3E%3C/svg%3E");
        -webkit-mask-size: 100% 100%;
        mask-size: 100% 100%;
        color: #333;
      }
      .right {
        background-color: currentColor;
        height: 1.2em;
        width: 1.2em;
        -webkit-mask-image: url("data:image/svg+xml;utf8,%3Csvg viewBox='0 0 512 512' width='1.2em' height='1.2em' xmlns='http://www.w3.org/2000/svg' %3E%3Cpath d='M184.7 413.1l2.1-1.8 156.5-136c5.3-4.6 8.6-11.5 8.6-19.2 0-7.7-3.4-14.6-8.6-19.2L187.1 101l-2.6-2.3C182 97 179 96 175.8 96c-8.7 0-15.8 7.4-15.8 16.6v286.8c0 9.2 7.1 16.6 15.8 16.6 3.3 0 6.4-1.1 8.9-2.9z' fill='currentColor'/%3E%3C/svg%3E");
        mask-image: url("data:image/svg+xml;utf8,%3Csvg viewBox='0 0 512 512' width='1.2em' height='1.2em' xmlns='http://www.w3.org/2000/svg' %3E%3Cpath d='M184.7 413.1l2.1-1.8 156.5-136c5.3-4.6 8.6-11.5 8.6-19.2 0-7.7-3.4-14.6-8.6-19.2L187.1 101l-2.6-2.3C182 97 179 96 175.8 96c-8.7 0-15.8 7.4-15.8 16.6v286.8c0 9.2 7.1 16.6 15.8 16.6 3.3 0 6.4-1.1 8.9-2.9z' fill='currentColor'/%3E%3C/svg%3E");
        -webkit-mask-size: 100% 100%;
        mask-size: 100% 100%;
        color: #333;
      }
    }
  }
}
</style>

如有转载或 CV 的请标注本站原文地址