文章目录
需求
项目需要做一个互动视频,需要提供白板功能,白板可以播放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}}"/>
要点
- 确定双指缩放中心点,xRatio yRatio,需要注意的是图片的中心点,不是屏幕的中心点,所以点的距离是到图片的边距,不是到屏幕的边距。
- 我们是通过margin 和 中心点来确定缩放后的图片位置移动。所以上边的中心点至关重要。
- 代码只是符合自己的项目需求,可参考代码实现自己的轮子。