溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

微信小程序中怎么實(shí)現(xiàn)拖動(dòng)圖片實(shí)現(xiàn)移動(dòng)、放大、旋轉(zhuǎn)

發(fā)布時(shí)間:2021-06-17 14:58:08 來(lái)源:億速云 閱讀:1565 作者:Leah 欄目:web開發(fā)

這篇文章將為大家詳細(xì)講解有關(guān)微信小程序中怎么實(shí)現(xiàn)拖動(dòng)圖片實(shí)現(xiàn)移動(dòng)、放大、旋轉(zhuǎn),文章內(nèi)容質(zhì)量較高,因此小編分享給大家做個(gè)參考,希望大家閱讀完這篇文章后對(duì)相關(guān)知識(shí)有一定的了解。

微信小程序這里提供了兩個(gè)API

wx.createContext() 創(chuàng)建并返回繪圖上下文context對(duì)象

  • getActions 獲取當(dāng)前context上存儲(chǔ)的繪圖動(dòng)作,對(duì)應(yīng)wx.drawCanvas(object)中的actions

  • clearActions 清空當(dāng)前的存儲(chǔ)繪圖動(dòng)作

wx.drawCanvas(object) 繪制

  • canvasId 畫布標(biāo)識(shí),傳入的cavas-id,這里的標(biāo)識(shí)可以為Number,也可以是String

  • actions 繪圖動(dòng)作數(shù)組,由wx.createContext創(chuàng)建的context,調(diào)用getActions方法導(dǎo)出繪圖動(dòng)作數(shù)組。

最近接到一個(gè)任務(wù),在微信小程序內(nèi)拖動(dòng)圖片組件實(shí)現(xiàn)移動(dòng)、放大、旋轉(zhuǎn),并記錄這些圖片的移動(dòng)位置,放大比例,旋轉(zhuǎn)角度,在一個(gè)畫布上生成一張圖片,最后保存到手機(jī)相冊(cè)。

我的具體實(shí)現(xiàn)思路是這樣的:

 一共三個(gè)功能,可以先把功能分為圖片 拖動(dòng) 和圖片 旋轉(zhuǎn)縮放 , 把圖片的縮放和旋轉(zhuǎn)做在了一起。

1.圖片移動(dòng):可移動(dòng)的圖片肯定是要?jiǎng)討B(tài)生成的,所以不能寫死,應(yīng)該是個(gè)數(shù)組,具備很多的屬性。

例如:(并不是我項(xiàng)目的真實(shí)數(shù)據(jù))

itemList: [{
      id: 1,
      image: '1.png',//圖片地址
      top: 100,//初始圖片的位置 
      left: 100,
      x: 155, //初始圓心位置,可再downImg之后又寬高和初始的圖片位置得出
      y: 155,
      scale: 1,//縮放比例 1為不縮放
      angle: 0,//旋轉(zhuǎn)角度
      active: false //判定點(diǎn)擊狀態(tài)
    }, {
      id: 2,
      image: '2.png',
      top: 50,
      left: 50,
      x: 155,
      y: 155,
      scale: 1,
      angle: 0,
      active: false

事件綁定圖片上或者圖片的父級(jí),綁定bindtouchstart  bindtouchmove事件。再bindtouchstart事件里,獲取手指點(diǎn)擊的某一個(gè)圖片的點(diǎn)擊坐標(biāo),并記錄在這個(gè)圖片對(duì)象的屬性里面,在bindtouchmove事件里,移動(dòng)的時(shí)候記錄移動(dòng)后的坐標(biāo),并算出倆次滑動(dòng)的距離差值,追加給圖片對(duì)象的left、top、x、y上,最后把本次滑動(dòng)的坐標(biāo)賦值給bindtouchmove事件里拿到的坐標(biāo),作為老坐標(biāo)。這樣就可以實(shí)現(xiàn)圖片的滑動(dòng)。

注:代碼里的 items  只是我定義的一個(gè)全局變量,是一個(gè)空數(shù)組,在onLoad函數(shù)里 items = this.data.itemLits; 

這樣就不會(huì)頻繁的去setData,我只需要處理items,處理完之后,再this.setData({itemLits:items })

WraptouchStart: function (e) {
    for (let i = 0; i < items.length; i++) { //旋轉(zhuǎn)數(shù)據(jù)找到點(diǎn)擊的
      items[i].active = false;
      if (e.currentTarget.dataset.id == items[i].id) {
        index = i;  //記錄下標(biāo)
        items[index].active = true; //開啟點(diǎn)擊屬性
      }
    }
    
    items[index].lx = e.touches[0].clientX; // 記錄點(diǎn)擊時(shí)的坐標(biāo)值
    items[index].ly = e.touches[0].clientY;
    this.setData({  //賦值 
      itemList: items
    })
  }
  , WraptouchMove: function (e) {
    //移動(dòng)時(shí)的坐標(biāo)值也寫圖片的屬性里
    items[index]._lx = e.touches[0].clientX;
    items[index]._ly = e.touches[0].clientY;
    
    //追加改動(dòng)值
    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({//賦值就移動(dòng)了
      itemList: items
    })
  }

2.圖片的旋轉(zhuǎn)和縮放,因?yàn)閳D片上已經(jīng)有了touch事件,所以解決辦法采用常規(guī)的在圖片的一角添加一個(gè)控件解決這個(gè)問(wèn)題,控件大致如圖:

微信小程序中怎么實(shí)現(xiàn)拖動(dòng)圖片實(shí)現(xiàn)移動(dòng)、放大、旋轉(zhuǎn)

左邊控件是刪除按鈕,右邊控件則是手指按著旋轉(zhuǎn)切縮放圖片的控件,綁定bindtouchstart  bindtouchmove事件。

index也是設(shè)置的全局變量。

// 觸摸開始事件 items是this.data.itemList的全局變量,便于賦值 所有的值都應(yīng)給到對(duì)應(yīng)的對(duì)象里
  touchStart: function (e) {
    //找到點(diǎn)擊的那個(gè)圖片對(duì)象,并記錄
    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;
      }
    }
     //獲取作為移動(dòng)前角度的坐標(biāo)
    items[index].tx = e.touches[0].clientX;
    items[index].ty = e.touches[0].clientY;
    //移動(dòng)前的角度
    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)
  },
  // 觸摸移動(dòng)事件 
  touchMove: function (e) {
    //記錄移動(dòng)后的位置
    items[index]._tx = e.touches[0].clientX;
    items[index]._ty = e.touches[0].clientY;
    //移動(dòng)的點(diǎn)到圓心的距離 * 因?yàn)閳A心的坐標(biāo)是相對(duì)與父元素定位的 ,所有要減去父元素的OffsetLeft和OffsetTop來(lái)計(jì)算移動(dòng)的點(diǎn)到圓心的距離
    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; //手指滑動(dòng)的點(diǎn)到圓心的距離與半徑的比值作為圖片的放大比例
    items[index].oScale = 1 / items[index].scale;//圖片放大響應(yīng)的右下角按鈕同比縮小
 
    //移動(dòng)后位置的角度
    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; //賦值
 
    //用過(guò)移動(dòng)后的坐標(biāo)賦值為移動(dòng)前坐標(biāo)
    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
    })
  }

