您好,欢迎来到好走旅游网。
搜索
您的当前位置:首页微信小程序内拖动图片实现移动、放大、旋转的方法

微信小程序内拖动图片实现移动、放大、旋转的方法

来源:好走旅游网

屏幕就像是数学上的坐标轴,且在第四象限,以屏幕左上角为圆点,X轴向右为正向左为负,Y轴向下为正向上为负(这点和数学上相反的)以圆点为基点画个距离圆点上下50宽高100的矩形来演示canvas基本用法

微信小程序这里提供了两个API

wx.createContext() 创建并返回绘图上下文context对象

  • getActions 获取当前context上存储的绘图动作,对应wx.drawCanvas(object)中的actions
  • clearActions 清空当前的存储绘图动作
  • wx.drawCanvas(object) 绘制

  • canvasId 画布标识,传入的cavas-id,这里的标识可以为Number,也可以是String
  • actions 绘图动作数组,由wx.createContext创建的context,调用getActions方法导出绘图动作数组。
  • 最近接到一个任务,在微信小程序内拖动图片组件实现移动、放大、旋转,并记录这些图片的移动位置,放大比例,旋转角度,在一个画布上生成一张图片,最后保存到手机相册。

    我的具体实现思路是这样的:

     一共三个功能,可以先把功能分为图片 拖动 和图片 旋转缩放 , 把图片的缩放和旋转做在了一起。

    1.图片移动:可移动的图片肯定是要动态生成的,所以不能写死,应该是个数组,具备很多的属性。

    例如:(并不是我项目的真实数据)

    itemList: [{
     id: 1,
     image: '1.png',//图片地址
     top: 100,//初始图片的位置 
     left: 100,
     x: 155, //初始圆心位置,可再downImg之后又宽高和初始的图片位置得出
     y: 155,
     scale: 1,//缩放比例 1为不缩放
     angle: 0,//旋转角度
     active: false //判定点击状态
     }, {
     id: 2,
     image: '2.png',
     top: 50,
     left: 50,
     x: 155,
     y: 155,
     scale: 1,
     angle: 0,
     active: false
    

    事件绑定图片上或者图片的父级,绑定bindtouchstart  bindtouchmove事件。再bindtouchstart事件里,获取手指点击的某一个图片的点击坐标,并记录在这个图片对象的属性里面,在bindtouchmove事件里,移动的时候记录移动后的坐标,并算出俩次滑动的距离差值,追加给图片对象的left、top、x、y上,最后把本次滑动的坐标赋值给bindtouchmove事件里拿到的坐标,作为老坐标。这样就可以实现图片的滑动。

    注:代码里的 items  只是我定义的一个全局变量,是一个空数组,在onLoad函数里 items = this.data.itemLits; 

    这样就不会频繁的去setData,我只需要处理items,处理完之后,再this.setData({itemLits:items })

    WraptouchStart: function (e) {
     for (let i = 0; i < items.length; i++) { //旋转数据找到点击的
     items[i].active = false;
     if (e.currentTarget.dataset.id == items[i].id) {
     index = i; //记录下标
     items[index].active = true; //开启点击属性
     }
     }
     
     items[index].lx = e.touches[0].clientX; // 记录点击时的坐标值
     items[index].ly = e.touches[0].clientY;
     this.setData({ //赋值 
     itemList: items
     })
     }
     , WraptouchMove: function (e) {
     //移动时的坐标值也写图片的属性里
     items[index]._lx = e.touches[0].clientX;
     items[index]._ly = e.touches[0].clientY;
     
     //追加改动值
     items[index].left += items[index]._lx - items[index].lx; // x方向
     items[index].top += items[index]._ly - items[index].ly; // y方向
     items[index].x += items[index]._lx - items[index].lx;
     items[index].y += items[index]._ly - items[index].ly;
     
     //把新的值赋给老的值
     items[index].lx = e.touches[0].clientX; 
     items[index].ly = e.touches[0].clientY;
     this.setData({//赋值就移动了
     itemList: items
     })
     }

    2.图片的旋转和缩放,因为图片上已经有了touch事件,所以解决办法采用常规的在图片的一角添加一个控件解决这个问题,控件大致如图:

    左边控件是删除按钮,右边控件则是手指按着旋转切缩放图片的控件,绑定bindtouchstart  bindtouchmove事件。

    index也是设置的全局变量。

    // 触摸开始事件 items是this.data.itemList的全局变量,便于赋值 所有的值都应给到对应的对象里
     touchStart: function (e) {
     //找到点击的那个图片对象,并记录
     for (let i = 0; i < items.length; i++) {
     items[i].active = false;
     
     if (e.currentTarget.dataset.id == items[i].id) {
     console.log('e.currentTarget.dataset.id', e.currentTarget.dataset.id)
     index = i;
     console.log(items[index])
     items[index].active = true;
     }
     }
     //获取作为移动前角度的坐标
     items[index].tx = e.touches[0].clientX;
     items[index].ty = e.touches[0].clientY;
     //移动前的角度
     items[index].anglePre = this.countDeg(items[index].x, items[index].y, items[index].tx, items[index].ty)
     //获取图片半径
     items[index].r = this.getDistancs(items[index].x, items[index].y, items[index].left, items[index].top)
     },
     // 触摸移动事件 
     touchMove: function (e) {
     //记录移动后的位置
     items[index]._tx = e.touches[0].clientX;
     items[index]._ty = e.touches[0].clientY;
     //移动的点到圆心的距离 * 因为圆心的坐标是相对与父元素定位的 ,所有要减去父元素的OffsetLeft和OffsetTop来计算移动的点到圆心的距离
     items[index].disPtoO = this.getDistancs(items[index].x, items[index].y, items[index]._tx - this.sysData.windowWidth * 0.125, items[index]._ty - 10)
     
     items[index].scale = items[index].disPtoO / items[index].r; //手指滑动的点到圆心的距离与半径的比值作为图片的放大比例
     items[index].oScale = 1 / items[index].scale;//图片放大响应的右下角按钮同比缩小
     
     //移动后位置的角度
     items[index].angleNext = this.countDeg(items[index].x, items[index].y, items[index]._tx, items[index]._ty)
     //角度差
     items[index].new_rotate = items[index].angleNext - items[index].anglePre;
     
     //叠加的角度差
     items[index].rotate += items[index].new_rotate;
     items[index].angle = items[index].rotate; //赋值
     
     //用过移动后的坐标赋值为移动前坐标
     items[index].tx = e.touches[0].clientX;
     items[index].ty = e.touches[0].clientY;
     items[index].anglePre = this.countDeg(items[index].x, items[index].y, items[index].tx, items[index].ty)
     
     //赋值setData渲染
     this.setData({
     itemList: items
     })
     }

    页面上是这样写的:

    <!-- *************操作区域************* -->
     <block wx:for="{{itemList}}" wx:key="{{item.id}}">
     <!-- 圆心坐标 <text style='position:absolute;top:{{item.y}}px;left:{{item.x}}px;width:2px;height:2px;background-color:yellow;z-index:500'></text> -->
     <view class='touchWrap' style='transform: scale({{item.scale}});top:{{item.top}}px;left:{{item.left}}px; '>
     <view class='imgWrap {{item.active? "touchActive":""}}' style="transform: rotate({{item.angle}}deg);">
     <image src='{{item.image}}' data-id='{{item.id}}' style='width:{{item.width}}px;height:{{item.height}}px;' bindtouchstart='WraptouchStart' bindload='loadImg' hidden='{{!item.isload}} bindtouchmove='WraptouchMove' bindtouchend='WraptouchEnd'></image>
     <image class='x' src='../../images/x.png' style='transform: scale({{item.oScale}});transform-origin:center;' data-id='{{item.id}}' bindtap='deleteItem'></image>
     <image class='o' src='../../images/o.png' style='transform: scale({{item.oScale}});transform-origin:center;' data-id='{{item.id}}' bindtouchstart='touchStart' bindtouchmove='touchMove' bindtouchend='touchEnd'></image>
     </view>
     </view>
     </block>
    <!-- **************操作区域************ -->

    这样一来就解决了微信小程序内拖动图片实现移动、放大、旋转的问题,操作也比较顺滑,也耗费我近四天的时间才把我的小程序上线,代码有点混乱,如果各位大佬有什么意见可以给我留言,我的小程序名字是:水逆转运符文,以后会持续改进。

    2018/5/7补充一条生成图片时,组件的属性:

    我的失误,忘了附上角度计算函数  countDeg :  

    /*
     *参数1和2为图片圆心坐标
     *参数3和4为手点击的坐标
     *返回值为手点击的坐标到圆心的角度
     */
     countDeg: function (cx, cy, pointer_x, pointer_y) {
     var ox = pointer_x - cx;
     var oy = pointer_y - cy;
     var to = Math.abs(ox / oy);
     var angle = Math.atan(to) / (2 * Math.PI) * 360;//鼠标相对于旋转中心的角度
     console.log("ox.oy:", ox, oy)
     if (ox < 0 && oy < 0)//相对在左上角,第四象限,js中坐标系是从左上角开始的,这里的象限是正常坐标系 
     {
     angle = -angle;
     } else if (ox <= 0 && oy >= 0)//左下角,3象限 
     {
     angle = -(180 - angle)
     } else if (ox > 0 && oy < 0)//右上角,1象限 
     {
     angle = angle;
     } else if (ox > 0 && oy > 0)//右下角,2象限 
     {
     angle = 180 - angle;
     }
     
     return angle;
     }

    计算触摸点到圆心的距离:

    getDistancs(cx, cy, pointer_x, pointer_y) {
     var ox = pointer_x - cx;
     var oy = pointer_y - cy;
     return Math.sqrt(
     ox * ox + oy * oy
     );
     }

    点击配件时的事件(因为再我测试在canvas中,图片不能是网络路径,所以需要下载): 【18/6/22】

    tpDownload: function(data, isDownload) { //data为组件的参数,isDownload判断是否为https网络图片来判断是否需要下载
     if (yy < 0) { //改变生成图片时的位置
     speed = -speed
     }
     if (yy > 300) {
     speed = -speed
     }
     yy += speed;
     let _this = this;
     let newTpdata = {};
     newTpdata.id = data.id;
     newTpdata.itemid = data.itemid;
     newTpdata.top = 100 + yy;
     newTpdata.left = 100;
     newTpdata.width = _this.sysData.windowWidth / 4;
     newTpdata.scale = 1;
     newTpdata.angle = 0;
     newTpdata.rotate = 0;
     newTpdata.active = true;
     for (let i = 0; i < items.length; i++) {
     items[i].active = false;
     }
     if (isDownload) {
     wx.downloadFile({
     url: data.image,
     success: res => {
     newTpdata.image = res.tempFilePath;
     items.push(newTpdata);
     _this.setData({
     itemList: items
     })
     wx.hideLoading();
     }
     })
     } else {
     newTpdata.image = data.image;
     items.push(newTpdata);
     _this.setData({
     itemList: items
     })
     wx.hideLoading();
     }
     }

    我的项目中生成canvas用到的代码 (绘图是通过保存按钮触发)

    save: function() {
     this.setData({
     showCanvas: true,
     canvasHeight: this.sysData.windowHeight * 0.85
     })
     let obj = this.data.item;
     /*
     canvasWidth值为canvas宽度;
     this.data.canvasPre是占屏幕宽度的百分比(80)
     */
     let canvasWidth = this.sysData.windowWidth * this.data.canvasPre / 100; //
     /*
     num为canvas内背景图占canvas的百分比,若全背景num =1
     this.sysData.windowWidth * 0.75为可移动区的宽度
     prop值为canvas内背景的宽度与可移动区域的宽度的比,如一致,则prop =1;
     */
     let prop = (canvasWidth * num) / (this.sysData.windowWidth * 0.75);
     maskCanvas.save();
     maskCanvas.beginPath();
     //一张白图
     maskCanvas.setFillStyle('#fff');
     maskCanvas.fillRect(0, 0, this.sysData.windowWidth, this.data.canvasHeight)
     maskCanvas.closePath();
     maskCanvas.stroke();
     //图头像
     let image = {
     w: canvasWidth * num * 0.287,
     h: canvasWidth * num * 0.287,
     r: canvasWidth * num * 0.287 / 2
     };
     //画背景 hCw 为 1.7781 背景图的高宽比
     maskCanvas.drawImage(obj.bgImg, canvasWidth * (1 - num) / 2, 10, canvasWidth * num, canvasWidth * num * hCw)
     //画底图
     maskCanvas.drawImage('../../images/xcx.png', canvasWidth * (1 - num) / 2, canvasWidth * num * hCw + 15, canvasWidth * num, this.data.canvasHeight * 0.15)
     //画原
     maskCanvas.save();
     maskCanvas.beginPath();
     maskCanvas.arc(canvasWidth / 2, canvasWidth * num * hCw * obj.userTop / 100 + 10 + image.w / 2, image.r, 0, Math.PI * 2, false);
     // maskCanvas.stroke()
     maskCanvas.clip(); //截取
     //画头像
     maskCanvas.drawImage(obj.avatarUrl, (canvasWidth - image.w) / 2, canvasWidth * num * hCw * obj.userTop / 100 + 10, image.w, image.h)
     maskCanvas.closePath();
     maskCanvas.restore();
     //绘制文字
     maskCanvas.save();
     maskCanvas.beginPath();
     let fontSize = this.sysData.screenWidth / 375 * 15;
     let textColor = obj.color || '#000';
     maskCanvas.setFontSize(parseInt(fontSize) * prop)
     maskCanvas.setFillStyle(textColor)
     maskCanvas.setTextAlign('center')
     maskCanvas.fillText(obj.nickName, canvasWidth / 2, obj.titleTop / 100 * canvasWidth * num * hCw + 10 * 0.9 * prop + fontSize * prop);
     maskCanvas.closePath();
     maskCanvas.stroke();
     /** 
     * x
     * y
     * scale
     * prop
     * width
     * height
     * 
     */
     //画组件
     items.forEach((currentValue,index)=>{
     maskCanvas.save();
     maskCanvas.translate(canvasWidth * (1 - num) / 2, 10);
     maskCanvas.beginPath();
     maskCanvas.translate(currentValue.x * prop, currentValue.y * prop); //圆心坐标
     maskCanvas.rotate(currentValue.angle * Math.PI / 180); // 旋转值
     maskCanvas.translate(-(currentValue.width * currentValue.scale * prop / 2), -(currentValue.height * currentValue.scale * prop / 2))
     maskCanvas.drawImage(currentValue.image, 0, 0, currentValue.width * currentValue.scale * prop, currentValue.height * currentValue.scale * prop);
     maskCanvas.restore();
     })
     maskCanvas.draw(false, (e)=> {
     wx.canvasToTempFilePath({
     canvasId: 'maskCanvas',
     success: res => {
     this.setData({
     canvasTemImg: res.tempFilePath
     })
     }
     }, this)
     })
     }
    

    Copyright © 2019- haog.cn 版权所有 赣ICP备2024042798号-2

    违法及侵权请联系:TEL:199 1889 7713 E-MAIL:2724546146@qq.com

    本站由北京市万商天勤律师事务所王兴未律师提供法律服务