微信小程序中图片自适应及双指缩放

本文介绍了如何在微信小程序中实现图片的自适应和双指缩放功能。首先,通过上传图片并获取其宽高信息来实现图片的自适应展示。接着,利用小程序的触摸事件记录双指的初始距离和移动距离,根据两者关系判断缩放方向。通过设定缩放阈值,限制图片的最大缩放比例为2倍。整个过程结合了微信小程序的API和计算方法,为后续的图片裁剪功能奠定了基础。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

预览

微信小程序实现对图片的缩放与裁剪

思路

  • wx.chooseImage上传图片
  • wx.getImageInfo获取图片宽高等信息
  • bindtouchstart,bindtouchmove记录双指事件
  • 通过双指移动的距离与初始距离的关系判断缩放
  • 规定阈值,最大与最小缩放

开始操作

上传图片

由于上传的图片需要放大与缩小,所以我们首先要在style动态绑定widthheight,其次要设置overflowscroll
然后开始使用wx.chooseImage上传图片,代码如下:

 choose: function() {
   
   
    let that = this,
      width = that.data.width,
      height = that.data.height;
    wx.chooseImage({
   
   
      count: 1,
      sizeType: ['original', 'compressed'],
      sourceType: ['album', 'camera'],
      success(res) {
   
   
        const tempFilePaths = res.tempFilePaths[0];
        }
        )}

获取图片信息

由于小程序无法操作DOM,我们获取图片信息不能像往常一样通过document.get。在小程序中需要使用wx.getImageInfo获取图片的相关信息。获取到上传的图片的宽高后,可以根据宽高的比列,实现特定条件下的自适应。

wx.getImageInfo({
   
   
          
<think>我们面对的问题是:在微信小程序的子组件中实现长按图片拖动,同时解决两个问题:1.触摸点偏移(即触摸开始位置与图片当前位置不一致导致拖动时跳动)2.拖动超出屏幕边界结合引用[1]和引用[2]以及之前的知识,我们采取以下思路:###问题分析1.**触摸点偏移**:在触摸开始时,记录触摸点相对于图片左上角的偏移量(即触摸点在图片内部的位置),然后在移动时用当前触摸点坐标减去这个偏移量,得到图片左上角的目标位置。2.**屏幕边界限制**:在移动过程中,计算图片左上角的位置,确保图片不会超出屏幕边界。需要同时考虑图片的宽度和高度。###解决方案####步骤1:获取图片和屏幕尺寸-在组件加载时获取屏幕尺寸(可通过`wx.getSystemInfoSync()`)-获取图片的尺寸(在图片加载完成时通过`wx.createSelectorQuery()`获取)####步骤2:处理触摸事件-**touchstart**:记录触摸起始点,并计算触摸点相对于图片左上角的偏移量(offsetX,offsetY)-**touchmove**:计算图片的目标位置(当前触摸点坐标减去偏移量),然后进行边界限制,再更新图片位置-**touchend**:可选,可以做一些收尾工作,比如回弹效果####步骤3:边界限制计算假设屏幕宽度为`screenWidth`,高度为`screenHeight`,图片宽度为`imgWidth`,高度为`imgHeight`。-左边界:0(图片左上角x不能小于0)-右边界:`screenWidth-imgWidth`(图片左上角x不能大于这个值)-上边界:0(图片左上角y不能小于0)-下边界:`screenHeight-imgHeight`(图片左上角y不能大于这个值)####步骤4:在子组件中实现由于是子组件,我们需要将触摸事件和图片数据放在子组件中,并通过属性传递图片路径等。###代码实现####子组件wxml```html<!--components/draggable-image/draggable-image.wxml--><viewclass="container"><imagesrc="{{src}}"mode="aspectFit"class="image"bindload="onImageLoad"bindtouchstart="onTouchStart"bindtouchmove="onTouchMove"bindtouchend="onTouchEnd"style="transform:translate({{imageLeft}}px,{{imageTop}}px);"/></view>```####子组件wxss```css/*components/draggable-image/draggable-image.wxss*/.container{position:relative;width:100%;height:100%;overflow:hidden;}.image{position:absolute;top:0;left:0;will-change:transform;/*提升性能*/}```####子组件js```javascript//components/draggable-image/draggable-image.jsComponent({properties:{src:String//图片路径},data:{imageLeft:0,//图片左上角x坐标(相对于容器)imageTop:0,//图片左上角y坐标imgWidth:0,//图片实际宽度imgHeight:0,//图片实际高度screenWidth:0,screenHeight:0,startX:0,//触摸起始点x(相对于屏幕)startY:0,//触摸起始点yoffsetX:0,//触摸点相对于图片左上角的x偏移offsetY:0//触摸点相对于图片左上角的y偏移},lifetimes:{attached(){//获取屏幕尺寸constsystemInfo=wx.getSystemInfoSync()this.setData({screenWidth:systemInfo.windowWidth,screenHeight:systemInfo.windowHeight})}},methods:{onImageLoad(e){//图片加载完成,获取图片尺寸const{width,height}=e.detailthis.setData({imgWidth:width,imgHeight:height})//或者使用createSelectorQuery,但bindload事件中可以直接拿到尺寸},onTouchStart(e){const{pageX,pageY}=e.touches[0]//计算触摸点相对于图片左上角的偏移constoffsetX=pageX-this.data.imageLeftconstoffsetY=pageY-this.data.imageTopthis.setData({startX:pageX,startY:pageY,offsetX:offsetX,offsetY:offsetY})},onTouchMove(e){const{pageX,pageY}=e.touches[0]const{offsetX,offsetY,imgWidth,imgHeight,screenWidth,screenHeight}=this.data//计算图片的目标位置(左上角坐标)letnewLeft=pageX-offsetXletnewTop=pageY-offsetY//边界限制//左边界if(newLeft<0){newLeft=0}//右边界if(newLeft>screenWidth-imgWidth){newLeft=screenWidth-imgWidth}//上边界if(newTop<0){newTop=0}//下边界if(newTop>screenHeight-imgHeight){newTop=screenHeight-imgHeight}this.setData({imageLeft:newLeft,imageTop:newTop})},onTouchEnd(e){//如果需要回弹效果,可以在这里实现//例如:如果图片部分超出边界,则回弹到边界内}}})```####父组件引用示例```html<!--父组件页面--><viewstyle="width:100%;height:100vh;"><draggable-imagesrc="/images/test.jpg"></draggable-image></view>```###边界条件说明-当图片尺寸大于屏幕尺寸时,边界限制会确保图片在屏幕内可拖动查看所有部分(因为边界限制的是图片左上角的位置,而图片的右下角位置为`(left+imgWidth,top+imgHeight)`,所以当图片比屏幕大时,我们只能拖动图片使得屏幕内显示图片的不同部分)。-当图片尺寸小于屏幕尺寸时,边界限制会确保图片不会超出屏幕。###触摸点偏移问题解决通过记录触摸点相对于图片左上角的偏移(`offsetX`和`offsetY`),在移动时使用当前触摸点减去这个偏移量,得到图片左上角的位置,这样图片就不会跳动了。###注意事项1.图片模式:这里使用了`mode="aspectFit"`,所以图片加载后的尺寸可能不是原始尺寸,而是等比例缩放的。我们在`bindload`事件中获取的尺寸是缩放后的尺寸,这正是我们需要的。2.性能:频繁的setData可能会引起性能问题,但在这个场景中,由于我们只更新两个数值(imageLeft和imageTop),且使用了transform(不引起重排),性能可以接受。3.如果图片尺寸在加载后发生变化(比如旋转、缩放等),需要重新获取尺寸并更新边界限制。###高级优化:回弹效果引用[1]中提到支持回弹效果(滑动时可滑出边界,释放时回弹到边界)。我们可以修改`onTouchMove`中的边界限制,允许拖动时超出边界,然后在`onTouchEnd`中执行回弹动画。但根据用户需求,我们这里只要求不超出边界,所以没有实现回弹。###相关问题1.如何在小程序子组件中实现图片双指缩放?2.微信小程序中如何实现图片旋转?3.如何在小程序中实现图片裁剪功能?4.微信小程序子组件与父组件之间的通信如何实现?5.如何处理微信小程序中的图片性能优化?</think># 微信小程序子组件长按图片拖动解决方案 针对子组件中长按图片拖动出现的触摸点偏移和超出屏幕边界问题,我提供以下完整的解决方案: ## 核心问题分析 1. **触摸点偏移**:由于子组件的相对定位,触摸点坐标计算需要转换为全局坐标 2. **边界限制**:需要动态计算图片可移动范围 3. **组件通信**:子组件需要获取屏幕尺寸信息 ## 完整解决方案 ### 子组件实现 ```html <!-- components/draggable-image/draggable-image.wxml --> <view class="container"> <image src="{{src}}" class="image" bindload="onImageLoad" bindtouchstart="onTouchStart" bindtouchmove="onTouchMove" bindtouchend="onTouchEnd" style="transform: translate({{imageX}}px, {{imageY}}px);" /> </view> ``` ```javascript // components/draggable-image/draggable-image.js Component({ properties: { src: String, // 图片路径 screenWidth: Number, // 父组件传递的屏幕宽度 screenHeight: Number // 父组件传递的屏幕高度 }, data: { imageX: 0, // 图片X偏移 imageY: 0, // 图片Y偏移 startX: 0, // 触摸起点X(相对屏幕) startY: 0, // 触摸起点Y(相对屏幕) imgWidth: 0, // 图片实际宽度 imgHeight: 0, // 图片实际高度 offsetX: 0, // 触摸点相对图片左上角的X偏移 offsetY: 0 // 触摸点相对图片左上角的Y偏移 }, methods: { // 图片加载完成时获取尺寸 onImageLoad(e) { const { width, height } = e.detail this.setData({ imgWidth: width, imgHeight: height }) }, // 触摸开始事件 onTouchStart(e) { // 获取触摸点在屏幕中的绝对位置 const touch = e.touches[0] const query = this.createSelectorQuery() // 获取图片在屏幕中的位置 query.select('.image').boundingClientRect(rect => { const { screenWidth, screenHeight } = this.properties const { imgWidth, imgHeight } = this.data // 计算触摸点相对于图片左上角的偏移 const offsetX = touch.pageX - rect.left const offsetY = touch.pageY - rect.top this.setData({ startX: touch.pageX, startY: touch.pageY, offsetX: offsetX, offsetY: offsetY, // 计算边界限制值 maxLeft: (screenWidth - imgWidth) / 2, maxRight: (screenWidth - imgWidth) / 2, maxTop: (screenHeight - imgHeight) / 2, maxBottom: (screenHeight - imgHeight) / 2 }) }).exec() }, // 触摸移动事件(添加边界限制) onTouchMove(e) { const touch = e.touches[0] const { startX, startY, offsetX, offsetY, maxLeft, maxRight, maxTop, maxBottom } = this.data // 计算移动差值(考虑触摸点偏移) let diffX = touch.pageX - startX - offsetX let diffY = touch.pageY - startY - offsetY // 应用边界限制 diffX = Math.min(Math.max(diffX, -maxLeft), maxRight) diffY = Math.min(Math.max(diffY, -maxTop), maxBottom) this.setData({ imageX: diffX, imageY: diffY }) }, // 触摸结束事件 onTouchEnd() { // 可添加回弹效果 } } }) ``` ```css /* components/draggable-image/draggable-image.wxss */ .container { position: relative; width: 100%; height: 100%; overflow: hidden; } .image { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); will-change: transform; max-width: 100%; max-height: 80vh; } ``` ### 父组件实现 ```html <!-- 父页面.wxml --> <view class="container"> <draggable-image src="/images/example.jpg" screen-width="{{screenWidth}}" screen-height="{{screenHeight}}" /> </view> ``` ```javascript // 父页面.js Page({ data: { screenWidth: 0, screenHeight: 0 }, onLoad() { const systemInfo = wx.getSystemInfoSync() this.setData({ screenWidth: systemInfo.windowWidth, screenHeight: systemInfo.windowHeight }) } }) ``` ```css /* 父页面.wxss */ .container { position: relative; width: 100%; height: 100vh; } ``` ## 解决方案关键点 ### 1. 触摸点偏移修正 - 使用`boundingClientRect()`获取图片在屏幕中的绝对位置 - 计算触摸点相对于图片左上角的偏移: $$ \Delta x = \text{pageX} - \text{rect.left} $$ $$ \Delta y = \text{pageY} - \text{rect.top} $$ - 移动时减去这个偏移量,确保触摸点与图片位置一致 ### 2. 边界限制算法 - 最大可移动范围计算公式: $$ \begin{cases} -\dfrac{W_{\text{screen}} - W_{\text{image}}}{2} \leq \Delta x \leq \dfrac{W_{\text{screen}} - W_{\text{image}}}{2} \\ \\ -\dfrac{H_{\text{screen}} - H_{\text{image}}}{2} \leq \Delta y \leq \dfrac{H_{\text{screen}} - H_{\text{image}}}{2} \end{cases} $$ - 使用`Math.min`和`Math.max`确保位置不超出边界 ### 3. 组件通信 - 父组件获取屏幕尺寸后传递给子组件 - 子组件使用`properties`接收屏幕尺寸数据 ## 高级优化 ### 1. 添加弹性边界效果 ```javascript // 在onTouchMove中添加 const boundaryBuffer = 20 // 弹性缓冲区 if (diffX > maxRight - boundaryBuffer) { const overflow = diffX - (maxRight - boundaryBuffer) diffX = maxRight - boundaryBuffer + overflow * 0.3 } ``` ### 2. 图片缩放支持 ```javascript // 添加缩放状态 data: { scale: 1, minScale: 0.5, maxScale: 3 }, // 添加双指缩放事件 onTouchMove(e) { if (e.touches.length === 2) { // 处理双指缩放 const touch1 = e.touches[0] const touch2 = e.touches[1] const distance = Math.sqrt( Math.pow(touch2.pageX - touch1.pageX, 2) + Math.pow(touch2.pageY - touch1.pageY, 2) ) if (this.initialDistance) { const scale = this.data.scale * (distance / this.initialDistance) this.setData({ scale: Math.min(Math.max(scale, this.data.minScale), this.data.maxScale) }) } else { this.initialDistance = distance } } else { // 单指拖动逻辑 } } ``` ### 3. 性能优化 - 使用`transform`代替`top/left`布局,避免重排 - 添加`will-change: transform`提示浏览器优化 - 减少不必要的`setData`调用 ## 相关问题 1. 如何在微信小程序中实现图片拖动时的旋转效果? 2. 子组件与父组件之间的复杂数据通信如何实现? 3. 如何在小程序中实现图片裁剪功能? 4. 如何处理不同尺寸屏幕上的拖动边界自适应问题? 5. 如何优化小程序中图片拖动的性能? 6. 在小程序中如何实现多图层的拖动交互? 通过以上解决方案,您可以完美解决子组件中长按图片拖动时的触摸点偏移和超出屏幕边界问题[^1]。实际实现时可根据具体需求调整边界计算参数和交互细节[^2]。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值