頁(yè)面上是這樣寫的:

<!-- *************操作區(qū)域************* -->
    <block wx:for="{{itemList}}" wx:key="{{item.id}}">
      <!-- 圓心坐標(biāo) <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":""}}' >
          <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>
<!-- **************操作區(qū)域************ -->

這樣一來(lái)就解決了微信小程序內(nèi)拖動(dòng)圖片實(shí)現(xiàn)移動(dòng)、放大、旋轉(zhuǎn)的問(wèn)題,操作也比較順滑,也耗費(fèi)我近四天的時(shí)間才把我的小程序上線,代碼有點(diǎn)混亂,如果各位大佬有什么意見(jiàn)可以給我留言,我的小程序名字是:水逆轉(zhuǎn)運(yùn)符文,以后會(huì)持續(xù)改進(jìn)。

2018/5/7補(bǔ)充一條生成圖片時(shí),組件的屬性:

微信小程序中怎么實(shí)現(xiàn)拖動(dòng)圖片實(shí)現(xiàn)移動(dòng)、放大、旋轉(zhuǎn)

我的失誤,忘了附上角度計(jì)算函數(shù)  countDeg :  

/*
 *參數(shù)1和2為圖片圓心坐標(biāo)
 *參數(shù)3和4為手點(diǎn)擊的坐標(biāo)
 *返回值為手點(diǎn)擊的坐標(biāo)到圓心的角度
 */
  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;//鼠標(biāo)相對(duì)于旋轉(zhuǎn)中心的角度
    console.log("ox.oy:", ox, oy)
    if (ox < 0 && oy < 0)//相對(duì)在左上角,第四象限,js中坐標(biāo)系是從左上角開始的,這里的象限是正常坐標(biāo)系 
    {
      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;
  }

計(jì)算觸摸點(diǎn)到圓心的距離:

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

點(diǎn)擊配件時(shí)的事件(因?yàn)樵傥覝y(cè)試在canvas中,圖片不能是網(wǎng)絡(luò)路徑,所以需要下載): 【18/6/22】

tpDownload: function(data, isDownload) { //data為組件的參數(shù),isDownload判斷是否為https網(wǎng)絡(luò)圖片來(lái)判斷是否需要下載
    if (yy < 0) { //改變生成圖片時(shí)的位置
      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();
    }
  }

我的項(xiàng)目中生成canvas用到的代碼 (繪圖是通過(guò)保存按鈕觸發(fā))

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內(nèi)背景圖占canvas的百分比,若全背景num =1
    this.sysData.windowWidth * 0.75為可移動(dòng)區(qū)的寬度
    prop值為canvas內(nèi)背景的寬度與可移動(dòng)區(qū)域的寬度的比,如一致,則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); //圓心坐標(biāo)
      maskCanvas.rotate(currentValue.angle * Math.PI / 180); // 旋轉(zhuǎn)值
      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)
    })
  }

關(guān)于微信小程序中怎么實(shí)現(xiàn)拖動(dòng)圖片實(shí)現(xiàn)移動(dòng)、放大、旋轉(zhuǎn)就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,可以學(xué)到更多知識(shí)。如果覺(jué)得文章不錯(cuò),可以把它分享出去讓更多的人看到。

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI