記錄一下微信小程序圖片拖拽,放大,位移效果實現

由於需求須要對圖片進行操做,放大,位移,旋轉等
思路也是參考別的博客
這個寫的比較全
https://blog.csdn.net/king096...
我看的第一篇是這個
https://www.jb51.net/article/...css


實現思路
1 在一個區域裏繪製圖片,
2 記錄全部圖片的座標,旋轉角度,尺寸,中心點,縮放比例
3 建立cavnas畫布,白底色,而後根據記錄的圖片的一些狀態繪製
4 根據canvas生產出圖片canvas

先上效果
image.png
藍框爲拖拽區域
紅框爲cavnas繪製部分
貼代碼
wxml
canvas標籤正常狀況下應該是隱藏或者讓他定位到十萬八千里外
由於須要展現效果,因此就沒有隱藏,實際上線能夠本身隱藏掉app

<view class="container">
  <button class="mini-btn" bindtap="generate" type="primary" size="mini">生成圖片</button>
<block wx:for="{{materialList}}" wx:key="index">
  <image class="img-list" bindtap="addImg" data-index="{{index}}" mode="aspectFit"  src="{{item.image}}"></image>
</block>
{{itemList.length}}
  <view class="img-box" id="img-box">
    <!-- *************操做區域************* -->
    <block wx:for="{{itemList}}" wx:key="index">
      <!-- 圓心座標 <text style='position:absolute;top:{{item.y}}px;left:{{item.x}}px;width:2px;height:2px;background-color:yellow;z-index:500'></text> -->
      <!-- {{item.scale}}---{{item.r}} -->
      <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);">
          <view>
            <image style="width:{{item.height}}px; height:{{item.width}}px" class="item-img" src='{{item.image}}' bindload='loadImg' data-index="{{index}}" data-id='{{item.id}}' bindtouchstart='wraptouchStart' bindtouchmove='WraptouchMove'></image>
          </view>
          <!-- <image  src='{{item.image}}'   bindload='loadImg' bindtouchend='WraptouchEnd'></image> -->
          <image class='x' hidden="{{!item.active}}" bindtap="hiddenImg" data-index="{{index}}" src='../../assets/img/wqy-close.png' style='transform: scale({{item.oScale}});transform-origin:center;' data-id='{{item.id}}'></image>
          <image class='o' hidden="{{!item.active}}" data-index="{{index}}" src='../../assets/img/wqy-stretch.png' style='transform: scale({{item.oScale}});transform-origin:center;' data-id='{{item.id}}' bindtouchstart='touchStart' bindtouchmove='touchMove'>
          </image>
        </view>
      </view>
    </block>
  </view>
  <canvas class='maskCanvas' canvas-id="maskCanvas" style='width:{{canvasWidth}}px; height:{{canvasHeight}}px;'></canvas>
</view>

js部分測試

/*
 * @Description: 
 * @Author: 冷山冷杉 <wqy.mail@foxmail.com>
 * @Date: 2020-06-04 11:58:14
 * @LastEditTime: 2020-06-05 16:14:51
 * @LastEditors: 冷山冷杉  <wqy.mail@foxmail.com>
 * @FilePath: \mini-fullalumni\pages\photoTest\photoTest.js
 */
