手势放大、缩小和移动图片

屠锐
2023-12-01

我写博客一般都是把在项目中遇到的一些问题记录下来,可以在以后遇到类似问题或者有新的思路时来回顾。
前两天就遇到一个需求:在手机端,把html页面的图片内容放大和缩小、移动位置。花了我一天时间才做好,本来是自己先画逻辑草图和整理思路,做着做着发现有个别细节不对,所以走了一些弯路。现在把代码直接贴出来,功能和效果比较简陋,后面会继续完善,如果有疑问或建议可以留言,3Q。

功能:双击图片放大,单点触摸图片移动,双点触摸图片缩小、放大

利用touch事件来绑定触摸开始(touchstart)、触摸移动(touchmove)、触摸结束(touchend)。
event.touches获取触摸点有几个,event.touches[0].target获取触摸的对象。

2019-7-10 更新:改为组件形式

代码

<!doctype html>
<html lang="zh-CN">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
    <meta http-equiv="Pragma" content="no-cache">
    <meta http-equiv="Cache-Control" content="no-cache">
    <meta name="renderer" content="webkit">
    <meta http-equiv="Expires" content="0">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <meta name="apple-mobile-web-app-status-bar-style" content="black">
    <meta name="apple-mobile-web-app-capable" content="yes">
    <meta name="format-detection" content="telephone=no">
    <title>图片缩放</title>
    <script src="https://cdn.bootcss.com/vue/2.5.15/vue.min.js"></script>
    <style>
        .img-div {
            position: relative;
        }
        .big_img {
            position: absolute;
            max-width: 200%;
            -webkit-transition: all 0s ease;
            -moz-transition: all 0s ease;
            -ms-transition: all 0s ease;
            -o-transition: all 0s ease;
            transition: all 0s ease;
        }
    </style>
</head>
<body class="bg-white">
<div id="app">
    <div v-cloak>
        <div class="big_img_div">
            <my-component v-bind:img_src="big_img_src"></my-component>
            <my-component v-bind:img_src="big_img_src"></my-component>
        </div>
    </div>
</div>
<script>
    Vue.component('my-component', {
        template: `<div class="img-div"><img :src="img_src" @touchstart="touchstart" @touchmove="touchmove" @touchend="touchend" class="big_img" alt=""></div>`,
        props: ['img_src'],
        data () {
            return {
                start: {
                    x: 0,
                    y: 0
                },
                move: {
                    x: 0,
                    y: 0
                },
                scaleStart: {
                    x0: 0,
                    y0: 0,
                    x1: 0,
                    y1: 0
                },
                scaleMove: {
                    x0: 0,
                    y0: 0,
                    x1: 0,
                    y1: 0
                },
                touchTimes: 0
            }
        },
        mounted: function () {
            document.body.style.overflow = 'hidden';
            document.body.style.webkitOverflowScrolling = 'none';
        },
        methods: {
            touchstart: function (e) {
                let event = window.event || e;
                let _this = this;
                this.touchTimes++;
                // 双击时放大50
                let timesFun = setTimeout(function () {
                    // 如果大于1,说明是双击图片
                    if (_this.touchTimes > 1) {
                        event.touches[0].target.width += 50;
                    }
                    // 清除定时
                    clearInterval(timesFun);
                    _this.touchTimes = 0;
                }, 300);
                // 初始多个触摸位置(进行放大缩小)
                if (event.touches.length > 1) {
                    this.scaleStart = {
                        x0: event.touches[0].clientX,
                        y0: event.touches[0].clientY,
                        x1: event.touches[1].clientX,
                        y1: event.touches[1].clientY
                    };
                } else if (event.touches.length === 1) {
                    // 初始单个触摸位置
                    this.start = {
                        x: event.touches[0].clientX,
                        y: event.touches[0].clientY
                    };
                }
            },
            touchmove: function (e) {
                this.touchTimes = 0;
                let event = window.event || e;
                // 如果是1个触摸点,只计算上下左右移动
                if (event.touches.length === 1) {
                    this.move.x += event.touches[0].clientX - this.start.x;
                    this.move.y += event.touches[0].clientY - this.start.y;
                    event.touches[0].target.style.transform = `translate3d(${this.move.x}px, ${this.move.y}px, 0px) translateZ(0px)`;
                    this.start = {
                        x: event.touches[0].clientX,
                        y: event.touches[0].clientY
                    };
                } else if (event.touches.length === 2) { // 如果是2个触摸点,计算放大还是缩小
                    // 触摸移动位置
                    this.scaleMove = {
                        x0: event.touches[0].pageX,
                        y0: event.touches[0].pageY,
                        x1: event.touches[1].pageX,
                        y1: event.touches[1].pageY
                    };
                    /*
                     * 计算触摸点差值
                     * */
                    // 初始2个触摸点
                    const diffXInit = Math.abs(this.scaleStart.x0 - this.scaleStart.x1);
                    const diffYInit = Math.abs(this.scaleStart.y0 - this.scaleStart.y1);
                    const diffX = Math.abs(this.scaleMove.x0 - this.scaleMove.x1);
                    const diffY = Math.abs(this.scaleMove.y0 - this.scaleMove.y1);
                    /*
                     * 判断是左右、还是上下滑动来放大缩小
                     * */
                    // 左右移动触摸
                    if (Math.abs(this.scaleStart.x0 - this.scaleMove.x0) > Math.abs(this.scaleStart.y0 - this.scaleMove.y0)) {
                        // 比较touch开始与移动时的差,计算出是缩小还是放大
                        if (diffX > diffXInit) { // 放大
                            event.touches[0].target.width += Math.abs(diffXInit - diffX);
                            console.log('左右放大');
                        } else { // 缩小
                            event.touches[0].target.width -= Math.abs(diffXInit - diffX);
                            console.log('左右缩小');
                        }
                    } else { // 上下移动触摸
                        // 比较触摸开始与移动时的差,计算出是缩小还是放大
                        if (diffY > diffYInit) { // 放大
                            event.touches[0].target.width += Math.abs(diffYInit - diffY);
                            console.log('上下放大');
                        } else { // 缩小
                            event.touches[0].target.width -= Math.abs(diffYInit - diffY);
                            console.log('上下缩小');
                        }
                    }
                    // 重置触摸位置
                    this.scaleStart = {
                        x0: event.touches[0].clientX,
                        y0: event.touches[0].clientY,
                        x1: event.touches[1].clientX,
                        y1: event.touches[1].clientY
                    };
                }
            },
            touchend: function (e) {
                let event = window.event || e;
                // 如果触摸点变为1个时(就像2个触摸点,松开了1个),重置初始触摸点
                if (event.touches.length > 0) {
                    this.start = {
                        x: event.touches[0].clientX,
                        y: event.touches[0].clientY
                    };
                }
            }
        },
    });

    var app = new Vue({
        el : '#app',
        data () {
            return {
                big_img_src: 'https://eslint.org/img/logo.svg',
            };
        }
    });
</script>
</body>
</html>

  • 注:虽然是用vue来写的,但是想分离成传统方法还是比较简单的,所有我就没有更改。
 类似资料: