赞
踩
先看效果
说下实现思路,在用户点击头像的时候 弹出选项 选择相册打开,选择头像后跳转到 头像裁剪页面,裁剪完成点击上传图像 上传到服务器上,返回个人主页并刷新本地个人图像
大概实现方式
1.点击用户头像 调用uni api打开相册选择照片,并返回临时路径,把这个临时路径传给 图片裁剪界面。
- tapUpdateImg() {
- const that = this;
- uni.chooseImage({//打开手机本地相册
- count: 1,//只能选取一张照片
- sizeType: ['original'], //可以指定是原图还是压缩图,默认二者都有
- success: function(res) {
- uni.$once('updateHeadimg', that.refreshData);//监听裁剪界面返回事件
- uni.navigateTo({
- url: "../userInfo/updateHeadimg?imgUrl=" + res.tempFilePaths[0]
- })
- }
- })
- }
2.裁剪界面(通用代码自己需要修改上传业务逻辑) 代码很多耐心看,可以改成自己想要的 这个也是我找的模板
- <template>
- <view class="container">
- <view class="page-body uni-content-info">
- <view class='cropper-content'>
- <view v-if="isShowImg" class="uni-corpper" :style="'width:'+cropperInitW+'px;height:'+cropperInitH+'px;background:#000'">
- <view class="uni-corpper-content" :style="'width:'+cropperW+'px;height:'+cropperH+'px;left:'+cropperL+'px;top:'+cropperT+'px'">
- <image :src="imageSrc" :style="'width:'+cropperW+'px;height:'+cropperH+'px'"></image>
- <view class="uni-corpper-crop-box" @touchstart.stop="contentStartMove" @touchmove.stop="contentMoveing"
- @touchend.stop="contentTouchEnd" :style="'left:'+cutL+'px;top:'+cutT+'px;right:'+cutR+'px;bottom:'+cutB+'px'">
- <view class="uni-cropper-view-box">
- <view class="uni-cropper-dashed-h"></view>
- <view class="uni-cropper-dashed-v"></view>
- <view class="uni-cropper-line-t" data-drag="top" @touchstart.stop="dragStart" @touchmove.stop="dragMove"></view>
- <view class="uni-cropper-line-r" data-drag="right" @touchstart.stop="dragStart" @touchmove.stop="dragMove"></view>
- <view class="uni-cropper-line-b" data-drag="bottom" @touchstart.stop="dragStart" @touchmove.stop="dragMove"></view>
- <view class="uni-cropper-line-l" data-drag="left" @touchstart.stop="dragStart" @touchmove.stop="dragMove"></view>
- <view class="uni-cropper-point point-t" data-drag="top" @touchstart.stop="dragStart" @touchmove.stop="dragMove"></view>
- <view class="uni-cropper-point point-tr" data-drag="topTight"></view>
- <view class="uni-cropper-point point-r" data-drag="right" @touchstart.stop="dragStart" @touchmove.stop="dragMove"></view>
- <view class="uni-cropper-point point-rb" data-drag="rightBottom" @touchstart.stop="dragStart" @touchmove.stop="dragMove"></view>
- <view class="uni-cropper-point point-b" data-drag="bottom" @touchstart.stop="dragStart" @touchmove.stop="dragMove"
- @touchend.stop="dragEnd"></view>
- <view class="uni-cropper-point point-bl" data-drag="bottomLeft"></view>
- <view class="uni-cropper-point point-l" data-drag="left" @touchstart.stop="dragStart" @touchmove.stop="dragMove"></view>
- <view class="uni-cropper-point point-lt" data-drag="leftTop"></view>
- </view>
- </view>
- </view>
- </view>
- </view>
- <view class='cropper-config'>
- <!-- <button type="primary reverse" @click="getImage" style='margin-top: 30upx;'> 选择图片 </button> -->
- <button type="warn" @click="getImageInfo" style='margin-top: 30upx;'> 上传图像 </button>
- </view>
- <canvas canvas-id="myCanvas" :style="'position:absolute;border: 1px solid red; width:'+imageW+'px;height:'+imageH+'px;top:-9999px;left:-9999px;'"></canvas>
- </view>
- <!-- <page-foot :name="name"></page-foot> -->
- </view>
- </template>
-
- <script>
- import indexApi from '../../common/api/indexApi.js';
- let sysInfo = uni.getSystemInfoSync();
- let SCREEN_WIDTH = sysInfo.screenWidth
- let PAGE_X, // 手按下的x位置
- PAGE_Y, // 手按下y的位置
- PR = sysInfo.pixelRatio, // dpi
- T_PAGE_X, // 手移动的时候x的位置
- T_PAGE_Y, // 手移动的时候Y的位置
- CUT_L, // 初始化拖拽元素的left值
- CUT_T, // 初始化拖拽元素的top值
- CUT_R, // 初始化拖拽元素的
- CUT_B, // 初始化拖拽元素的
- CUT_W, // 初始化拖拽元素的宽度
- CUT_H, // 初始化拖拽元素的高度
- IMG_RATIO, // 图片比例
- IMG_REAL_W, // 图片实际的宽度
- IMG_REAL_H, // 图片实际的高度
- DRAFG_MOVE_RATIO = 1, //移动时候的比例,
- INIT_DRAG_POSITION = 100, // 初始化屏幕宽度和裁剪区域的宽度之差,用于设置初始化裁剪的宽度
- DRAW_IMAGE_W = sysInfo.screenWidth // 设置生成的图片宽度
-
- export default {
- /**
- * 页面的初始数据
- */
- data() {
- return {
- name: '杨大宝',
- imageSrc: '',
- isShowImg: false,
- // 初始化的宽高
- cropperInitW: SCREEN_WIDTH,
- cropperInitH: SCREEN_WIDTH,
- // 动态的宽高
- cropperW: SCREEN_WIDTH,
- cropperH: SCREEN_WIDTH,
- // 动态的left top值
- cropperL: 0,
- cropperT: 0,
-
- transL: 0,
- transT: 0,
-
- // 图片缩放值
- scaleP: 0,
- imageW: 0,
- imageH: 0,
-
- // 裁剪框 宽高
- cutL: 0,
- cutT: 0,
- cutB: SCREEN_WIDTH,
- cutR: '100%',
- qualityWidth: DRAW_IMAGE_W,
- innerAspectRadio: DRAFG_MOVE_RATIO
- }
- },
- /**
- * 生命周期函数--监听页面加载
- */
- onLoad: function(opt) {
- this.imageSrc = opt.imgUrl;
- },
-
- /**
- * 生命周期函数--监听页面初次渲染完成
- */
- mounted: function() {
- //this.getImage();
- this.loadImage();
-
- },
- methods: {
- setData: function(obj) {
- let that = this;
- Object.keys(obj).forEach(function(key) {
- that.$set(that.$data, key, obj[key])
- });
- },
- // getImage: function() {
- // var _this = this
- // uni.chooseImage({
- // success: function(res) {
- // _this.setData({
- // imageSrc: res.tempFilePaths[0],
- // })
- // _this.loadImage();
- // },
- // })
- // },
- loadImage: function() {
- var _this = this
- uni.showLoading({
- title: '图片加载中...',
- })
-
- uni.getImageInfo({
- src: _this.imageSrc,
- success: function success(res) {
- console.log(res);
- IMG_RATIO = res.width / res.height
- if (IMG_RATIO >= 1) {
- IMG_REAL_W = SCREEN_WIDTH
- IMG_REAL_H = SCREEN_WIDTH / IMG_RATIO
- } else {
- IMG_REAL_W = SCREEN_WIDTH * IMG_RATIO
- IMG_REAL_H = SCREEN_WIDTH
- }
- let minRange = IMG_REAL_W > IMG_REAL_H ? IMG_REAL_W : IMG_REAL_H
- INIT_DRAG_POSITION = minRange > INIT_DRAG_POSITION ? INIT_DRAG_POSITION : minRange
- // 根据图片的宽高显示不同的效果 保证图片可以正常显示
- if (IMG_RATIO >= 1) {
- let cutT = Math.ceil((SCREEN_WIDTH / IMG_RATIO - (SCREEN_WIDTH / IMG_RATIO - INIT_DRAG_POSITION)) / 2);
- let cutB = cutT;
- let cutL = Math.ceil((SCREEN_WIDTH - SCREEN_WIDTH + INIT_DRAG_POSITION) / 2);
- let cutR = cutL;
- _this.setData({
- cropperW: SCREEN_WIDTH,
- cropperH: SCREEN_WIDTH / IMG_RATIO,
- // 初始化left right
- cropperL: Math.ceil((SCREEN_WIDTH - SCREEN_WIDTH) / 2),
- cropperT: Math.ceil((SCREEN_WIDTH - SCREEN_WIDTH / IMG_RATIO) / 2),
- cutL: cutL,
- cutT: cutT,
- cutR: cutR,
- cutB: cutB,
- // 图片缩放值
- imageW: IMG_REAL_W,
- imageH: IMG_REAL_H,
- scaleP: IMG_REAL_W / SCREEN_WIDTH,
- qualityWidth: DRAW_IMAGE_W,
- innerAspectRadio: IMG_RATIO
- })
- } else {
- let cutL = Math.ceil((SCREEN_WIDTH * IMG_RATIO - (SCREEN_WIDTH * IMG_RATIO)) / 2);
- let cutR = cutL;
- let cutT = Math.ceil((SCREEN_WIDTH - INIT_DRAG_POSITION) / 2);
- let cutB = cutT;
- _this.setData({
- cropperW: SCREEN_WIDTH * IMG_RATIO,
- cropperH: SCREEN_WIDTH,
- // 初始化left right
- cropperL: Math.ceil((SCREEN_WIDTH - SCREEN_WIDTH * IMG_RATIO) / 2),
- cropperT: Math.ceil((SCREEN_WIDTH - SCREEN_WIDTH) / 2),
-
- cutL: cutL,
- cutT: cutT,
- cutR: cutR,
- cutB: cutB,
- // 图片缩放值
- imageW: IMG_REAL_W,
- imageH: IMG_REAL_H,
- scaleP: IMG_REAL_W / SCREEN_WIDTH,
- qualityWidth: DRAW_IMAGE_W,
- innerAspectRadio: IMG_RATIO
- })
- }
- _this.setData({
- isShowImg: true
- })
- uni.hideLoading()
- }
- })
- },
- // 拖动时候触发的touchStart事件
- contentStartMove(e) {
- PAGE_X = e.touches[0].pageX
- PAGE_Y = e.touches[0].pageY
- },
-
- // 拖动时候触发的touchMove事件
- contentMoveing(e) {
- var _this = this
- var dragLengthX = (PAGE_X - e.touches[0].pageX) * DRAFG_MOVE_RATIO
- var dragLengthY = (PAGE_Y - e.touches[0].pageY) * DRAFG_MOVE_RATIO
- // 左移
- if (dragLengthX > 0) {
- if (this.cutL - dragLengthX < 0) dragLengthX = this.cutL
- } else {
- if (this.cutR + dragLengthX < 0) dragLengthX = -this.cutR
- }
-
- if (dragLengthY > 0) {
- if (this.cutT - dragLengthY < 0) dragLengthY = this.cutT
- } else {
- if (this.cutB + dragLengthY < 0) dragLengthY = -this.cutB
- }
- this.setData({
- cutL: this.cutL - dragLengthX,
- cutT: this.cutT - dragLengthY,
- cutR: this.cutR + dragLengthX,
- cutB: this.cutB + dragLengthY
- })
-
- PAGE_X = e.touches[0].pageX
- PAGE_Y = e.touches[0].pageY
- },
-
- contentTouchEnd() {
-
- },
-
- // 获取图片
- getImageInfo() {
- var _this = this;
- uni.showLoading({
- title: '头像上传中...',
- });
- // 将图片写入画布
- const ctx = uni.createCanvasContext('myCanvas');
- ctx.drawImage(_this.imageSrc, 0, 0, IMG_REAL_W, IMG_REAL_H);
- ctx.draw(true, () => {
- // 获取画布要裁剪的位置和宽度 均为百分比 * 画布中图片的宽度 保证了在微信小程序中裁剪的图片模糊 位置不对的问题 canvasT = (_this.cutT / _this.cropperH) * (_this.imageH / pixelRatio)
- var canvasW = ((_this.cropperW - _this.cutL - _this.cutR) / _this.cropperW) * IMG_REAL_W;
- var canvasH = ((_this.cropperH - _this.cutT - _this.cutB) / _this.cropperH) * IMG_REAL_H;
- var canvasL = (_this.cutL / _this.cropperW) * IMG_REAL_W;
- var canvasT = (_this.cutT / _this.cropperH) * IMG_REAL_H;
- uni.canvasToTempFilePath({
- x: canvasL,
- y: canvasT,
- width: canvasW,
- height: canvasH,
- destWidth: canvasW,
- destHeight: canvasH,
- quality: 0.5,
- canvasId: 'myCanvas',
- success: function(res) {
- indexApi.uploadImg(res.tempFilePath,function(res1){
- uni.$emit("updateHeadimg", true);
- uni.navigateBack();
- })
- },
- fail: function(error) {
- console.log(error);
- },
- complete:function(){
- uni.hideLoading();
- }
- });
- });
- },
-
- // 设置大小的时候触发的touchStart事件
- dragStart(e) {
- T_PAGE_X = e.touches[0].pageX
- T_PAGE_Y = e.touches[0].pageY
- CUT_L = this.cutL
- CUT_R = this.cutR
- CUT_B = this.cutB
- CUT_T = this.cutT
- },
-
- // 设置大小的时候触发的touchMove事件
- dragMove(e) {
- var _this = this
- var dragType = e.target.dataset.drag
- switch (dragType) {
- case 'right':
- var dragLength = (T_PAGE_X - e.touches[0].pageX) * DRAFG_MOVE_RATIO
- if (CUT_R + dragLength < 0) dragLength = -CUT_R
- this.setData({
- cutR: CUT_R + dragLength
- })
- break;
- case 'left':
- var dragLength = (T_PAGE_X - e.touches[0].pageX) * DRAFG_MOVE_RATIO
- if (CUT_L - dragLength < 0) dragLength = CUT_L
- if ((CUT_L - dragLength) > (this.cropperW - this.cutR)) dragLength = CUT_L - (this.cropperW - this.cutR)
- this.setData({
- cutL: CUT_L - dragLength
- })
- break;
- case 'top':
- var dragLength = (T_PAGE_Y - e.touches[0].pageY) * DRAFG_MOVE_RATIO
- if (CUT_T - dragLength < 0) dragLength = CUT_T
- if ((CUT_T - dragLength) > (this.cropperH - this.cutB)) dragLength = CUT_T - (this.cropperH - this.cutB)
- this.setData({
- cutT: CUT_T - dragLength
- })
- break;
- case 'bottom':
- var dragLength = (T_PAGE_Y - e.touches[0].pageY) * DRAFG_MOVE_RATIO
- if (CUT_B + dragLength < 0) dragLength = -CUT_B
- this.setData({
- cutB: CUT_B + dragLength
- })
- break;
- case 'rightBottom':
- var dragLengthX = (T_PAGE_X - e.touches[0].pageX) * DRAFG_MOVE_RATIO
- var dragLengthY = (T_PAGE_Y - e.touches[0].pageY) * DRAFG_MOVE_RATIO
-
- if (CUT_B + dragLengthY < 0) dragLengthY = -CUT_B
- if (CUT_R + dragLengthX < 0) dragLengthX = -CUT_R
- let cutB = CUT_B + dragLengthY;
- let cutR = CUT_R + dragLengthX;
-
- this.setData({
- cutB: cutB,
- cutR: cutR
- })
- break;
- default:
- break;
- }
- }
- }
- }
- </script>
-
- <style>
- /* pages/uni-cropper/index.wxss */
-
- .uni-content-info {
- position: fixed;
- top: 0;
- left: 0;
- right: 0;
- bottom: 0;
- display: block;
- align-items: center;
- flex-direction: column;
- }
-
- .cropper-config {
- padding: 20upx 40upx;
- }
-
- .cropper-content {
- min-height: 750upx;
- width: 100%;
- }
-
- .uni-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;
- }
-
- .uni-corpper-content {
- position: relative;
- }
-
- .uni-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;
- }
-
- /* 移动图片效果 */
-
- .uni-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;
- }
-
- /* 内部的信息 */
-
- .uni-corpper-crop-box {
- position: absolute;
- background: rgba(255, 255, 255, 0.3);
- z-index: 2;
- }
-
- .uni-corpper-crop-box .uni-cropper-view-box {
- position: relative;
- display: block;
- width: 100%;
- height: 100%;
- overflow: visible;
- outline: 1upx solid #69f;
- outline-color: rgba(102, 153, 255, .75)
- }
-
- /* 横向虚线 */
-
- .uni-cropper-dashed-h {
- position: absolute;
- top: 33.33333333%;
- left: 0;
- width: 100%;
- height: 33.33333333%;
- border-top: 1upx dashed rgba(255, 255, 255, 0.5);
- border-bottom: 1upx dashed rgba(255, 255, 255, 0.5);
- }
-
- /* 纵向虚线 */
-
- .uni-cropper-dashed-v {
- position: absolute;
- left: 33.33333333%;
- top: 0;
- width: 33.33333333%;
- height: 100%;
- border-left: 1upx dashed rgba(255, 255, 255, 0.5);
- border-right: 1upx dashed rgba(255, 255, 255, 0.5);
- }
-
- /* 四个方向的线 为了之后的拖动事件*/
-
- .uni-cropper-line-t {
- position: absolute;
- display: block;
- width: 100%;
- background-color: #69f;
- top: 0;
- left: 0;
- height: 1upx;
- opacity: 0.1;
- cursor: n-resize;
- }
-
- .uni-cropper-line-t::before {
- content: '';
- position: absolute;
- top: 50%;
- right: 0upx;
- width: 100%;
- -webkit-transform: translate3d(0, -50%, 0);
- transform: translate3d(0, -50%, 0);
- bottom: 0;
- height: 41upx;
- background: transparent;
- z-index: 11;
- }
-
- .uni-cropper-line-r {
- position: absolute;
- display: block;
- background-color: #69f;
- top: 0;
- right: 0upx;
- width: 1upx;
- opacity: 0.1;
- height: 100%;
- cursor: e-resize;
- }
-
- .uni-cropper-line-r::before {
- content: '';
- position: absolute;
- top: 0;
- left: 50%;
- width: 41upx;
- -webkit-transform: translate3d(-50%, 0, 0);
- transform: translate3d(-50%, 0, 0);
- bottom: 0;
- height: 100%;
- background: transparent;
- z-index: 11;
- }
-
- .uni-cropper-line-b {
- position: absolute;
- display: block;
- width: 100%;
- background-color: #69f;
- bottom: 0;
- left: 0;
- height: 1upx;
- opacity: 0.1;
- cursor: s-resize;
- }
-
- .uni-cropper-line-b::before {
- content: '';
- position: absolute;
- top: 50%;
- right: 0upx;
- width: 100%;
- -webkit-transform: translate3d(0, -50%, 0);
- transform: translate3d(0, -50%, 0);
- bottom: 0;
- height: 41upx;
- background: transparent;
- z-index: 11;
- }
-
- .uni-cropper-line-l {
- position: absolute;
- display: block;
- background-color: #69f;
- top: 0;
- left: 0;
- width: 1upx;
- opacity: 0.1;
- height: 100%;
- cursor: w-resize;
- }
-
- .uni-cropper-line-l::before {
- content: '';
- position: absolute;
- top: 0;
- left: 50%;
- width: 41upx;
- -webkit-transform: translate3d(-50%, 0, 0);
- transform: translate3d(-50%, 0, 0);
- bottom: 0;
- height: 100%;
- background: transparent;
- z-index: 11;
- }
-
- .uni-cropper-point {
- width: 5upx;
- height: 5upx;
- background-color: #69f;
- opacity: .75;
- position: absolute;
- z-index: 3;
- }
-
- .point-t {
- top: -3upx;
- left: 50%;
- margin-left: -3upx;
- cursor: n-resize;
- }
-
- .point-tr {
- top: -3upx;
- left: 100%;
- margin-left: -3upx;
- cursor: n-resize;
- }
-
- .point-r {
- top: 50%;
- left: 100%;
- margin-left: -3upx;
- margin-top: -3upx;
- cursor: n-resize;
- }
-
- .point-rb {
- left: 100%;
- top: 100%;
- -webkit-transform: translate3d(-50%, -50%, 0);
- transform: translate3d(-50%, -50%, 0);
- cursor: n-resize;
- width: 36upx;
- height: 36upx;
- background-color: #69f;
- position: absolute;
- z-index: 1112;
- opacity: 1;
- }
-
- .point-b {
- left: 50%;
- top: 100%;
- margin-left: -3upx;
- margin-top: -3upx;
- cursor: n-resize;
- }
-
- .point-bl {
- left: 0%;
- top: 100%;
- margin-left: -3upx;
- margin-top: -3upx;
- cursor: n-resize;
- }
-
- .point-l {
- left: 0%;
- top: 50%;
- margin-left: -3upx;
- margin-top: -3upx;
- cursor: n-resize;
- }
-
- .point-lt {
- left: 0%;
- top: 0%;
- margin-left: -3upx;
- margin-top: -3upx;
- cursor: n-resize;
- }
-
- /* 裁剪框预览内容 */
-
- .uni-cropper-viewer {
- position: relative;
- width: 100%;
- height: 100%;
- overflow: hidden;
- }
-
- .uni-cropper-viewer image {
- position: absolute;
- z-index: 2;
- }
- </style>
3. 裁剪好后可以上传图像 上传业务逻辑
- //用户上传图像
- const uploadImg = (path, callback) => {
- uni.uploadFile({//上传文件api
- url: '后台上传接口',
- filePath: path,
- name: 'file',
- success: (uploadFileRes) => {
- callback(uploadFileRes.data);
- }
- });
- }
*** 注意事项 在个人首页 会监听裁剪页面保存动作,裁剪了并保存成功了会返回 true ,根据true刷新个人头像
*** 注意事项 如果全部成功了 但是头像没有刷新 那么你可以在 头像那个 view 里面加上个 :key 属性,动态设置 :key 就行了,其实就是 :key 没改变 所以刷新不了,这个也坑了我一会了。