小程序图片双指缩放

需求

项目需要做一个互动视频,需要提供白板功能,白板可以播放ppt,但是做ppt的人没考虑到手机屏幕的小,ppt很多的内容就看不清,所以,就需要把投屏的ppt放大。这里就需要用到双指缩放的能力。

小程序提供的图片缩放能力

  • 小程序 movable-area movable-view组件,这俩个组件提供了可移动区域能力,不仅仅图片可以在里面被缩放,移动,所有组件都可以,但是这个组件提供的缩放能力不能大于手机屏幕大小,这种功能就不符合需求。
  • 小程序有个图片预览的api wx.previewImage()接口,可提供完美的图片缩放能力,但是这种功能类似相册预览,调用后,会掉起原生组件,组断视频流的传输,所以这个接口也不符合需求。

自己实现的符合业务的图片缩放能力

经过多次尝试,最终最佳的解决方案就是,根据手势,自己实现图片缩放能力,这样,与项目兼容好,符合项目要求。接下来就是参考各种轮子,实现自己的轮子。

代码示例

1. 我们首先先建个组件名子 zoomImgByView

2. zoomImgByView.json

{  
	"component": true,
	"usingComponents": {}
}

3. zoomImgByView.wxss

4. zoomImgByView.html

<view style="width:{{view_width}}px;height:{{view_height}}px;overflow: hidden; position: relative;">
  <image
    id="mapImage"
    style="width:{{imgWidth}}px;height:{{imgHeight}}px;margin-top:{{marginTop}}px;margin-left:{{marginLeft}}px;"
    src="{{img_src}}"
    mode="aspectFit"
    catchload="_imgLoadEvent"
    catchtouchstart="_touchStartEvent"
    catchtouchmove="_touchMoveEvent"
    catchtouchend="_touchEndEvent"
    catchtouchcancel="_touchEndEvent" />
</view>

5. zoomImgByView.js

