亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 課堂 > 小程序 > 正文

微信小程序canvas拖拽、截圖組件功能

2020-03-21 16:10:54
字體:
來源:轉載
供稿:網友

先看下微信小程序canvas拖拽功能

組件地址

github.com/jasondu/wx-… readme近期補上

實現效果

微信小程序,canvas,拖拽,截圖組件

如何實現

  1. 使用canvas
  2. 使用movable-view標簽

由于movable-view無法實現旋轉,所以選擇使用canvas

需要解決的問題

  • 如何將多個元素渲染到canvas上
  • 如何知道手指在元素上、如果多個元素重疊如何知道哪個元素在最上層
  • 如何實現拖拽元素
  • 如何縮放、旋轉、刪除元素

看起來挺簡單的嘛,就把上面這幾個問題解決了,就可以實現功能了;接下來我們一一解決。

如何將多個元素渲染到canvas上

定義一個DragGraph類,傳入元素的各種屬性(坐標、尺寸…)實例化后推入一個 渲染數組 里,然后再循環這個數組調用實例中的渲染方法,這樣就可以把多個元素渲染到canvas上了。

如何知道手指在元素上、如果多個元素重疊如何知道哪個元素在最上層

在DragGraph類中定義了判斷點擊位置的方法,我們在canvas上綁定touchstart事件,將手指的坐標傳入上面的方法,我們就可以知道手指是點擊到元素本身,還是刪除圖標或者變換大小的圖標上了,這個方法具體怎么判斷后面會講解。

通過循環 渲染數組 判斷是非點擊到哪個元素到,如果點擊中了多個元素,也就是多個元素重疊,那第一個元素就是最上層的元素啦。

###如何實現拖拽元素

通過上面我們可以判斷手指是否在元素上,當touchstart事件觸發時我們記錄當前的手指坐標,當touchmove事件觸發時,我們也知道這時的坐標,兩個坐標取差值,就可以得出元素位移的距離啦,修改這個元素實例的x和y,再重新循環渲染 渲染數組 就可以實現拖拽的功能。

如何縮放、旋轉、刪除元素

這一步相對比較難一點,我會通過示意圖跟大家講解。

微信小程序,canvas,拖拽,截圖組件

我們先講縮放和旋轉

通過touchstart和touchmove我們可以獲得旋轉前的旋轉后的坐標,圖中的線A為元素的中點和旋轉前點的連線;線B為元素中點和旋轉后點的連線;我們只需要求A和B兩條線的夾角就可以知道元素旋轉的角度??s放尺寸為A和B兩條線長度之差。

計算旋轉角度的代碼如下:

const centerX = (this.x + this.w) / 2; // 中點坐標const centerY = (this.y + this.h) / 2; // 中點坐標const diffXBefore = px - centerX;  // 旋轉前坐標const diffYBefore = py - centerY;  // 旋轉前坐標const diffXAfter = x - centerX;   // 旋轉后坐標const diffYAfter = y - centerY;   // 旋轉后坐標const angleBefore = Math.atan2(diffYBefore, diffXBefore) / Math.PI * 180;const angleAfter = Math.atan2(diffYAfter, diffXAfter) / Math.PI * 180;// 旋轉的角度this.rotate = currentGraph.rotate + angleAfter - angleBefore;

計算縮放尺寸的代碼如下:

// 放大 或 縮小this.x = currentGraph.x - (x - px);this.y = currentGraph.y - (x - px);

下面介紹下小程序canvas截圖組件

最近做一個小程序的過程中,需要用到截圖功能,網上搜了一下,發現沒有符合要求的,就自己搞了個組件,方便復用。

目前功能很簡單,傳入寬高和圖片路徑即可,寬高是為了計算截圖的比例,只支持縮放和移動。

實現思路是:

1.模擬一個截取框;

2.移動圖片位置,縮放圖片;

3.獲取圖片在其中的位置(left,top,width,height);

4.使用canvas繪制圖片,然后截取就ok了。

其中第二步的縮放圖片比較麻煩,縮放中心點以及平滑縮放

以下是我的實現方式

wxml:

