VUE3+TS实现图片缩放移动弹窗

完整代码

使用VUE3、TS,实现将图片通过鼠标拖拽缩放以及选择缩放比例。

bash 复制代码
<template>
  <div>
    <el-dialog
        v-model="dialogVisible"
        title="查看图片"
        :close-on-click-modal="false"
        :close-on-press-escape="false"
        fullscreen
        style="overflow: hidden;"
    >
        <div style="margin-bottom: 10px;">
            <el-select
                v-model="magnification"
                placeholder="请选择图片放大尺寸"
                size="large"
                style="width: 240px"
                @change="changePicSize"
            >
                <el-option
                    v-for="item in options"
                    :key="item.value"
                    :label="item.label"
                    :value="item.value"
                />
                </el-select>
        </div>
        <div 
            class="img_area" 
            ref="container" 
            @wheel.prevent="handleWheel"
            @mousemove="handleDrag"
            @mouseup="endDrag"
            @mouseleave="endDrag"
        >
            <img 
                :style="imageStyle" 
                class="auto-scale-image" 
                :src="props.imgSrc" 
                ref="image"
                @mousedown="startDrag"
                draggable="false"
            />
        </div>
        <template #footer></template>
    </el-dialog>
  </div>
</template>
<script lang='ts' setup>
    // 显隐设置
    const props = defineProps<{
        modelValue: boolean,
        imgSrc:any
    }>()
    const emit = defineEmits<{
        (e: 'update:modelValue', value: boolean): void
    }>()
    const dialogVisible = computed({
        get: () => props.modelValue,
        set: (value) => emit('update:modelValue', value)
    })
    let magnification = $ref(null) as any
    let options = [{
        value: 0.1,
        label: '0.1',
    },{
        value: 1,
        label: '1',
    },
    {
        value: 2,
        label: '2',
    },]
    
    const translate = ref({ x: 0, y: 0 });
    const scale = ref(1);
    const isDragging = ref(false);
    const container = ref<HTMLDivElement | null>(null);
    const step = ref(0.1)
    const minScale = ref(0.1) 
    const maxScale = ref(3) 
    const imageStyle = computed(() => ({
        transform: `scale(${scale.value}) translate(${translate.value.x}px, ${translate.value.y}px)`,
        
        transformOrigin: 'center center',
        cursor: isDragging.value ? 'grabbing' : 'grab'
    }));
    // 处理鼠标滚轮缩放
    const handleWheel = (e: WheelEvent) => {
        if (!container.value) return;
        const delta = e.deltaY > 0 ? -step.value : step.value;
        const newScale = Math.max(minScale.value, Math.min(maxScale.value, scale.value + delta));
        // 计算缩放中心点
        const rect = container.value.getBoundingClientRect();
        const mouseX = e.clientX - rect.left;
        const mouseY = e.clientY - rect.top;
        
        // 计算缩放后的偏移量,使鼠标位置保持相对不变
        const scaleRatio = newScale / scale.value;
        translate.value = {
            x: translate.value.x * scaleRatio + (1 - scaleRatio) * (mouseX - rect.width / 2 - translate.value.x),
            y: translate.value.y * scaleRatio + (1 - scaleRatio) * (mouseY - rect.height / 2 - translate.value.y)
        };
        scale.value = newScale;
    };
   
    // 响应式状态
    const image = ref<HTMLImageElement | null>(null);
    const startPos = ref({ x: 0, y: 0 });
    // 开始拖拽
    const startDrag = (e: MouseEvent) => {
        
        if (e.button !== 0) return; // 只响应左键
        
        isDragging.value = true;
        startPos.value = {
            x: e.clientX - translate.value.x,
            y: e.clientY - translate.value.y
        };
        e.preventDefault();
    };
    // 处理拖拽
    const handleDrag = (e: MouseEvent) => {
        if (!isDragging.value) return;
        
        let newX = e.clientX - startPos.value.x;
        let newY = e.clientY - startPos.value.y;
        
        translate.value = { x: newX, y: newY };
    };

    // 结束拖拽
    const endDrag = () => {
        isDragging.value = false;
    };
   
    // 通过选择器改变图片大小
    const changePicSize = () => {
        scale.value = magnification;
        centerImage();
    }
    // 居中图片
    const centerImage = () => {
        if (!container.value) return;
        const imageContainer = container.value;
        imageContainer.scrollLeft = (imageContainer.scrollWidth - imageContainer.clientWidth) / 2;
        imageContainer.scrollTop = (imageContainer.scrollHeight - imageContainer.clientHeight) / 2;
    };
</script>
<style scope lang="less">
    .img_area{
        width: 100%;
        height: calc(100vh - 130px);
        text-align: center;
        .auto-scale-image{
            max-width: 100%;
            max-height: 100%;
            object-fit: contain; /* 保持宽高比 */
        }
    }
</style>
相关推荐
烛阴34 分钟前
从0到1掌握盒子模型:精准控制网页布局的秘诀
前端·javascript·css
前端工作日常4 小时前
我理解的`npm pack` 和 `npm install <local-path>`
前端
李剑一4 小时前
说个多年老前端都不知道的标签正确玩法——q标签
前端
嘉小华4 小时前
大白话讲解 Android屏幕适配相关概念(dp、px 和 dpi)
前端
姑苏洛言4 小时前
在开发跑腿小程序集成地图时,遇到的坑,MapContext.includePoints(Object object)接口无效在组件中使用无效?
前端
七八书4 小时前
Vue3 组件通信全解析:从基础到进阶的实用指南
vue.js
奇舞精选4 小时前
Prompt 工程实用技巧:掌握高效 AI 交互核心
前端·openai
用户3802258598244 小时前
vue3源码解析:模块总览
vue.js
Danny_FD4 小时前
React中可有可无的优化-对象类型的使用
前端·javascript
用户757582318554 小时前
混合应用开发:企业降本增效之道——面向2025年移动应用开发趋势的实践路径
前端