图片可以在固定区域放大缩小,且可以拖拽移动,有复位(vue3)

<template>
  <div class="AspJpeg">
    <div class="image-container" @wheel.prevent="handleWheel">
      <img ref="pic" draggable="false" :src="OneUrl" class="image"
        :style="{ transform: `scale(${scale}) translate(${translateX}px, ${translateY}px)` }"></img>
    </div>
    <!-- <button @click="zoomIn">Zoom In</button>
    <button @click="zoomOut">Zoom Out</button>
    <button @click="resetImage">Reset</button>
    <div>Current Scale: {{ Math.round(scale * 100) }}%</div> -->
    <div class="imageButtonBox">
      <div class="imageButtonLeft">
        <template v-if="currentIndex == 0">
          <el-icon size="20" :color="'#808081'">
            <ArrowLeftBold />
          </el-icon>
        </template>
        <template v-else>
          <el-icon size="20" :color="'#ffffff'" @click="TheNextOneLeft" style="cursor: pointer">
            <ArrowLeftBold />
          </el-icon>
        </template>
        {{ currentIndex + 1 }} / {{ urls.length }}
        <template v-if="currentIndex == urls.length - 1">
          <el-icon size="20" :color="'#808081'" >
            <ArrowRightBold />
          </el-icon>
        </template>
        <template v-else>

          <el-icon size="20" :color="'#ffffff'" @click="TheNextOneRight" style="cursor: pointer">
            <ArrowRightBold />
          </el-icon>
        </template>
      </div>
      <div class="RestorationIcon" @click="resetImage"  style="cursor: pointer;">
        <el-icon size="20">
          <RefreshRight />
        </el-icon>
      </div>
      <div class="imageButtonRight">
        <el-icon size="20"  @click="zoomIn" style="cursor: pointer;">
          <ZoomIn />
        </el-icon>
        <span style="margin: 0 10px;">{{ Math.round(scale * 100) }}%</span>
        <el-icon size="20" @click="zoomOut"  style="cursor: pointer;">
          <ZoomOut />
        </el-icon>
      </div>


    </div>
  </div>
</template>

<script setup>
import { ref, onMounted } from 'vue'
const urls = [{
  id: 1,
  image: 'https://fuss10.elemecdn.com/a/3f/3302e58f9a181d2509f3dc0fa68b0jpeg.jpeg',
}, {
  id: 2,
  image: 'https://fuss10.elemecdn.com/1/34/19aa98b1fcb2781c4fba33d850549jpeg.jpeg',
}, {
  id: 3,
  image: 'https://fuss10.elemecdn.com/0/6f/e35ff375812e6b0020b6b4e8f9583jpeg.jpeg',
}, {
  id: 4,
  image: 'https://fuss10.elemecdn.com/9/bb/e27858e973f5d7d3904835f46abbdjpeg.jpeg',
}]
const OneUrl = ref()
const scale = ref(1)
const translateX = ref(0)
const translateY = ref(0)
const currentIndex = ref(0)
let isDragging = false
let startX = 0
let startY = 0
const pic = ref(null);
let isDrag = false;
let disX, disY;


onMounted(() => {
  OneUrl.value = urls[currentIndex.value].image
  pic.value.addEventListener('mousedown', function (e) {
    isDrag = true;
    this.style.cursor = 'move';
    e = e || window.event;
    const x = e.clientX;
    const y = e.clientY;
    disX = x - this.offsetLeft;
    disY = y - this.offsetTop;
  });

  document.addEventListener('mousemove', function (e) {
    if (!isDrag) {
      return;
    }
    e = e || window.event;
    const x = e.clientX;
    const y = e.clientY;
    pic.value.style.left = x - disX + 'px';
    pic.value.style.top = y - disY + 'px';
  });

  document.addEventListener('mouseup', function () {
    isDrag = false;
    pic.value.style.cursor = 'default';
  });
})

const handleWheel = (event) => {
  const deltaY = event.deltaY

  if (deltaY > 0 && scale.value > 0.5) {
    scale.value -= 0.1
  } else if (deltaY < 0 && scale.value < 3) {
    scale.value += 0.1
  }
}


const resetImage = () => {
  scale.value = 1
  translateX.value = 0
  translateY.value = 0
  pic.value.style.transform = `scale(${scale.value}) translate(${translateX.value}px, ${translateY.value}px)`
  pic.value.style.left = '0px';
  pic.value.style.top = '0px';
}


