小程序图片双指缩放

需求

项目需要做一个互动视频,需要提供白板功能,白板可以播放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. 代码只是符合自己的项目需求,可参考代码实现自己的轮子。

演示

在这里插入图片描述

要在小程序的 canvas 中实现双指缩放图片的功能,你可以按照以下步骤进行操作: 1. 创建一个 canvas 组件,并设置好宽度和高度。 ``` <canvas id="myCanvas" style="width: 300px; height: 300px;"></canvas> ``` 2. 在小程序的 js 文件中获取到 canvas 组件的上下文对象。 ``` const ctx = wx.createCanvasContext('myCanvas'); ``` 3. 加载并绘制图片到 canvas 上。 ``` const imgPath = '图片路径'; // 替换为你的图片路径 wx.getImageInfo({ src: imgPath, success(res) { const imgWidth = res.width; // 图片原始宽度 const imgHeight = res.height; // 图片原始高度 // 计算图片缩放后的初始宽度和高度 const initialWidth = 300; const initialHeight = (imgHeight / imgWidth) * initialWidth; ctx.drawImage(imgPath, 0, 0, initialWidth, initialHeight); ctx.draw(); } }); ``` 4. 监听 canvas 组件的触摸事件,实现双指缩放功能。 ``` let touchStartDistance = 0; // 记录触摸开始时两指之间的距离 let scale = 1; // 记录当前缩放比例 wx.canvasTouchStart((e) => { if (e.touches.length >= 2) { const x1 = e.touches[0].x; const y1 = e.touches[0].y; const x2 = e.touches[1].x; const y2 = e.touches[1].y; // 计算两指之间的距离 touchStartDistance = Math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2); } }); wx.canvasTouchMove((e) => { if (e.touches.length >= 2) { const x1 = e.touches[0].x; const y1 = e.touches[0].y; const x2 = e.touches[1].x; const y2 = e.touches[1].y; // 计算两指之间的距离 const touchMoveDistance = Math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2); // 计算缩放比例 const newScale = touchMoveDistance / touchStartDistance; // 更新缩放比例 scale = newScale; // 清空 canvas ctx.clearRect(0, 0, 300, 300); // 根据缩放比例绘制图片 const newWidth = initialWidth * scale; const newHeight = initialHeight * scale; ctx.drawImage(imgPath, 0, 0, newWidth, newHeight); ctx.draw(); } }); ``` 请根据你的实际需求,将上述代码片段进行适当修改和组合。希望对你有所帮助!
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值