移动端图片缩放、平移效果

原理:

  • 触摸事件处理

    • 使用 @touchstart@touchmove 和 @touchend 事件来监听用户的触摸操作。
    • 在这些事件中更新图片的缩放和平移状态。
  • 触摸开始 (onTouchStart)

    • 当有两个触摸点时,初始化捏合操作的状态:
      • 计算两个触摸点之间的初始距离 initialTouchDistance
      • 记录当前的缩放级别 initialScale
    • 当有一个触摸点时,记录初始触摸位置 lastTouchPosition
  • 触摸移动 (onTouchMove)

    • 平移操作:
      • 计算手指移动的距离 dx 和 dy
      • 更新当前的平移量 currentTranslation
    • 缩放操作:
      • 计算两个触摸点之间的距离 touchDistance
      • 根据新的触摸点距离和初始触摸点距离计算缩放比例 currentScale
      • 限制缩放比例在 1 到 3 之间。
  • 更新图片的变换状态 (debouncedUpdateTransform)

    • 使用 transform 属性更新图片的位置和缩放状态:

效果如下:

移动端图片缩放、平移效果

代码如下: 

<template>
    <div>
        <button @click="showModal">点我放大按钮</button>
        <!-- 添加模态框 -->
        <div class="modal" :class="{ 'is-active': isModalActive }">
            <div class="modal-background" @click="hideModal"></div>
            <img ref="zoomableImage" :src="currentImageUrl" alt="" @touchstart="onTouchStart" @touchmove="onTouchMove"
                @touchend="onTouchEnd">
        </div>
    </div>

</template>

<script>
import url from './abc.png';
export default {
    name: 'DataIndicator',
    data() {
        return {
            // 模态框激活状态
            isModalActive: false,
            // 当前图像URL
            currentImageUrl: '',
            // 当前缩放级别
            initialScale: 1,
            // 初始触摸距离
            initialTouchDistance: 0,
            // 当前缩放比例
            currentScale: 1,
            // 是否开始捏合操作
            pinchStart: false,
            // 上次触摸位置
            lastTouchPosition: null,
            // 当前平移量
            currentTranslation: { x: 0, y: 0 },
        }
    },
    mounted() {
        this.debouncedUpdateTransform(); // 初始化图片的transform状态
    },
    methods: {
        showModal() {
            this.currentImageUrl = url; // 设置当前图片URL,具体路径自己填写
            this.currentScale = 3; // 初始化时设置图片放大3倍
            this.debouncedUpdateTransform(); // 应用变换
            this.isModalActive = true;
        },
        hideModal() {
            this.isModalActive = false;
        },
        /**
       * 处理触摸开始事件
       * 此方法主要用于初始化触摸操作的起始状态,包括双指缩放和平移的初始状态
       * 
       * @param {Event} e 触摸事件对象,包含触摸点的信息
       */
        onTouchStart(e) {
            // 当有两个触摸点时,初始化捏合操作的状态
            if (e.touches.length === 2) {
                this.pinchStart = true;
                // 计算两个触摸点之间的初始距离
                this.initialTouchDistance = Math.hypot(
                    e.touches[0].pageX - e.touches[1].pageX,
                    e.touches[0].pageY - e.touches[1].pageY
                );
                // 保存当前的缩放级别,以便在捏合操作中使用
                this.initialScale = this.currentScale;
            } else if (e.touches.length === 1) {
                // 当有一个触摸点时,记录下初始触摸位置,用于平移操作
                this.lastTouchPosition = {
                    x: e.touches[0].pageX,
                    y: e.touches[0].pageY,
                };
            }
        },
        /**
        * 处理触摸移动事件
        * @param {TouchEvent} e 触摸移动事件对象
        */
        onTouchMove(e) {
            // 如果没有处于捏合缩放状态且存在上一次触摸位置记录,则处理平移
            if (!this.pinchStart && this.lastTouchPosition) {
                // 计算手指移动的水平和垂直距离
                const dx = e.touches[0].pageX - this.lastTouchPosition.x;
                const dy = e.touches[0].pageY - this.lastTouchPosition.y;

                // 更新当前的平移量,dx和dy代表手指移动的距离
                // 注意这里除以6是为了减小移动的幅度,避免图片移动过快
                this.currentTranslation = {
                    x: this.currentTranslation.x + dx / 6,
                    y: this.currentTranslation.y + dy / 6,
                };

                // 更新图片的变换状态
                this.debouncedUpdateTransform();
            }
            // 如果处于捏合缩放状态,则处理缩放
            else if (this.pinchStart) {
                // 计算两个触摸点之间的距离
                const touchDistance = Math.hypot(
                    e.touches[0].pageX - e.touches[1].pageX,
                    e.touches[0].pageY - e.touches[1].pageY
                );
                // 根据新的触摸点距离和初始触摸点距离计算缩放比例
                this.currentScale = this.initialScale * (touchDistance / this.initialTouchDistance);

                // 限制缩放比例在1到3之间
                this.currentScale = Math.max(1, Math.min(3, this.currentScale));

                // 更新图片的变换状态
                this.debouncedUpdateTransform();
            }
        },
        // 图片放大
        debouncedUpdateTransform() {
            const image = this.$refs.zoomableImage;
            image.style.transform = `translate(${this.currentTranslation.x}px, ${this.currentTranslation.y}px) scale(${this.currentScale})`;
        },
        onTouchEnd() {
            this.pinchStart = false;
        },
    }
}

</script>
<style>
img {
    display: block;
    /* 确保 img 是块级元素,这样设置宽度才有效 */
    width: 100%;
    /* 让 img 宽度填满其容器 */
    height: 200px;
    /* 自动调整高度以保持图片比例 */
    /* border: 1px solid cadetblue; */
}

.modal {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    visibility: hidden;
    opacity: 0;
    transition: visibility 0s linear .25s, opacity .25s 0s;
    z-index: 10000;
}

.modal.is-active {
    visibility: visible;
    opacity: 1;
}

.modal-background {
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    background-color: rgba(0, 0, 0, 0.8);
}

.modal-content {
    position: relative;
    width: 100%;
    height: 60%;
    overflow: auto;
    background-color: white;
    box-shadow: 0 0 20px rgba(0, 0, 0, 0.2);

    img {
        height: 100%;
        /* 图片高度与模态框高度相同 */
        object-fit: contain;
        /* 保持图片比例,图片不会被拉伸 */

    }
}

.modal-close {
    position: absolute;
    top: 10px;
    right: 10px;
    font-size: 24px;
    color: #aaa;
    cursor: pointer;
}
</style>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不要随地大便

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值