<!--component/picPro/picPro.wxml--><scroll-view class='body' hidden="{{hidden}}"><view class='flex-column flex-between full-height full-width' bindtouchstart="touchstart" bindtouchmove="touchmove" bindtouchend="touchend"><view class='bg_dark out_item'></view><view class='flex-row main flex-between' style='height:{{(windowWidth - margin.left - margin.right)/ratio + "px"}}'><view class='bg_dark main_item full-height' style='width:{{margin.left + "px"}}'></view><view class='inner relative full-width' id='showArea'><image class='absolute img' src='{{src}}' style="width:{{img.width}}px;height:{{img.height}}px;left:{{img.left}}px;top:{{img.top}}px;"></image><canvas canvas-id='imgCanvas' class='absolute img_canvas full-height full-width' /><view class='absolute inner_item left_top'></view><view class='absolute inner_item right_top'></view><view class='absolute inner_item right_bottom'></view><view class='absolute inner_item left_bottom'></view></view><view class='bg_dark main_item full-height' style='width:{{margin.right + "px"}}'></view></view><view class='bg_dark out_item flex-column flex-end'> <view class='flex-around text_white text_bg'><view catchtap='outputImg' data-type='1'><text>重新上傳</text></view><view catchtap='getImg'><text>選擇圖片</text></view></view> </view><!-- --><view class='absolute full-width full-height bg_black'></view></view></scroll-view>

wxss:(其中引入了一個公共樣式,關于flex布局的,看樣式名也能猜到)

/* component/picPro/picPro.wxss */@import '../../resource/style/flex.wxss';.body{ position: fixed; top: 0; right: 0; bottom: 0; left: 0;}.text_white{ color: white;}.main{}.out_item{ width: 100%; height: 100%; flex: 1;}.bg_dark{ background-color: rgba(0, 0, 0, 0.85)}.main_item{ width: 15px;}.inner{ outline: 3rpx solid white; background-color: rgba(0, 0, 0, 0.12); box-shadow: 0 0 4px rgba(0, 0, 0, 0.5) inset;}.inner_item{ width: 8px; height: 8px;}.inner_item.left_top{ border-left: 3px solid white; border-top: 3px solid white; left: -3px; top: -3px;}.inner_item.right_top{ border-right: 3px solid white; border-top: 3px solid white; right: -3px; top: -3px;}.inner_item.right_bottom{ border-right: 3px solid white; border-bottom: 3px solid white; right: -3px; bottom: -3px;}.inner_item.left_bottom{ border-left: 3px solid white; border-bottom: 3px solid white; left: -3px; bottom: -3px;}.img{ z-index: -1;}.bg_black{ background-color:black; z-index: -2; }.text_bg{ padding-bottom: 2em; font-size: 0.9em;}.img_canvas{ opacity: 0.5;}.newImg{ z-index: 2}

js:

// component/picPro/picPro.jsconst state = { // 可用區域body window: { width: 0, height: 0 }, // 原始圖片信息 originImg: { width: 0, height: 0 }, // 第一次圖片縮放信息 firstScaleImg: { width: 0, height: 0 }, // 截取區域信息 interArea: { width: 0, height: 0 }, // 單手觸摸位置 touchLast: { x: 0, y: 0 }, // 滑動距離 touchMove: { x: 0, y: 0 }, // 滑動離開時圖片狀態 moveImgState: {  width: 0,  height: 0,  top: 0,  left: 0, }, // 雙手觸摸位置 touchList: [{ x: 0, y: 0 }, { x: 0, y: 0 }], // 圖片縮放比例 scale: 1,}Component({ /**  * 組件的屬性列表  */ properties: {  //寬(非實際值)  width: {   type: Number,   value: 600  },  //高  height: {   type: Number,   value: 300  },  //圖片路徑  src: {   type: String,   value: ""  },  //顯示隱藏  hidden: {   type: Boolean,   value: false  },  //截取框的信息  margin: {   type: Object,   value: {    left: 15,    right: 15,    top: 200,    bottom: 200,   }  } }, ready() {  this.initialize();  // const canvas = wx.createCanvasContext('imgCanvas', this);  // canvas.draw(false, () => { console.log('ccc') }, this); }, /**  * 組件的初始數據  */ data: {  touchRange: 8,  img: {   width: 0,   height: 0,   top: 0,   left: 0,  },  canvas: {},  ratio: 0,  originImg: {   width: 0,   height: 0  } }, /**  * 組件的方法列表  */ methods: {  touchstart(e) {   // console.log("touchstart", e);  },  touchmove(e) {   if (e.touches.length === 1) { this.singleSlip(e.touches[0]) } else {    this.doubleSlip(e.touches)   }  },  touchend(e) {   // console.log("touchend", e);   const x = 0, y = 0;   state.touchLast = { x, y };   state.touchMove = { x, y };   state.touchList = [{ x, y }, { x, y }];   state.moveImgState = this.data.img;   // console.log(this.data.img);  },  // 單手滑動操作  singleSlip(e) {   const { clientX: x, clientY: y } = e;   const that = this;   if (state.touchLast.x && state.touchLast.y) {    state.touchMove = { x: x - state.touchLast.x, y: y - state.touchLast.y };    state.touchLast = { x, y };    const move = (_x = false, _y = false) => {     const bottom = that.data.img.height + that.data.img.top;     const right = that.data.img.width + that.data.img.left;     const h = state.interArea.height;     const w = state.interArea.width;     const param = {};     if (_x) {      if (right > w && that.data.img.left < 0) {       param.left = that.data.img.left + state.touchMove.x * 0.1      } else if (right <= w && state.touchMove.x > 0) {       param.left = that.data.img.left + state.touchMove.x * 0.1      } else if (that.data.img.left >= 0 && state.touchMove.x < 0) {       param.left = that.data.img.left + state.touchMove.x * 0.1      }     };     if (_y) {      if (bottom > h && that.data.img.top < 0) {       param.top = that.data.img.top + state.touchMove.y * 0.1      } else if (bottom <= h && state.touchMove.y > 0) {       param.top = that.data.img.top + state.touchMove.y * 0.1      } else if (that.data.img.top >= 0 && state.touchMove.y < 0) {       param.top = that.data.img.top + state.touchMove.y * 0.1      }     };     // console.log(param);     that.setImgPos(param)    };    if (state.scale == 1) {     if (that.data.img.width == state.interArea.width) {      move(false, true)     } else {      move(true, false)     }    } else {     move(true, true)    }   } else {    state.touchLast = { x, y }   }  },  // 雙手縮放操作  doubleSlip(e) {   const that = this;   const { clientX: x0, clientY: y0 } = e[0];   const { clientX: x1, clientY: y1 } = e[1];   if (state.touchList[0].x && state.touchList[0].y) {    let changeScale = (Math.sqrt((x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0)) - Math.sqrt((state.touchList[1].x - state.touchList[0].x) * (state.touchList[1].x - state.touchList[0].x) + (state.touchList[1].y - state.touchList[0].y) * (state.touchList[1].y - state.touchList[0].y))) * 0.0005;    changeScale = changeScale >= 1.5 ? 1.5 : (changeScale <= -1 ? -1 : changeScale);    state.scale = that.data.img.width / state.firstScaleImg.width < 1 ? 1 : (state.scale > 2.5 ? 2.5 : 1 + changeScale);    let width = state.firstScaleImg.width * (state.scale - 1) + state.moveImgState.width;    width = width < state.firstScaleImg.width ? state.firstScaleImg.width : width;    let height = state.firstScaleImg.height * (state.scale - 1) + state.moveImgState.height;    height = height < state.firstScaleImg.height ? state.firstScaleImg.height : height;    let left = width * (1 - state.scale) / 4 + state.moveImgState.left;    left = left * (-1) > width - state.interArea.width ? state.interArea.width - width: left > 0 ? 0 : left;    let top = height * (1 - state.scale) / 4 + state.moveImgState.top;    top = top * (-1) > height - state.interArea.height ?state.interArea.height - height : top > 0 ? 0 : top;    const setImgObj = { width, height, left, top };    that.setImgPos(setImgObj)   } else {    state.touchList = [{ x: x0, y: y0 }, { x: x1, y: y1 }]   }  },  // 獲取可用區域寬高  getScreenInfo() {   const that = this;   return new Promise((resolve, reject) => {    wx.getSystemInfo({     success: function (res) {      const { windowHeight, windowWidth } = res;      state.window = { windowHeight, windowWidth };      that.setData({ windowHeight, windowWidth })      // console.log(state.window);      resolve(res);     },    })   })  },  setShowArea() {   const that = this;   const w = state.window.windowWidth - that.data.margin.left - that.data.margin.right;   const h = (that.data.height / that.data.width) * w;  },  outputImg() {   this.setData({    hidden: true,   })  },  getImgInfo(path) {   return new Promise((resolve, reject) => {    wx.getImageInfo({     src: path,     success(res) {      console.log(res);      resolve(res);     },     fail(err) {      reject(err)     }    })   })  },  // 設置圖片  setImgPos({ width, height, top, left }) {   width = width || this.data.img.width;   height = height || this.data.img.height;   top = top || this.data.img.top;   left = left || this.data.img.left   this.setData({    img: { width, height, top, left }   })  },  // 初始化圖片位置大小  initialize() {   const that = this;   const ratio = that.data.width / that.data.height;   this.getScreenInfo().then(res => {    console.log(res);    state.interArea = { width: res.windowWidth - that.data.margin.left - that.data.margin.right + 2, height: (res.windowWidth - that.data.margin.left - that.data.margin.right) / ratio };    console.log("interArea", state.interArea)    that.getImgInfo(that.data.src).then(imgInfo => {     const { width, height } = imgInfo;     const imgRatio = width / height;     state.originImg = { width, height };     that.setData({      ratio: ratio     });     if (imgRatio > ratio) {      that.setImgPos({       height: state.interArea.height,       width: state.interArea.height * imgRatio      })     } else {      that.setImgPos({       height: state.interArea.width / imgRatio,       width: state.interArea.width,      })     };     state.firstScaleImg = { width: that.data.img.width, height: that.data.img.height }    });   });  },  // 截圖  getImg(){   const that = this;   // console.log('dudu', that.data.img);   const canvas = wx.createCanvasContext('imgCanvas', this);   const {width,height,left,top} = that.data.img;   const saveImg = ()=>{    console.log('開始截取圖片');    wx.canvasToTempFilePath({     canvasId:"imgCanvas",     success(res){      // console.log(res);      that.setData({       hidden:true,       // src:""      });      that.triggerEvent("putimg", { imgUrl: res.tempFilePath},{});     },     fail(err){      console.log(err)     }    },that)   };   canvas.drawImage(that.data.src, left, top, width, height);   canvas.draw(false, () => { saveImg() }, that)  } }})

引用的時候除了寬高路徑以外,需要wx:if;如果不卸載組件,會出現只能截一次的bug

因為小程序里面沒有類似vue中catch的觀測數據變化的東西,也不想為了個組件專門去搞一個,就用這種方式代替了,嘻嘻,好敷衍。。

總結

以上所述是小編給大家介紹的微信小程序canvas拖拽、截圖組件功能,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對VEVB武林網網站的支持!


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
亚洲高清久久久久久| 亚洲黄页网在线观看| 中文一区二区视频| 久久精品免费播放| 欧美超级免费视 在线| 久久久久国产精品一区| 久久久久免费精品国产| 久久久久免费精品国产| 国产欧美最新羞羞视频在线观看| 中文字幕综合一区| 中文精品99久久国产香蕉| 日本sm极度另类视频| 欧美亚洲第一区| 国产成人免费91av在线| 国产日韩中文字幕在线| 成人乱人伦精品视频在线观看| 国产97在线观看| 57pao成人永久免费视频| 国模叶桐国产精品一区| 日韩中文字幕不卡视频| 日韩有码片在线观看| 2018中文字幕一区二区三区| 日韩av手机在线看| 久久精品国产视频| 韩国精品美女www爽爽爽视频| 91精品91久久久久久| 亚洲少妇中文在线| 欧美性jizz18性欧美| 中文字幕精品网| 国产欧美一区二区三区四区| 91色在线视频| 97在线观看视频| 亚洲在线免费视频| 97精品视频在线播放| 在线观看久久久久久| 日韩美女福利视频| 欧美综合在线观看| 国产精品久久不能| 亚洲国产精品国自产拍av秋霞| 91精品国产自产91精品| 欧美多人爱爱视频网站| 91美女高潮出水| 亚洲精品一区二区三区婷婷月| 久久777国产线看观看精品| 久久久久久国产精品久久| 91免费版网站入口| 中文字幕亚洲欧美| 亚洲精品电影网在线观看| 亚洲精品aⅴ中文字幕乱码| 中文字幕欧美国内| 一本色道久久综合狠狠躁篇的优点| 久久久精品久久久久| 岛国av一区二区| 日韩乱码在线视频| 91精品视频在线看| 久久精品亚洲一区| 国产在线播放不卡| 亚洲精品一区二区久| 欧美激情亚洲自拍| 国产99久久精品一区二区永久免费| 中文字幕亚洲一区二区三区| 欧美极品美女视频网站在线观看免费| 国产精品白丝av嫩草影院| 精品香蕉一区二区三区| 亚洲成人网久久久| 国产91精品久| 色噜噜狠狠色综合网图区| 色哟哟亚洲精品一区二区| 国产精品白丝av嫩草影院| 亚洲 日韩 国产第一| 日韩av在线免费观看| 精品亚洲一区二区三区在线观看| 大荫蒂欧美视频另类xxxx| 亚洲精品一区在线观看香蕉| 日韩av在线免费观看一区| 91精品久久久久久久久久入口| 国产精品va在线播放| 国产欧美日韩亚洲精品| 亚洲欧美激情一区| 综合国产在线观看| 日韩的一区二区| 中文在线资源观看视频网站免费不卡| 国产精品99久久久久久久久| 国产在线不卡精品| 毛片精品免费在线观看| 欧美小视频在线观看| 97视频在线观看成人| 久久久久久久久综合| 国产精品免费久久久久影院| 欧美极品在线播放| 欧美人交a欧美精品| 精品国产31久久久久久| 国产精品日韩在线一区| 亚洲成人av资源网| 日韩在线免费视频观看| 欧美在线视频一区| 久久精品国产清自在天天线| 欧美在线观看一区二区三区| 国产午夜精品全部视频在线播放| 97在线视频免费| 538国产精品一区二区在线| 欧美性一区二区三区| 色综合久久精品亚洲国产| 国产综合在线看| 国产91对白在线播放| 亚洲精品在线不卡| 国产精品第8页| 97精品在线视频| 亚洲一区av在线播放| 久久久久久久爱| 欧美黑人xxxⅹ高潮交| 色哟哟亚洲精品一区二区| 精品久久久久国产| 日韩精品中文字幕在线| 视频在线一区二区| 欧美成人免费网| 欧美日韩美女在线| 国产精品亚洲精品| 不卡毛片在线看| 国产成人一区二区| 精品欧美aⅴ在线网站| 8090理伦午夜在线电影| 亚洲xxxx视频| 伊人久久久久久久久久久| 91爱视频在线| 亚洲欧美中文字幕| 亚洲永久免费观看| 57pao国产精品一区| 欧洲精品久久久| 国产精品人人做人人爽| 57pao国产精品一区| 日韩av在线网址| 国产免费亚洲高清| 欧美色视频日本版| 久久成人亚洲精品| 91精品国产91久久久久久久久| 欧美日韩一二三四五区| 国产区精品视频| 视频一区视频二区国产精品| 亚洲欧洲中文天堂| 久久久电影免费观看完整版| 久久久91精品| 久久久在线视频| 精品成人69xx.xyz| 欧美精品18videos性欧| 中文字幕不卡av| 岛国av一区二区三区| 97国产在线视频| 中日韩美女免费视频网址在线观看| 欧美日韩在线免费观看| 国产欧美韩国高清| 久久精品夜夜夜夜夜久久| 国产精品你懂得| 欧美另类第一页| 91亚洲午夜在线| 日韩欧美aaa| 亚洲色图狂野欧美| 国产午夜精品理论片a级探花| 国产在线观看不卡| 在线观看日韩av| 成人av在线网址| 永久免费毛片在线播放不卡| 日韩欧美在线第一页| 97精品视频在线|