微信小程序 圖片裁剪工具,簡單易用git
項目需求github
在作微信小程序的時候有個圖片上傳以前裁剪的需求,找過一些github中的項目,都不太理想,主要是沒有辦法自定義寬高,因而本身研究了一下,作了一個簡單的圖片裁剪效果web
使用方法json
項目裏面直接放的該功能的文件,將這個文件夾複製到小程序項目中,在app.json中啓動該頁面便可看到效果canvas
微信小程序技術比較新,因此在網上找了好久都沒有找到理想的裁剪插件,正好這兩天不是很忙就本身查找資料作了,通過一天的努力終於把它作出來了,不容易啊,如今把它分享給大家(可爲你門節省一天開發時間哦):小程序
wxml:微信小程序
<!--圖片展現 --> <view bindtap='upEwm' data-which='1' > <view>第一個圖</view> <image style='width:200rpx;height:200rpx;background-color:red' src='{{headImg}}'></image> </view> <view bindtap='upEwm' data-which='2' > <view>第二個圖</view> <image style='width:200rpx;height:200rpx;background-color:red' src='{{ewmImg}}'></image> </view> <!--裁剪圖片浮層--> <view class='fixed-upimg' wx:if="{{imageFixed}}"> <view class="wx-content-info" > <!-- <view class="wx-content-info" wx:if="{{imageSrc}}"> --> <view wx:if="{{isShowImg}}" class="wx-corpper" style="width:{{cropperInitW}}rpx;height:{{cropperInitH}}rpx;background:#000"> <view bindtap='upLoad' class="wx-corpper-content" style="width:{{cropperW}}rpx;height:{{cropperH}}rpx;left:{{cropperL}}rpx;top:{{cropperT}}rpx"> <image src="{{imageSrc}}" style="width:{{cropperW}}rpx;height:{{cropperH}}rpx"></image> <view class="wx-corpper-crop-box" bindtouchstart="contentStartMove" bindtouchmove="contentMoveing" style="width:{{cutW}}rpx;height:{{cutH}}rpx;left:{{cutL}}rpx;top:{{cutT}}rpx"> <view class="wx-cropper-view-box"> <!-- <view class="wx-cropper-viewer"> <image src="{{imageSrc}}" style="width:{{cropperW}}rpx;height:{{cropperH}}rpx;left:{{cropperL - cutL}}rpx;top:{{cropperT - cutT}}rpx"></image> </view> --> <view class="wx-cropper-dashed-h"></view> <view class="wx-cropper-dashed-v"></view> <view class="wx-cropper-line-t" data-drag="top" catchtouchstart="dragStart" catchtouchmove="dragMove"></view> <view class="wx-cropper-line-r" data-drag="right" catchtouchstart="dragStart" catchtouchmove="dragMove"></view> <view class="wx-cropper-line-b" data-drag="bottom" catchtouchstart="dragStart" catchtouchmove="dragMove"></view> <view class="wx-cropper-line-l" data-drag="left" catchtouchstart="dragStart" catchtouchmove="dragMove"></view> <view class="wx-cropper-point point-t" data-drag="top" catchtouchstart="dragStart" catchtouchmove="dragMove"></view> <view class="wx-cropper-point point-tr" data-drag="topTight"></view> <view class="wx-cropper-point point-r" data-drag="right" catchtouchstart="dragStart" catchtouchmove="dragMove"></view> <view class="wx-cropper-point point-rb" data-drag="rightBottom" catchtouchstart="dragStart" catchtouchmove="dragMove"></view> <view class="wx-cropper-point point-b" data-drag="bottom" catchtouchstart="dragStart" catchtouchmove="dragMove"></view> <view class="wx-cropper-point point-bl" data-drag="bottomLeft"></view> <view class="wx-cropper-point point-l" data-drag="left" catchtouchstart="dragStart" catchtouchmove="dragMove"></view> <view class="wx-cropper-point point-lt" data-drag="leftTop"></view> </view> </view> </view> <!-- <view class="wx-cropper-drag-box"></view> --> </view> <canvas canvas-id="myCanvas" style="position:absolute;border: 1px solid red; width:{{imageW}}rpx;height:{{imageH}}rpx;top:-9999px;left:-9999px;"></canvas> <button type="primary" bindtap="getImageInfo" style="position:fixed;bottom:120rpx;width:90%;left:50%;transform:translate3d(-50%,0,0)"> 確認提交 </button> </view> </view>
// pages/wx-cropper/index.js // 手機的寬度 var windowWRPX = 750 // 拖動時候的 pageX var pageX = 0 // 拖動時候的 pageY var pageY = 0 var pixelRatio = wx.getSystemInfoSync().pixelRatio // 調整大小時候的 pageX var sizeConfPageX = 0 // 調整大小時候的 pageY var sizeConfPageY = 0 var initDragCutW = 0 var initDragCutL = 0 var initDragCutH = 0 var initDragCutT = 0 // 移動時 手勢位移與 實際元素位移的比 var dragScaleP = 2 Page({ /** * 頁面的初始數據 */ data: { imageNum:'', //上傳的圖片id headImg:'', //頭像上傳 ewmImg:'', //二維碼上傳 imageFixed:false, //裁剪浮層 // imageSrc: 'http://topmdrt-static.oss-cn-shenzhen.aliyuncs.com/images/testimg2.jpeg', imageSrc: '', //要裁剪的圖片 returnImage: '', isShowImg: false, // 初始化的寬高 cropperInitW: windowWRPX, cropperInitH: windowWRPX, // 動態的寬高 cropperW: windowWRPX, cropperH: windowWRPX, // 動態的left top值 cropperL: 0, cropperT: 0, // 圖片縮放值 scaleP: 0, imageW: 0, imageH: 0, // 裁剪框 寬高 cutW: 400, cutH: 400, cutL: 0, cutT: 0, }, /** * 生命週期函數--監聽頁面加載 */ onReady: function (options) { }, /** * 生命週期函數--監聽頁面初次渲染完成 */ onLoad: function () { var _this = this // wx.showLoading({ // title: '圖片加載中...', // }) wx.getImageInfo({ src: _this.data.imageSrc, success: function success(res) { var innerAspectRadio = res.width / res.height; console.log(innerAspectRadio) // 根據圖片的寬高顯示不一樣的效果 保證圖片能夠正常顯示 if (innerAspectRadio >= 1) { _this.setData({ cropperW: windowWRPX, cropperH: windowWRPX / innerAspectRadio, // 初始化left right cropperL: Math.ceil((windowWRPX - windowWRPX) / 2), cropperT: Math.ceil((windowWRPX - windowWRPX / innerAspectRadio) / 2), // 裁剪框 寬高 // cutW: windowWRPX - 200, // cutH: windowWRPX / innerAspectRadio - 200, cutL: Math.ceil((windowWRPX - windowWRPX + 340) / 2), cutT: Math.ceil((windowWRPX / innerAspectRadio - (windowWRPX / innerAspectRadio - 20)) / 2), // 圖片縮放值 scaleP: res.width * pixelRatio / windowWRPX, // 圖片原始寬度 rpx imageW: res.width * pixelRatio, imageH: res.height * pixelRatio }) } else { _this.setData({ cropperW: windowWRPX * innerAspectRadio, cropperH: windowWRPX, // 初始化left right cropperL: Math.ceil((windowWRPX - windowWRPX * innerAspectRadio) / 2), cropperT: Math.ceil((windowWRPX - windowWRPX) / 2), // 裁剪框的寬高 // cutW: windowWRPX * innerAspectRadio - 66, // cutH: 400, cutL: Math.ceil((windowWRPX * innerAspectRadio - (windowWRPX * innerAspectRadio - 20)) / 2), cutT: Math.ceil((windowWRPX - 340) / 2), // 圖片縮放值 scaleP: res.width * pixelRatio / windowWRPX, // 圖片原始寬度 rpx imageW: res.width * pixelRatio, imageH: res.height * pixelRatio }) } _this.setData({ isShowImg: true }) wx.hideLoading() } }) }, // 拖動時候觸發的touchStart事件 contentStartMove (e) { pageX = e.touches[0].pageX pageY = e.touches[0].pageY }, // 拖動時候觸發的touchMove事件 contentMoveing (e) { var _this = this // _this.data.cutL + (e.touches[0].pageX - pageX) // console.log(e.touches[0].pageX) // console.log(e.touches[0].pageX - pageX) var dragLengthX = (pageX - e.touches[0].pageX) * dragScaleP var dragLengthY = (pageY - e.touches[0].pageY) * dragScaleP var minX = Math.max(_this.data.cutL - (dragLengthX), 0) var minY = Math.max(_this.data.cutT - (dragLengthY), 0) var maxX = _this.data.cropperW - _this.data.cutW var maxY = _this.data.cropperH - _this.data.cutH this.setData({ cutL: Math.min(maxX, minX), cutT: Math.min(maxY, minY), }) console.log(`${maxX} ----- ${minX}`) pageX = e.touches[0].pageX pageY = e.touches[0].pageY }, // 獲取圖片 getImageInfo () { var _this = this console.log('shengcheng:'+_this.data.imageSrc) wx.showLoading({ title: '圖片生成中...', }) // wx.downloadFile({ // url:_this.data.imageSrc, //僅爲示例,並不是真實的資源 // success: function (res) { // 將圖片寫入畫布 const ctx = wx.createCanvasContext('myCanvas') // ctx.drawImage(res.tempFilePath) ctx.drawImage(_this.data.imageSrc) ctx.draw(true, () => { // 獲取畫布要裁剪的位置和寬度 均爲百分比 * 畫布中圖片的寬度 保證了在微信小程序中裁剪的圖片模糊 位置不對的問題 var canvasW = (_this.data.cutW / _this.data.cropperW) * (_this.data.imageW / pixelRatio) var canvasH = (_this.data.cutH / _this.data.cropperH) * (_this.data.imageH / pixelRatio) var canvasL = (_this.data.cutL / _this.data.cropperW) * (_this.data.imageW / pixelRatio) var canvasT = (_this.data.cutT / _this.data.cropperH) * (_this.data.imageH / pixelRatio) console.log(`canvasW:${canvasW} --- canvasH: ${canvasH} --- canvasL: ${canvasL} --- canvasT: ${canvasT} -------- _this.data.imageW: ${_this.data.imageW} ------- _this.data.imageH: ${_this.data.imageH} ---- pixelRatio ${pixelRatio}`) wx.canvasToTempFilePath({ x: canvasL, y: canvasT, width: canvasW, height: canvasH, destWidth: canvasW, destHeight: canvasH, canvasId: 'myCanvas', success: function (res) { wx.hideLoading() // 成功得到地址的地方 console.log('end:'+res.tempFilePath) // 判斷時上傳頭像仍是二維碼 _this.setData({ imageFixed: false, }) if (_this.data.imageNum == '1') { _this.setData({ headImg: res.tempFilePath }) } else if (_this.data.imageNum == '2') { _this.setData({ ewmImg: res.tempFilePath }) } } }) }) // } // }) }, // 設置大小的時候觸發的touchStart事件 dragStart(e) { var _this = this sizeConfPageX = e.touches[0].pageX sizeConfPageY = e.touches[0].pageY initDragCutW = _this.data.cutW initDragCutL = _this.data.cutL initDragCutT = _this.data.cutT initDragCutH = _this.data.cutH }, // 設置大小的時候觸發的touchMove事件 dragMove (e) { var _this = this var dragType = e.target.dataset.drag switch (dragType) { case 'right': var dragLength = (sizeConfPageX - e.touches[0].pageX) * dragScaleP if (initDragCutW >= dragLength) { // 若是 移動小於0 說明是在往下啦 放大裁剪的高度 這樣一來 圖片的高度 最大 等於 圖片的top值加 當前圖片的高度 不然就說明超出界限 if (dragLength < 0 && _this.data.cropperW > initDragCutL + _this.data.cutW) { this.setData({ cutW: initDragCutW - dragLength }) } // 若是是移動 大於0 說明在縮小 只須要縮小的距離小於本來裁剪的高度就ok if (dragLength > 0) { this.setData({ cutW: initDragCutW - dragLength }) } else { return } } else { return } break; case 'left': var dragLength = (dragLength = sizeConfPageX - e.touches[0].pageX) * dragScaleP console.log(dragLength) if (initDragCutW >= dragLength && initDragCutL > dragLength) { if (dragLength < 0 && Math.abs(dragLength) >= initDragCutW) return this.setData({ cutL: initDragCutL - dragLength, cutW: initDragCutW + dragLength }) } else { return; } break; case 'top': var dragLength = (sizeConfPageY - e.touches[0].pageY) * dragScaleP if (initDragCutH >= dragLength && initDragCutT > dragLength) { if (dragLength < 0 && Math.abs(dragLength) >= initDragCutH) return this.setData({ cutT: initDragCutT - dragLength, cutH: initDragCutH + dragLength }) } else { return; } break; case 'bottom': var dragLength = (sizeConfPageY - e.touches[0].pageY) * dragScaleP // console.log(_this.data.cropperH > _this.data.cutT + _this.data.cutH) console.log(dragLength) console.log(initDragCutH >= dragLength) console.log(_this.data.cropperH > initDragCutT + _this.data.cutH) // 必須是 dragLength 向上縮小的時候必須小於本來的高度 if (initDragCutH >= dragLength) { // 若是 移動小於0 說明是在往下啦 放大裁剪的高度 這樣一來 圖片的高度 最大 等於 圖片的top值加 當前圖片的高度 不然就說明超出界限 if (dragLength < 0 && _this.data.cropperH > initDragCutT + _this.data.cutH) { this.setData({ cutH: initDragCutH - dragLength }) } // 若是是移動 大於0 說明在縮小 只須要縮小的距離小於本來裁剪的高度就ok if (dragLength > 0) { this.setData({ cutH: initDragCutH - dragLength }) } else{ return } } else { return } break; case 'rightBottom': var dragLengthX = (sizeConfPageX - e.touches[0].pageX) * dragScaleP var dragLengthY = (sizeConfPageY - e.touches[0].pageY) * dragScaleP if (initDragCutH >= dragLengthY && initDragCutW >= dragLengthX) { // bottom 方向的變化 if ((dragLengthY < 0 && _this.data.cropperH > initDragCutT + _this.data.cutH) || (dragLengthY > 0)) { this.setData({ cutH: initDragCutH - dragLengthY }) } // right 方向的變化 if ((dragLengthX < 0 && _this.data.cropperW > initDragCutL + _this.data.cutW) || (dragLengthX > 0)) { this.setData({ cutW: initDragCutW - dragLengthX }) } else { return } } else { return } break; default: break; } }, // 圖片上傳 upLoad: function () { }, upEwm: function(e){ var _this = this wx.chooseImage({ count: 1, // 默認9 sizeType: ['original', 'compressed'], // 能夠指定是原圖仍是壓縮圖,默認兩者都有 sourceType: ['album', 'camera'], // 能夠指定來源是相冊仍是相機,默認兩者都有 success: function (res) { // 返回選定照片的本地文件路徑列表,tempFilePath能夠做爲img標籤的src屬性顯示圖片 var tempFilePaths = res.tempFilePaths; // var mode = parseFloat(e.currentTarget.dataset.current); console.log('shangchuan:' + tempFilePaths) console.log(e.currentTarget.dataset.which); _this.setData({ imageFixed: true, imageSrc: tempFilePaths.join(), imageNum: e.currentTarget.dataset.which }) // start wx.getImageInfo({ src: _this.data.imageSrc, success: function success(res) { var innerAspectRadio = res.width / res.height; console.log('bili'+innerAspectRadio) // 根據圖片的寬高顯示不一樣的效果 保證圖片能夠正常顯示 if (innerAspectRadio == '1') { console.log('zhengfangxingtu') _this.setData({ imageFixed: false, }) // 判斷時上傳頭像仍是二維碼 if (_this.data.imageNum == '1') { _this.setData({ headImg: tempFilePaths.join() }) } else if (_this.data.imageNum == '2') { _this.setData({ ewmImg: tempFilePaths.join() }) } } else if (innerAspectRadio > 1) { _this.setData({ cropperW: windowWRPX, cropperH: windowWRPX / innerAspectRadio, // 初始化left right cropperL: Math.ceil((windowWRPX - windowWRPX) / 2), cropperT: Math.ceil((windowWRPX - windowWRPX / innerAspectRadio) / 2), // 裁剪框 寬高 // cutW: windowWRPX - 200, // cutH: windowWRPX / innerAspectRadio - 200, cutL: Math.ceil((windowWRPX - windowWRPX + 340) / 2), cutT: Math.ceil((windowWRPX / innerAspectRadio - (windowWRPX / innerAspectRadio - 20)) / 2), // 圖片縮放值 scaleP: res.width * pixelRatio / windowWRPX, // 圖片原始寬度 rpx imageW: res.width * pixelRatio, imageH: res.height * pixelRatio }) } else { _this.setData({ cropperW: windowWRPX * innerAspectRadio, cropperH: windowWRPX, // 初始化left right cropperL: Math.ceil((windowWRPX - windowWRPX * innerAspectRadio) / 2), cropperT: Math.ceil((windowWRPX - windowWRPX) / 2), // 裁剪框的寬高 // cutW: windowWRPX * innerAspectRadio - 66, // cutH: 400, cutL: Math.ceil((windowWRPX * innerAspectRadio - (windowWRPX * innerAspectRadio - 20)) / 2), cutT: Math.ceil((windowWRPX - 340) / 2), // 圖片縮放值 scaleP: res.width * pixelRatio / windowWRPX, // 圖片原始寬度 rpx imageW: res.width * pixelRatio, imageH: res.height * pixelRatio }) } _this.setData({ isShowImg: true }) wx.hideLoading() } }) // end } }) }, /** * 生命週期函數--監聽頁面顯示 */ onShow: function () { }, /** * 生命週期函數--監聽頁面隱藏 */ onHide: function () { }, /** * 生命週期函數--監聽頁面卸載 */ onUnload: function () { }, /** * 頁面相關事件處理函數--監聽用戶下拉動做 */ onPullDownRefresh: function () { }, /** * 頁面上拉觸底事件的處理函數 */ onReachBottom: function () { }, /** * 用戶點擊右上角分享 */ onShareAppMessage: function () { } })
/* pages/wx-cropper/index.wxss */ .fixed-upimg{ position: fixed; top: 0; left: 0; right: 0; bottom: 0; background-color: #fff; } .wx-content-info{ position: fixed; top: 130rpx; left: 0; right: 0; bottom: 0; } .wx-corpper{ position: relative; overflow: hidden; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; -webkit-tap-highlight-color: transparent; -webkit-touch-callout: none; box-sizing: border-box; } .wx-corpper-content{ position: absolute; top: 0; right: 0; bottom: 0; left: 0; } .wx-corpper-content image { display: block; width: 100%; min-width: 0 !important; max-width: none !important; height: 100%; min-height: 0 !important; max-height: none !important; image-orientation: 0deg !important; margin: 0 auto; } /* 移動圖片效果 */ .wx-cropper-drag-box{ position: absolute; top: 0; right: 0; bottom: 0; left: 0; cursor: move; background: rgba(0,0,0,0.6); z-index: 1; } /* 內部的信息 */ .wx-corpper-crop-box{ position: absolute; width: 500rpx; height: 500rpx; background: rgba(255,255,255,0.3); z-index: 2; } .wx-corpper-crop-box .wx-cropper-view-box { position: relative; display: block; width: 100%; height: 100%; overflow: visible; outline: 1px solid #69f; outline-color: rgba(102, 153, 255, .75) } /* 橫向虛線 */ .wx-cropper-dashed-h{ position: absolute; top: 33.33333333%; left: 0; width: 100%; height: 33.33333333%; border-top: 1px dashed rgba(255,255,255,0.5); border-bottom: 1px dashed rgba(255,255,255,0.5); } /* 縱向虛線 */ .wx-cropper-dashed-v{ position: absolute; left: 33.33333333%; top: 0; width: 33.33333333%; height: 100%; border-left: 1px dashed rgba(255,255,255,0.5); border-right: 1px dashed rgba(255,255,255,0.5); } /* 四個方向的線 爲了以後的拖動事件*/ .wx-cropper-line-t{ position: absolute; display: block; width: 100%; background-color: #69f; top: 0; left: 0; height: 1px; opacity: 0.1; cursor: n-resize; } .wx-cropper-line-t::before{ content: ''; position: absolute; top: 50%; right: 0rpx; width: 100%; -webkit-transform: translate3d(0,-50%,0); transform: translate3d(0,-50%,0); bottom: 0; height: 41rpx; background: transparent; z-index: 11; } .wx-cropper-line-r{ position: absolute; display: block; background-color: #69f; top: 0; right: 0px; width: 1px; opacity: 0.1; height: 100%; cursor: e-resize; } .wx-cropper-line-r::before{ content: ''; position: absolute; top: 0; left: 50%; width: 41rpx; -webkit-transform: translate3d(-50%,0,0); transform: translate3d(-50%,0,0); bottom: 0; height: 100%; background: transparent; z-index: 11; } .wx-cropper-line-b{ position: absolute; display: block; width: 100%; background-color: #69f; bottom: 0; left: 0; height: 1px; opacity: 0.1; cursor: s-resize; } .wx-cropper-line-b::before{ content: ''; position: absolute; top: 50%; right: 0rpx; width: 100%; -webkit-transform: translate3d(0,-50%,0); transform: translate3d(0,-50%,0); bottom: 0; height: 41rpx; background: transparent; z-index: 11; } .wx-cropper-line-l{ position: absolute; display: block; background-color: #69f; top: 0; left: 0; width: 1px; opacity: 0.1; height: 100%; cursor: w-resize; } .wx-cropper-line-l::before{ content: ''; position: absolute; top: 0; left: 50%; width: 41rpx; -webkit-transform: translate3d(-50%,0,0); transform: translate3d(-50%,0,0); bottom: 0; height: 100%; background: transparent; z-index: 11; } .wx-cropper-point{ width: 5px; height: 5px; background-color: #69f; opacity: .75; position: absolute; z-index: 3; } .point-t{ top: -3px; left: 50%; margin-left: -3px; cursor: n-resize; } .point-tr{ top: -3px; left: 100%; margin-left: -3px; cursor: n-resize; } .point-r{ top: 50%; left:100%; margin-left: -3px; margin-top: -3px; cursor: n-resize; } .point-rb{ left: 100%; top: 100%; -webkit-transform: translate3d(-50%,-50%,0); transform: translate3d(-50%,-50%,0); cursor: n-resize; width: 24rpx; height: 24rpx; background-color: #69f; position: absolute; z-index: 1112; opacity: 1; } .point-b{ left:50%; top: 100%; margin-left: -3px; margin-top: -3px; cursor: n-resize; } .point-bl{ left:0%; top: 100%; margin-left: -3px; margin-top: -3px; cursor: n-resize; } .point-l{ left:0%; top: 50%; margin-left: -3px; margin-top: -3px; cursor: n-resize; } .point-lt{ left:0%; top: 0%; margin-left: -3px; margin-top: -3px; cursor: n-resize; } /* 裁剪框預覽內容 */ .wx-cropper-viewer{ position: relative; width: 100%; height: 100%; overflow: hidden; } .wx-cropper-viewer image{ position: absolute; z-index: 2; }