var lastTouchPoint = { x: 0, y: 0 };
var newDist = 0;
var oldDist = 0;
var app = getApp();
Component({
  /**
   * 组件的属性列表
   */
  properties: {
    // 图片地址
    img_src: {
      type: String
    },
    // 可视区域的大小
    view_width: {
      type: String
    },
    view_height: {
      type: String
    },
  },

  /**
   * 组件的初始数据
   */
  data: {
    imgWidth:0,
    imgHeight:0,
    marginTop:0,
    marginLeft:0
  },

  /**
   * 组件的方法列表
   */
  methods: {
    /**
     * 图片加载完成方法
     */
    _imgLoadEvent: function (event) {
      this.setData({
        imgWidth: this.data.view_width,
        imgHeight: this.data.view_height,
        marginLeft: 0,
        marginTop: 0,
      })
    },
    /**
     * 触摸开始事件
     */
    _touchStartEvent: function () {
      lastTouchPoint = { x: 0, y: 0 }
      oldDist = 0
    },
    /**
     * 手指移动事件
     */
    _touchMoveEvent: function (e) {
      // 单指移动事件
      if (e.touches.length == 1) {
        if (lastTouchPoint.x == 0 && lastTouchPoint.y == 0) {
          lastTouchPoint.x = e.touches[0].clientX
          lastTouchPoint.y = e.touches[0].clientY
        } else {
          var xOffset = e.touches[0].clientX - lastTouchPoint.x
          var yOffset = e.touches[0].clientY - lastTouchPoint.y
          this.setData({
            marginTop: this.data.marginTop + yOffset,
            marginLeft: this.data.marginLeft + xOffset,
          })
          lastTouchPoint.x = e.touches[0].clientX
          lastTouchPoint.y = e.touches[0].clientY
        }
      }
      // 双指缩放事件
      if (e.touches.length == 2) {
        if (oldDist == 0) {
          oldDist = this._spacing(e);
        } else {
          newDist = this._spacing(e);
          if (newDist > oldDist + 1) {
            this._zoom(newDist / oldDist, e);
            oldDist = newDist;
          }
          if (newDist < oldDist - 1) {
            this._zoom(newDist / oldDist, e);
            oldDist = newDist;
          }
        }
      }
    },
    /**
     * 触摸事件结束
     */
    _touchEndEvent: function () {
      this._reboundAnimation();
    },
    /**
     * 计算x轴上的双指中心点比例
     */
    _calcXRatio: function (event) {
      var xRatio = ((event.touches[0].clientX + event.touches[1].clientX) / 2 - this.data.marginLeft) / this.data.imgWidth
      return xRatio
    },
    /**
     * 计算y轴上的双指中心点比例 注意是图片的中心点
     */
    _calcYRatio: function (event) {
      let max, min;
      if (app.globalData.systemInfo.screenHeight > app.globalData.systemInfo.screenWidth) {
        max = app.globalData.systemInfo.screenHeight;
        min = app.globalData.systemInfo.screenWidth;
      } else {
        min = app.globalData.systemInfo.screenHeight;
        max = app.globalData.systemInfo.screenWidth;
      }
      let top;
      if (app.globalData.systemInfo.deviceOrientation === 'portrait' ) {
        top = 0.75 * min + 35 + 42;
      } else {
        if (app.globalData.systemInfo.model.indexOf('iPad') > -1) {
          top = 0.03 * max;
        } else {
          top = (min - 0.421875 * max) / 2;
        }
      }
      var yRatio = ((event.touches[0].clientY + event.touches[1].clientY) / 2 - this.data.marginTop - top) / this.data.imgHeight
      return yRatio
    },
    /**
     * 双指缩放
     */
    _zoom: function (f, event) {
      var xRatio = this._calcXRatio(event)
      var yRatio = this._calcYRatio(event)
      if (this.data.imgWidth <= this.data.view_width && f < 1) {
        var ratio = this.data.view_width / this.data.imgWidth
        this.setData({
          imgWidth: this.data.imgWidth * ratio,
          imgHeight: this.data.imgHeight * ratio
        })
        return;
      }
      if (this.data.imgHeight <= this.data.view_height && f < 1) {
        var ratio = this.data.view_height / this.data.imgHeight
        this.setData({
          imgWidth: this.data.imgWidth * ratio,
          imgHeight: this.data.imgHeight * ratio
        })
        return;
      }
      this.setData({
        // 此处的ratio为双指中心点在图片的百分比
        marginLeft: this.data.marginLeft + xRatio * this.data.imgWidth * (1 - f),
        marginTop: this.data.marginTop + yRatio * this.data.imgHeight * (1 - f),
        imgWidth: this.data.imgWidth * f,
        imgHeight: this.data.imgHeight * f,
      })
    },
    /**
     * 计算两指间距
     */
    _spacing: function (event) {
      var x = event.touches[0].clientX - event.touches[1].clientX;
      var y = event.touches[0].clientY - event.touches[1].clientY;
      return Math.sqrt(x * x + y * y);
    },
    /**
     * 边界的回弹动画
     */
    _reboundAnimation: function () {
      if (this.data.marginTop > 0) {
        this.setData({
          marginTop: 0
        })
      }
      if (this.data.marginLeft > 0) {
        this.setData({
          marginLeft: 0
        })
      }
      if (this.data.marginLeft < 0 && (this.data.imgWidth - Math.abs(this.data.marginLeft)) < this.data.view_width) {
        this.setData({
          marginLeft: this.data.view_width - this.data.imgWidth
        })
      }
      if (this.data.marginTop < 0 && (this.data.imgHeight - Math.abs(this.data.marginTop)) < this.data.view_height) {
        this.setData({
          marginTop: this.data.view_height - this.data.imgHeight
        })
      }
    },
    // 强制图片宽高缩放为1
    initImgSize(size) {
      this.setData({
        imgWidth: size.x,
        imgHeight: size.y,
        marginLeft: 0,
        marginTop: 0,
      })
    }
  }
})

6. 使用

// 传入图片大小 和连接即可
<zoomImgByView id="zoomImg" class="whiteboard-img" img_src="{{showDocumentUrl}}" view_width="{{boardArea.x}}" view_height="{{boardArea.y}}"/>

要点

  1. 确定双指缩放中心点,xRatio yRatio,需要注意的是图片的中心点,不是屏幕的中心点,所以点的距离是到图片的边距,不是到屏幕的边距。
  2. 我们是通过margin 和 中心点来确定缩放后的图片位置移动。所以上边的中心点至关重要。
  3. 代码只是符合自己的项目需求,可参考代码实现自己的轮子。

演示

在这里插入图片描述

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值