ImageScale.vue 3.03 KB
<script setup lang="ts">
import { ref, reactive } from 'vue';

const imgUrl = ref('https://w.wallhaven.cc/full/kx/wallhaven-kx98xd.jpg');
const standard = ref(100);
const state = reactive({
  zoomVal: 1,
  left: 0,
  top: 0,
});

function onMousedown(e: any) {
  let oDiv = e.target; // 获取当前元素
  // 算出鼠标相对元素的位置
  let disX = e.clientX - oDiv.offsetLeft;
  let disY = e.clientY - oDiv.offsetTop;

  document.onmousemove = function (e: any) {
    // 用鼠标的位置减去鼠标相对元素的位置,得到元素的位置
    let left = e.clientX - disX;
    let top = e.clientY - disY;
    // oDiv.style.left = left + 'px';
    // oDiv.style.top = top + 'px';
    state.left = left;
    state.top = top;
  };

  //为document绑定一个鼠标抬起事件,鼠标松开时,将box固定在当前位置
  document.onmouseup = function () {
    //取消document的onmousemove事件
    document.onmousemove = null;

    //取消document的onmouseup事件
    document.onmouseup = null;
  };
}
function onWheel(e: any) {
  let direction = e.deltaY > 0 ? 'down' : 'up';
  let val = state.zoomVal;
  if (direction === 'up') {
    // 滑轮向上滚动
    val += 0.1;
    if (val >= 2) {
      val = 2;
    }
  } else {
    // 滑轮向下滚动
    val -= 0.1;
    if (val <= 0.2) {
      val = 0.2;
    }
  }

  let diffScale = val - state.zoomVal;
  let diffX = e.offsetX * diffScale;
  let diffY = e.offsetY * diffScale;

  state.zoomVal = val;
  standard.value = Number((val * 100).toFixed(2));
  state.left -= diffX;
  state.top -= diffY;
}
function sliderUpdate(value: any) {
  state.zoomVal = value / 100;
}
</script>
<template>
  <div class="box q-ma-md">
    <div class="tool-box">
      <q-slider
        v-model="standard"
        :inner-min="20"
        :min="0"
        :max="200"
        label
        :label-value="standard + '%'"
        @update:model-value="sliderUpdate"
      />
    </div>
    <div class="img-box">
      <div
        class="img-mask"
        @mousedown.prevent="onMousedown"
        @mousewheel.prevent="onWheel"
        :style="{
          backgroundImage: `url(${imgUrl})`,
          transform: `scale(${state.zoomVal})`,
          left: state.left + 'px',
          top: state.top + 'px',
        }"
      ></div>
    </div>
  </div>
</template>

<style lang="scss" scoped>
.box {
  width: 800px;
  height: 500px;
}
.tool-box {
  box-sizing: border-box;
  height: 40px;
  border-top: 1px solid rgba(180, 180, 180, 0.3);
  border-left: 1px solid rgba(180, 180, 180, 0.3);
  border-right: 1px solid rgba(180, 180, 180, 0.3);
  display: flex;
  align-items: center;
  padding: 0 16px;
}
.img-box {
  height: calc(500px - 40px);
  width: 100%;
  border: 1px solid rgba(180, 180, 180, 0.3);
  overflow: hidden;
  position: relative;
  .img-mask {
    position: absolute;
    box-sizing: border-box;
    background-color: #dadada;
    width: 100%;
    height: 100%;
    top: 0;
    left: 0;
    transform-origin: 0 0;
    background-size: contain;
    background-repeat: no-repeat;
    background-position: center;
  }
}
</style>