const app = getApp()
const maskCanvas = wx.createCanvasContext('maskCanvas', this)
let items = []
Page({
  data: {
    itemList: [
    ],
    materialList: [
      {
        id: null,
        image: 'https://img3.doubanio.com/view/subject/m/public/s9074663.jpg',//圖片地址
        top: 0,//初始圖片的位置 
        left: 0,
        x: 0, //初始圓心位置,可再downImg以後又寬高和初始的圖片位置得出
        y: 0,
        scale: 1,//縮放比例 1爲不縮放
        angle: 0,//旋轉角度
        active: true //斷定點擊狀態
      },
      {
        id: null,
        image: 'https://img9.doubanio.com/view/subject/m/public/s3893375.jpg',
        top: 0,
        left: 0,
        x: 0,
        y: 0,
        scale: 1,
        angle: 0,
        active: false
      }
    ],
    canvasWidth: null,
    canvasHeight: null
  },
  onReady() {
    const query = wx.createSelectorQuery()
    query.select('#img-box').boundingClientRect()
    query.selectViewport().scrollOffset()
    query.exec((res) => {
      this.setData({
        canvasWidth: res[0].width,
        canvasHeight: res[0].height
      })
    })
    // wx.getSystemInfo({ // 獲取系統信息
    //   success: sysData => {
    //     this.sysData = sysData
    //     // 設置畫布寬高,this.sysData.windowWidth爲屏幕的寬度
    //     this.setData({
    //       canvasWidth: this.sysData.windowWidth, // 若是以爲不清晰的話,能夠把全部組件、寬高放大一倍
    //       canvasHeight: this.sysData.windowWidth
    //     })
    //   }
    // })
  },
  addImg(e) {
    let index = e.currentTarget.dataset.index
    let materialList = this.data.materialList
    let itemList = this.data.itemList
    if (itemList.length) {
      materialList[index].id = itemList[itemList.length - 1].id + 1
    } else {
      materialList[index].id = 1
    }
    itemList.push(JSON.parse(JSON.stringify(materialList[index])))
    this.setData({ itemList })
  },
  loadImg(e) {
    let index = e.currentTarget.dataset.index
    let itemList = this.data.itemList
    // x,y爲圓心的距離, +25: 按鈕定位的距離 + 按鈕自身大小/2
    itemList[index].width = e.detail.width
    itemList[index].height = e.detail.height
    itemList[index].x = e.detail.width / 2 + 25
    itemList[index].y = e.detail.height / 2 + 25
    this.setData({ itemList })
  },
  hiddenImg(e) {
    let index = e.currentTarget.dataset.index
    let itemList = this.data.itemList
    itemList.splice(index, 1)
    this.setData({ itemList })
  },
  wraptouchStart: function (e) {
    let items = this.data.itemList;
    for (let i = 0; i < items.length; i++) { //旋轉數據找到點擊的
      items[i].active = false;
      if (e.currentTarget.dataset.id == items[i].id) {
        items[i].active = true; //開啓點擊屬性
        items[i].lx = e.touches[0].clientX; // 記錄點擊時的座標值
        items[i].ly = e.touches[0].clientY;
      }
    }
    this.setData({  //賦值 
      itemList: items
    })
  },
  WraptouchMove: function (e) {

    let index = e.currentTarget.dataset.index
    let items = this.data.itemList;
    //移動時的座標值也寫圖片的屬性裏
    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
    })
  },
  // 觸摸開始事件 items是this.data.itemList的全局變量,便於賦值 全部的值都應給到對應的對象裏
  touchStart: function (e) {

    //找到點擊的那個圖片對象,並記錄
    let items = this.data.itemList;
    let index = e.currentTarget.dataset.index

    for (let i = 0; i < items.length; i++) {
      items[i].active = false;
      if (e.currentTarget.dataset.id == items[i].id) {
        items[i].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) {
    let items = this.data.itemList;

    let index = e.currentTarget.dataset.index
    // items[index].x = e.detail.width / 2
    // items[index].y = e.detail.height / 2
    // this.setData({itemList: items})
    //記錄移動後的位置
    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, items[index]._ty)
    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].angle += items[index].new_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
    })
  },
  /*
 *參數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;//鼠標相對於旋轉中心的角度
    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
    );
  },
  generate: function () {
    maskCanvas.save();
    maskCanvas.beginPath();
    //一張白圖
    maskCanvas.setFillStyle('#fff');
    maskCanvas.fillRect(0, 0, this.data.windowWidth, this.data.canvasHeight)
    maskCanvas.closePath();
    maskCanvas.stroke();
    this.data.itemList.forEach((val, index) => {
      maskCanvas.save();
      maskCanvas.translate(0, 0);
      maskCanvas.beginPath();
      maskCanvas.translate(val.x, val.y); // 圓心座標
      maskCanvas.rotate(val.angle * Math.PI / 180);
      maskCanvas.translate(-(val.width * val.scale / 2) - 25, -(val.height * val.scale / 2) - 25)
      maskCanvas.drawImage(val.image, 0, 0, val.width * val.scale, val.height * val.scale);
      maskCanvas.restore();
    })
    maskCanvas.draw(false, (e) => {
      wx.canvasToTempFilePath({
        canvasId: 'maskCanvas',
        success: res => {
          this.setData({
            canvasTemImg: res.tempFilePath
          })
          console.log(res.tempFilePath);

        }
      }, this)
    })
  }
})

cssthis

page{
  height:100%;
}
.img-list{
  width: 100rpx;
  height: 100rpx
}
.container{
  height: 100%;
}
.img-box {
  position: relative;
  width: 80%;
  overflow: hidden;
  margin: auto;
  border: 1px solid blue;
  height: 60%;
  
}


.maskCanvas {
  /* display: none; */
  position: absolute;
  left: 0;
  bottom: 0;
  border:1px solid red;
}
.o, .x {
  width: 40rpx;
  height: 40rpx;
  position: absolute;
}

.o{
  bottom: -20px;
  right: -20px;
}
.x{
  top: -20px;
  left: -20px
}
.touchWrap {
  position: absolute;
}

.imgWrap{
  border: 1px solid transparent;

}
.touchActive{

  border: 1px solid black;
}

jasonspa

{
  "navigationBarTitleText": "照片測試",
  "usingComponents": {},
  "disableScroll": true
}

這是個demo,本身大概測過沒問題
由於頁面簡潔,若是要放在頁面上換樣式,可能會有問題
僅供參考,僅供參考!.net

相關文章
相關標籤/搜索