const zoomIn = () => {
  if (scale.value < 3) {
    scale.value += 0.1
  }
}

const zoomOut = () => {
  if (scale.value > 1) {
    scale.value -= 0.1
  }
}
/**
 * 点击
 * ***/
const TheNextOneLeft = () => {
  console.log(currentIndex.value, '左点')
  currentIndex.value -= 1
  OneUrl.value = urls[currentIndex.value].image
}
const TheNextOneRight = () => {
  console.log(currentIndex.value, '右点')
  currentIndex.value += 1
  OneUrl.value = urls[currentIndex.value].image
}
</script>

<style scoped lang="scss">
.AspJpeg {
  height: calc(100vh - 133px);
  background-color: #eaeef3;
  display: flex;
  justify-content: center;
  align-items: center;
  position: relative;


  .imageButtonBox {
    width: 231px;
    height: 34px;
    border-radius: 32px;
    opacity: 0.7;
    background: var(--blue-1, #0E1D31);
    position: absolute;
    bottom: 8%;
    left: 50%;
    transform: translateX(-50%);
    ;
    // z-index: 2;
    color: #FFF;
    font-family: "PingFang SC";
    font-size: 14px;
    font-style: normal;
    font-weight: 400;
    line-height: normal;
    display: flex;
    align-items: center;
    justify-content: space-around;

    .imageButtonLeft {
      display: flex;
      align-items: center;

    }

    .imageButtonRight {
      display: flex;
      align-items: center;
    }
  }
}

.image-container {
  width: 690px;
  /* 放大区域宽度 */
  height: calc(100vh - 133px);
  overflow: hidden;
  cursor: grab;
  /* 修改鼠标样式为抓取手势 */

}

.image {
  margin: 10px;
  transition: transform 0.3s ease;
  /* 添加过渡效果 */
}
</style>
<template>
  <div class="AspJpeg" :style="style">
    <div class="image-container" @wheel.prevent="handleWheel" :style="style">
      <img ref="pic" draggable="false" :src="OneUrl" class="image"
        :style="{ transform: `scale(${scale}) translate(${translateX}px, ${translateY}px)` }" />
    </div>
    <!-- <button @click="zoomIn">Zoom In</button>
    <button @click="zoomOut">Zoom Out</button>
    <button @click="resetImage">Reset</button>
    <div>Current Scale: {{ Math.round(scale * 100) }}%</div> -->
    <div class="imageButtonBox">
      <div class="imageButtonLeft">
        <template v-if="currentIndex == 0">
          <el-icon size="20" :color="'#808081'">
            <ArrowLeftBold />
          </el-icon>
        </template>
        <template v-else>
          <el-icon size="20" :color="'#ffffff'" @click="TheNextOneLeft" style="cursor: pointer">
            <ArrowLeftBold />
          </el-icon>
        </template>
        {{ currentIndex + 1 }} / {{ getImageData.length }}
        <template v-if="currentIndex == getImageData.length - 1">
          <el-icon size="20" :color="'#808081'">
            <ArrowRightBold />
          </el-icon>
        </template>
        <template v-else>

          <el-icon size="20" :color="'#ffffff'" @click="TheNextOneRight" style="cursor: pointer">
            <ArrowRightBold />
          </el-icon>
        </template>
      </div>
      <div class="RestorationIcon" @click="resetImage" style="cursor: pointer;">
        <el-icon size="20">
          <RefreshRight />
        </el-icon>
      </div>
      <div class="imageButtonRight">
        <el-icon size="20" @click="zoomIn" style="cursor: pointer;">
          <ZoomIn />
        </el-icon>
        <span style="margin: 0 10px;">{{ Math.round(scale * 100) }}%</span>
        <el-icon size="20" @click="zoomOut" style="cursor: pointer;">
          <ZoomOut />
        </el-icon>
      </div>


    </div>
  </div>
</template>

<script setup>
import { ref, onMounted, watch } from 'vue'
// const urls =  [
//   'https://fuss10.elemecdn.com/a/3f/3302e58f9a181d2509f3dc0fa68b0jpeg.jpeg',
//   'https://fuss10.elemecdn.com/1/34/19aa98b1fcb2781c4fba33d850549jpeg.jpeg',
//   'https://fuss10.elemecdn.com/0/6f/e35ff375812e6b0020b6b4e8f9583jpeg.jpeg',
//   'https://fuss10.elemecdn.com/9/bb/e27858e973f5d7d3904835f46abbdjpeg.jpeg',
//   'https://fuss10.elemecdn.com/d/e6/c4d93a3805b3ce3f323f7974e6f78jpeg.jpeg',
//   'https://fuss10.elemecdn.com/3/28/bbf893f792f03a54408b3b7a7ebf0jpeg.jpeg',
//   'https://fuss10.elemecdn.com/2/11/6535bcfb26e4c79b48ddde44f4b6fjpeg.jpeg',
// ]
// 传递数据
const props = defineProps({
  // 数据
  getImageData: {
    type: Array,
    default: () => {
      return []
    }
  },
  style: {
    type: String,
    default: () => {
      return ''
    }
  }
})

const OneUrl = ref()
const scale = ref(1)
const translateX = ref(0)
const translateY = ref(0)
const currentIndex = ref(0)
// let isDragging = false
// let startX = 0
// let startY = 0
const pic = ref(null);
// const isDrag =ref(false);
let isDrag = false;
let disX, disY;
// const disX =ref(0);
// const disY =ref(0)
const getImageData = ref(props.getImageData)
watch(() => props.getImageData, (newValue, oldValue) => {
  console.log(newValue)
  // 处理getImageData变化的逻辑
  getImageData.value = newValue
  handData()
})
onMounted(() => {
  handData()
})

const handData = () => {
  let timer
  if (timer) {
    clearTimeout(timer);
  }
  timer = setTimeout(() => {
    OneUrl.value = getImageData.value[currentIndex.value];
    // console.log(OneUrl.value, getImageData.value);
    pic.value.addEventListener('mousedown', function (e) {
      isDrag = true;
      this.style.cursor = 'move';
      e = e || window.event;
      const x = e.clientX;
      const y = e.clientY;
      disX = x - this.offsetLeft;
      disY = y - this.offsetTop;
    });

    document.addEventListener('mousemove', function (e) {
      if (!isDrag) {
        return;
      }
      e = e || window.event;
      const x = e.clientX;
      const y = e.clientY;
      pic.value.style.left = x - disX + 'px';
      pic.value.style.top = y - disY + 'px';
    });

    document.addEventListener('mouseup', function () {
      isDrag = false;
      pic.value.style.cursor = 'default';
    });
  }, 200); // 延迟一秒执行
}

const handleWheel = (event) => {
  const deltaY = event.deltaY

  if (deltaY > 0 && scale.value > 0.5) {
    scale.value -= 0.1
  } else if (deltaY < 0 && scale.value < 3) {
    scale.value += 0.1
  }
}


const resetImage = () => {
  scale.value = 1
  translateX.value = 0
  translateY.value = 0
  pic.value.style.transform = `scale(${scale.value}) translate(${translateX.value}px, ${translateY.value}px)`
  pic.value.style.left = '0px';
  pic.value.style.top = '0px';
}


const zoomIn = () => {
  if (scale.value < 3) {
    scale.value += 0.1
  }
}

const zoomOut = () => {
  if (scale.value > 1) {
    scale.value -= 0.1
  }
}
/**
 * 点击
 * ***/
const TheNextOneLeft = () => {
  console.log(currentIndex.value, '左点')
  if (getImageData.value.length > 0) {
    currentIndex.value -= 1
    OneUrl.value = getImageData.value[currentIndex.value]
  } else {
    currentIndex.value = 0
  }

}
const TheNextOneRight = () => {
  console.log(currentIndex.value, '右点')
  if (getImageData.value.length > 0) {
    currentIndex.value += 1
    OneUrl.value = getImageData.value[currentIndex.value]
  } else {
    currentIndex.value = 0
  }
}
</script>

<style scoped lang="scss">
.AspJpeg {
  padding: 10px;
  height: calc(100vh - 133px);
  background-color: #eaeef3;
  display: flex;
  justify-content: center;
  align-items: center;
  position: relative;


  .imageButtonBox {
    width: 231px;
    height: 34px;
    border-radius: 32px;
    opacity: 0.7;
    background: #0E1D31;
    position: absolute;
    bottom: 8%;
    left: 50%;
    transform: translateX(-50%);
    ;
    // z-index: 2;
    color: #FFF;
    font-family: "PingFang SC";
    font-size: 14px;
    font-style: normal;
    font-weight: 400;
    line-height: normal;
    display: flex;
    align-items: center;
    justify-content: space-around;

    .imageButtonLeft {
      display: flex;
      align-items: center;

    }

    .imageButtonRight {
      display: flex;
      align-items: center;
    }
  }
}

.image-container {
  max-width: 690px;
  /* 放大区域宽度 */
  height: calc(100vh - 133px);
  overflow: hidden;
  cursor: grab;
  /* 修改鼠标样式为抓取手势 */

}

.image {
  width: 100%;
  transition: transform 0.3s ease;
  /* 添加过渡效果 */
}
</style>

这段代码是一个Vue组件,用于显示图片并实现缩放、拖动等功能。下面是对代码的解释:

  1. <template> 部分包含组件的模板代码,包括一个图片容器 .image-container,内部包含一个图片元素 <img>,以及一些按钮和文字用于控制缩放和切换图片。

  2. <script setup> 部分使用了 Vue 3 的 Composition API 的 setup 函数来定义组件的逻辑。这部分代码包括:

    • 引入了 refonMountedwatch 等函数。
    • 定义了一些响应式数据,如图片地址 OneUrl、缩放比例 scale、水平和垂直偏移量 translateXtranslateY,以及当前图片索引 currentIndex 等。
    • 监听 props.getImageData 的变化,并在变化时更新图片数据并调用 handData 方法。
    • 在组件挂载时调用 handData 方法初始化数据。
  3. handData 方法用于处理图片拖动功能。它在图片元素上添加了 mousedownmousemovemouseup 事件监听器,实现了图片的拖动效果。

  4. 其他方法包括 handleWheel 处理滚轮缩放事件,resetImage 重置图片缩放和位置,zoomInzoomOut 控制缩放功能,以及 TheNextOneLeftTheNextOneRight 控制切换图片功能。

  5. <style scoped lang="scss"> 部分包含组件的样式,如容器样式 .AspJpeg、按钮样式 .imageButtonBox、图片容器样式 .image-container 和图片样式 .image 等。

Vue3中实现移动端的图片双指控制放大缩小和拖动,可以利用Vue官方提供的指令v-touch,结合自定义指令和事件处理进行实现。 首先,在需要实现双指控制的图片元素上,使用v-touch指令绑定一个Touch事件。 ```html <template> <div> <img src="imageSrc" v-touch="handleTouch"> </div> </template> ``` 然后,在Vue的setup函数中,定义handleTouch方法来处理触摸事件。可以通过event.touches获取触摸点信息,并根据触摸点的个数来判断是拖动还是放大缩小操作。 ```javascript <script> import { ref } from &#39;vue&#39;; export default { setup() { const imageSrc = ref(&#39;path/to/image&#39;); const handleTouch = (event) => { const touches = event.touches; if (touches.length === 2) { // 双指操作:放大缩小 // 获取两个触摸点的距离 const distance = Math.hypot(touches[0].clientX - touches[1].clientX, touches[0].clientY - touches[1].clientY); // 根据距离放大缩小图片 zoomImage(distance); } else if (touches.length === 1) { // 单指操作:拖动 // 记录当前触摸点的坐标 const touch = touches[0]; const startX = touch.clientX; const startY = touch.clientY; // 监听触摸点的移动 document.addEventListener(&#39;touchmove&#39;, handleMove); // 监听触摸点的离开 document.addEventListener(&#39;touchend&#39;, handleEnd); // 处理移动事件 function handleMove(event) { event.preventDefault(); // 计算偏移量 const offsetX = event.touches[0].clientX - startX; const offsetY = event.touches[0].clientY - startY; // 根据偏移量拖动图片 dragImage(offsetX, offsetY); } // 处理离开事件 function handleEnd() { // 移除事件监听 document.removeEventListener(&#39;touchmove&#39;, handleMove); document.removeEventListener(&#39;touchend&#39;, handleEnd); } } }; // 放大缩小图片 const zoomImage = (distance) => { // 实现放大缩小操作 }; // 拖动图片 const dragImage = (offsetX, offsetY) => { // 实现拖动操作 }; return { imageSrc, handleTouch, zoomImage, dragImage }; } }; </script> ``` 在zoomImage和dragImage方法中,根据传入的参数来实现图片放大缩小和拖动操作。具体实现方式可以根据项目需求选择。 需要注意的是,由于涉及到与DOM直接交互,所以在Vue3的Composition API中,需要使用document.addEventListener和document.removeEventListener来监听和移除事件。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值