赞
踩
1.新建公共组件components / cropping / cropping.vue 将代码复制进去
复制代码放入cropping.vue
- <template name="yq-avatar">
- <view class="croppage">
- <canvas canvas-id="avatar-canvas" id="avatar-canvas" class="my-canvas" :style="{top: sT, height: csH}"
- :disable-scroll="false"></canvas>
- <canvas canvas-id="oper-canvas" id="oper-canvas" class="oper-canvas" :style="{top: sT1, height: csH1}"
- :disable-scroll="false" @touchstart="fStart" @touchmove="fMove" @touchend="fEnd">
- </canvas>
- <canvas canvas-id="prv-canvas" id="prv-canvas" class="prv-canvas" :disable-scroll="false" @touchstart="fHideImg"
- :style="{ height: csH, top: pT }">
- </canvas>
- <view class="oper-wrapper" :style="{top:tp}">
- <view class="oper">
- <view class="btn-wrapper">
- <view @click="rechoose" hover-class="hover" :style="{width: bW}"><text>重选</text></view>
- <view @click="fUpload" hover-class="hover" :style="{width: bW}"><text>确定</text></view>
- </view>
- </view>
- </view>
- </view>
- </template>
-
- <script>
- const tH = 50;
- export default {
- data() {
- return {
- sT1num: 0,
- csH1: '0px',
- sT1: 0,
- csH: '0px',
- sD: 'none',
- sT: '-10000px',
- pT: '-10000px',
- iS: {},
- sS: {},
- bW: '19%',
- bD: 'flex',
- tp: 0,
- };
- },
- props: {
- avatarSrc: '',
- avatarStyle: '',
- selWidth: '',
- selHeight: '',
- expWidth: '',
- expHeight: '',
- minScale: '',
- maxScale: '',
- canScale: '',
- canRotate: '',
- lockWidth: '',
- lockHeight: '',
- stretch: '',
- lock: '',
- fileType: '',
- noTab: '',
- inner: '',
- quality: '',
- index: '',
- bgImage: '',
- },
- created() {
- this.cc = uni.createCanvasContext('avatar-canvas', this);
- this.cco = uni.createCanvasContext('oper-canvas', this);
- this.ccp = uni.createCanvasContext('prv-canvas', this);
- this.qlty = parseFloat(this.quality) || 1; //画质
- this.letRotate = (this.canRotate === false || this.inner === true || this.inner === 'true' || this
- .canRotate === 'false') ? 0 : 1; //是否旋转
- this.letScale = (this.canScale === false || this.canScale === 'false') ? 0 : 1; //是否放大
- this.isin = (this.inner === true || this.inner === 'true') ? 1 : 0;
- this.indx = this.index || undefined; //禁止旋转并在图片范围内移动
- this.mnScale = parseFloat(this.minScale) || 0.3; //缩小比例
- this.mxScale = parseFloat(this.maxScale) || 4; //放大比列
- this.noBar = (this.noTab === true || this.noTab === 'true') ? 1 : 0; //是否存在nobar
- this.stc = this.stretch; //自动铺满
- this.lck = this.lock; //锁定图方向 x y long short longSel shortSel
- this.fType = this.fileType === 'jpg' ? 'jpg' : 'png';
- if (this.isin || !this.letRotate) {
- this.bW = '24%';
- this.bD = 'none';
- } else {
- this.bW = '19%';
- this.bD = 'flex';
- }
- if (this.noBar) {
- this.fWindowResize();
- } else {
- uni.showTabBar({
- fail: () => {
- this.noBar = 1;
- },
- success: () => {
- this.noBar = 0;
- },
- complete: (res) => {
- this.fWindowResize();
- }
- });
- }
- },
- methods: {
- //重选
- rechoose() {
- const that = this
- uni.chooseImage({
- count: 1,
- sizeType: ['original', 'compressed'],
- sourceType: ["album"],
- success: (res) => {
- that.sT1 = that.sT
- that.csH1 = that.csH
- that.fSelect(res.tempFilePaths[0])
- }
- });
- },
- //初始化各种数据
- fWindowResize() {
- let sysInfo = uni.getSystemInfoSync();
- this.platform = sysInfo.platform;
- this.wW = sysInfo.windowWidth;
- // #ifndef H5
- let phone = uni.getSystemInfoSync().platform
- if (phone == 'ios') {
- let h = uni.upx2px(60); //将rpx单位值转换成px 裁剪框自己
- let b = uni.upx2px(100); //底部按钮
- this.sT1 = (sysInfo.windowHeight - h - b) / 2 + 'px' //裁剪框距离顶部+px
- this.sT1num = (sysInfo.windowHeight - h - b) / 2 //裁剪框距离顶部不+px
- } else {
- this.drawTop = 0;
- }
- // #endif
- // #ifndef MP-ALIPAY
- this.wH = sysInfo.windowHeight; //设备高
- if (!this.noBar) this.wH += tH; //th=50
- this.csH = this.wH - tH + 'px'; //高=设备高-50(导航)
- if (phone == 'ios') { //适配ios
- this.csH1 = (this.wH - 50) / 2 + 'px'
- } else {
- this.csH1 = this.wH - tH + 'px';
- }
- // #endif
- this.tp = this.csH;
- this.pxRatio = this.wW / 750; //设备宽/750 比列
- this.expWidth && (this.eW = this.expWidth.toString().indexOf('upx') >= 0 ? parseInt(this.expWidth) * this
- .pxRatio : parseInt(this.expWidth));
- this.expHeight && (this.eH = this.expHeight.toString().indexOf('upx') >= 0 ? parseInt(this.expHeight) *
- this.pxRatio : parseInt(this.expHeight));
- this.fHideImg();
- },
- fSelect(r) {
- if (this.fSelecting) return;
- this.fSelecting = true;
- setTimeout(() => {
- this.fSelecting = false;
- }, 500); //防抖
- let path = this.imgPath = r; //需要剪裁的图片路径
- // 获取图片信息
- uni.getImageInfo({
- src: path,
- success: r => {
- this.imgWidth = r.width;
- this.imgHeight = r.height;
- this.path = path;
- if (!this.hasSel) {
- let style = this.sS || {};
- if (this.selWidth && this.selHeight) { //设置的剪裁区域宽高
- let sW = this.selWidth.toString().indexOf('upx') >= 0 ?
- parseInt(this.selWidth) * this.pxRatio : parseInt(
- this.selWidth),
- sH = this.selHeight.toString().indexOf('upx') >= 0 ?
- parseInt(this.selHeight) * this.pxRatio : parseInt(
- this.selHeight);
- style.width = sW + 'px';
- style.height = sH + 'px';
- style.top = ((this.wH - sH - tH) | 0) / 2 + 'px';
- style.left = ((this.wW - sW) | 0) / 2 + 'px';
- // }
- } else {
- uni.showModal({
- title: '裁剪框的宽或高没有设置',
- showCancel: false
- })
- return;
- }
- this.sS = style;
- }
- if (this.noBar) {
- setTimeout(() => {
- this.fDrawInit(true);
- }, 200)
- } else {
- uni.hideTabBar({
- complete: () => {
- setTimeout(() => {
- this.fDrawInit(true);
- }, 200)
- }
- });
- }
- },
- fail: () => {
- uni.showToast({
- title: "请选择正确图片",
- duration: 2000,
- })
- },
- complete() {
- uni.hideLoading();
- }
- });
- },
- //剪裁确定
- fUpload() {
- uni.showLoading({
- title: '图片上传中...',
- mask: true
- });
- if (this.fUploading) return;
- this.fUploading = true;
- setTimeout(() => {
- this.fUploading = false;
- }, 1000)
- let style = this.sS,
- x = parseInt(style.left),
- y = parseInt(style.top),
- width = parseInt(style.width),
- height = parseInt(style.height),
- expWidth = this.eW || (width * this.pixelRatio),
- expHeight = this.eH || (height * this.pixelRatio);
- this.fHideImg();
- // #ifndef MP-ALIPAY
- let phone = uni.getSystemInfoSync().platform
- if (phone == 'ios') {
- y = this.sT1num
- }
- uni.canvasToTempFilePath({
- x: x,
- y: y,
- width: width,
- height: height,
- destWidth: expWidth,
- destHeight: expHeight,
- canvasId: 'avatar-canvas',
- fileType: this.fType,
- quality: this.qlty,
- success: (r) => {
- r = r.tempFilePath;
- // #ifndef H5
- this.$emit("upload", {
- avatar: this.imgSrc,
- path: r,
- index: this.indx,
- data: this.rtn,
- base64: this.base64 || null
- });
- // #endif
- },
- fail: (res) => {
- uni.showToast({
- title: "error1",
- duration: 2000,
- })
- },
- complete: () => {
- uni.hideLoading();
- this.noBar || uni.showTabBar();
- this.$emit("end");
- }
- }, this);
- // #endif
- },
- fDrawInit(ini = false) {
- let allWidth = this.wW, //设备宽
- allHeight = this.wH, //设备高
- imgWidth = this.imgWidth, //图宽
- imgHeight = this.imgHeight, //图高
- imgRadio = imgWidth / imgHeight, //图比
- useWidth = allWidth - 40, //设备宽-40
- useHeight = allHeight - tH - 80, //设备高-80
- useRadio = useWidth / useHeight,
- sW = parseInt(this.sS.width), //图片信息
- sH = parseInt(this.sS.height);
- this.fixWidth = 0;
- this.fixHeight = 0;
- this.lckWidth = 0;
- this.lckHeight = 0;
- switch (this.stc) { //以下是自动铺满的算法
- case 'x':
- this.fixWidth = 1;
- break;
- case 'y':
- this.fixHeight = 1;
- break;
- case 'long':
- if (imgRadio > 1) this.fixWidth = 1;
- else this.fixHeight = 1;
- break;
- case 'short':
- if (imgRadio > 1) this.fixHeight = 1;
- else this.fixWidth = 1;
- break;
- case 'longSel':
- if (sW > sH) this.fixWidth = 1;
- else this.fixHeight = 1;
- break;
- case 'shortSel':
- if (sW > sH) this.fixHeight = 1;
- else this.fixWidth = 1;
- break;
- }
- switch (this.lck) { //以下锁定屏幕的移动方向
- case 'x':
- this.lckWidth = 1;
- break;
- case 'y':
- this.lckHeight = 1;
- break;
- case 'long':
- if (imgRadio > 1) this.lckWidth = 1;
- else this.lckHeight = 1;
- break;
- case 'short':
- if (imgRadio > 1) this.lckHeight = 1;
- else this.lckWidth = 1;
- break;
- case 'longSel':
- if (sW > sH) this.lckWidth = 1;
- else this.lckHeight = 1;
- break;
- case 'shortSel':
- if (sW > sH) this.lckHeight = 1;
- else this.lckWidth = 1;
- break;
- }
- if (this.fixWidth) {
- useWidth = sW;
- useHeight = useWidth / imgRadio;
- } else if (this.fixHeight) {
- useHeight = sH;
- useWidth = useHeight * imgRadio;
- } else if (imgRadio < useRadio) {
- if (imgHeight < useHeight) {
- useWidth = imgWidth;
- useHeight = imgHeight;
- } else {
- useWidth = useHeight * imgRadio;
- }
- } else {
- if (imgWidth < useWidth) {
- useWidth = imgWidth;
- useHeight = imgHeight;
- } else {
- useHeight = useWidth / imgRadio;
- }
- }
- if (this.isin) {
- if (useWidth < sW) {
- useWidth = sW;
- useHeight = useWidth / imgRadio;
- this.lckHeight = 0;
- }
- if (useHeight < sH) {
- useHeight = sH;
- useWidth = useHeight * imgRadio;
- this.lckWidth = 0;
- }
- }
- this.scaleSize = 1;
- this.rotateDeg = 0;
- this.posWidth = (allWidth - useWidth) / 2 | 0;
- this.posHeight = (allHeight - useHeight - tH) / 2 | 0;
- this.useWidth = useWidth | 0;
- this.useHeight = useHeight | 0;
- this.centerX = this.posWidth + useWidth / 2;
- this.centerY = this.posHeight + useHeight / 2;
- this.focusX = 0;
- this.focusY = 0;
- let style = this.sS,
- left = parseInt(style.left),
- top = parseInt(style.top),
- width = parseInt(style.width),
- height = parseInt(style.height),
- canvas = this.canvas,
- canvasOper = this.canvasOper,
- cc = this.cc, //avatar-canvas
- cco = this.cco; //oper-canvas 裁剪
- cco.beginPath(); //开始创建一个路径
- cco.setLineWidth(3); //设置线条的宽度 px
- cco.setGlobalAlpha(1); //设置全局画笔透明度。
- cco.setStrokeStyle('white'); //设置边框颜色。如果没有设置 fillStyle,默认颜色为 black。
- cco.strokeRect(left, top, width, height); //画一个矩形(非填充)。用 setFillStroke() 设置边框颜色,如果没设置默认是黑色。
- cco.setFillStyle('black');
- cco.setGlobalAlpha(0.5);
- cco.fillRect(0, 0, this.wW, top); //填充一个矩形
- cco.fillRect(0, top, left, height);
- cco.fillRect(0, top + height, this.wW, this.wH - height - top - tH);
- cco.fillRect(left + width, top, this.wW - width - left, height);
- cco.setGlobalAlpha(1);
- cco.setStrokeStyle('red');
- cco.moveTo(left + 15, top); //把路径移动到画布中的指定点,不创建线条。用 stroke() 方法来画线条。
- cco.lineTo(left, top); //增加一个新点,然后创建一条从上次指定点到目标点的线。
- cco.lineTo(left, top + 15);
- cco.moveTo(left + width - 15, top);
- cco.lineTo(left + width, top);
- cco.lineTo(left + width, top + 15);
- cco.moveTo(left + 15, top + height);
- cco.lineTo(left, top + height);
- cco.lineTo(left, top + height - 15);
- cco.moveTo(left + width - 15, top + height);
- cco.lineTo(left + width, top + height);
- cco.lineTo(left + width, top + height - 15);
- cco.stroke(); //画线条
- cco.draw(false, () => { //将之前在绘图上下文中的描述(路径、变形、样式)画到 canvas 中。
- if (ini) {
- this.sT = this.drawTop + 'px';
- this.fDrawImage(true); //绘制背景色
- }
- });
- this.$emit("init");
- },
- //绘制背景色
- fDrawImage(ini = false) {
- let tm_now = Date.now(); //当前时间
- if (tm_now - this.drawTm < 20) return;
- this.drawTm = tm_now;
- let cc = this.cc,
- imgWidth = this.useWidth * this.scaleSize,
- imgHeight = this.useHeight * this.scaleSize;
- if (this.bgImage) { //如果背景图
- // #ifndef MP-ALIPAY
- cc.drawImage(this.bgImage, 0, 0, this.wW, this.wH - tH); //绘制图像到画布。
- // #endif
- } else {
- cc.fillRect(0, 0, this.wW, this.wH - tH); //填充一个矩形
- }
- if (this.isin) { //禁止旋转并在图片范围内移动
- let cx = this.focusX * (this.scaleSize - 1),
- cy = this.focusY * (this.scaleSize - 1);
- cc.translate(this.centerX, this.centerY);
- cc.rotate(this.rotateDeg * Math.PI / 180);
- cc.drawImage(this.imgPath, this.posWidth - this.centerX - cx, this.posHeight - this.centerY - cy,
- imgWidth, imgHeight);
- } else {
- cc.translate(this.posWidth + imgWidth / 2, this.posHeight + imgHeight / 2);
- cc.rotate(this.rotateDeg * Math.PI / 180);
- cc.drawImage(this.imgPath, -imgWidth / 2, -imgHeight / 2, imgWidth, imgHeight);
- }
- cc.draw(false);
- },
- //旋转
- fRotate() {
- this.rotateDeg += 90 - this.rotateDeg % 90;
- this.fDrawImage();
- },
- //手指触摸开始
- fStart(e) {
- let touches = e.touches,
- touch0 = touches[0],
- touch1 = touches[1];
- this.touch0 = touch0;
- this.touch1 = touch1;
- if (touch1) {
- let x = touch1.x - touch0.x,
- y = touch1.y - touch0.y;
- this.fgDistance = Math.sqrt(x * x + y * y);
- }
- },
- //手指触摸后移动
- fMove(e) {
- let touches = e.touches,
- touch0 = touches[0],
- touch1 = touches[1];
- if (touch1) {
- let x = touch1.x - touch0.x,
- y = touch1.y - touch0.y,
- fgDistance = Math.sqrt(x * x + y * y),
- scaleSize = 0.005 * (fgDistance - this.fgDistance),
- beScaleSize = this.scaleSize + scaleSize;
- do {
- if (!this.letScale) break;
- if (beScaleSize < this.mnScale) break;
- if (beScaleSize > this.mxScale) break;
- let growX = this.useWidth * scaleSize / 2,
- growY = this.useHeight * scaleSize / 2;
- if (this.isin) {
- let imgWidth = this.useWidth * beScaleSize,
- imgHeight = this.useHeight * beScaleSize,
- l = this.posWidth - growX,
- t = this.posHeight - growY,
- r = l + imgWidth,
- b = t + imgHeight,
- left = parseInt(this.sS.left),
- top = parseInt(this.sS.top),
- width = parseInt(this.sS.width),
- height = parseInt(this.sS.height),
- right = left + width,
- bottom = top + height,
- cx, cy;
- if (imgWidth <= width || imgHeight <= height) break;
- this.cx = cx = this.focusX * beScaleSize - this.focusX,
- this.cy = cy = this.focusY * beScaleSize - this.focusY;
- this.posWidth -= growX;
- this.posHeight -= growY;
- if (this.posWidth - cx > left) {
- this.posWidth = left + cx;
- }
- if (this.posWidth + imgWidth - cx < right) {
- this.posWidth = right - imgWidth + cx;
- }
- if (this.posHeight - cy > top) {
- this.posHeight = top + cy;
- }
- if (this.posHeight + imgHeight - cy < bottom) {
- this.posHeight = bottom - imgHeight + cy;
- }
- } else {
- this.posWidth -= growX;
- this.posHeight -= growY;
- }
- this.scaleSize = beScaleSize;
- } while (0);
- this.fgDistance = fgDistance;
- if (touch1.x !== touch0.x && this.letRotate) {
- x = (this.touch1.y - this.touch0.y) / (this.touch1.x - this.touch0.x);
- y = (touch1.y - touch0.y) / (touch1.x - touch0.x);
- this.rotateDeg += Math.atan((y - x) / (1 + x * y)) * 180 / Math.PI;
- this.touch0 = touch0;
- this.touch1 = touch1;
- }
- this.fDrawImage();
- } else if (this.touch0) {
- let x = touch0.x - this.touch0.x,
- y = touch0.y - this.touch0.y,
- beX = this.posWidth + x,
- beY = this.posHeight + y;
- if (this.isin) {
- let imgWidth = this.useWidth * this.scaleSize,
- imgHeight = this.useHeight * this.scaleSize,
- l = beX,
- t = beY,
- r = l + imgWidth,
- b = t + imgHeight,
- left = parseInt(this.sS.left),
- top = parseInt(this.sS.top),
- right = left + parseInt(this.sS.width),
- bottom = top + parseInt(this.sS.height),
- cx, cy;
- this.cx = cx = this.focusX * this.scaleSize - this.focusX;
- this.cy = cy = this.focusY * this.scaleSize - this.focusY;
- if (!this.lckWidth && Math.abs(x) < 100) {
- if (left < l - cx) {
- this.posWidth = left + cx;
- } else if (right > r - cx) {
- this.posWidth = right - imgWidth + cx;
- } else {
- this.posWidth = beX;
- this.focusX -= x;
- }
- }
- if (!this.lckHeight && Math.abs(y) < 100) {
- if (top < t - cy) {
- this.focusY -= (top + cy - this.posHeight);
- this.posHeight = top + cy;
- } else if (bottom > b - cy) {
- this.focusY -= (bottom + cy - (this.posHeight + imgHeight));
- this.posHeight = bottom - imgHeight + cy;
- } else {
- this.posHeight = beY;
- this.focusY -= y;
- }
- }
- } else {
- if (Math.abs(x) < 100 && !this.lckWidth) this.posWidth = beX;
- if (Math.abs(y) < 100 && !this.lckHeight) this.posHeight = beY;
- this.focusX -= x;
- this.focusY -= y;
- }
- this.touch0 = touch0;
- this.fDrawImage();
- }
- },
- //手指触摸动作结束
- fEnd(e) {
- let touches = e.touches,
- touch0 = touches && touches[0],
- touch1 = touches && touches[1];
- if (touch0) {
- this.touch0 = touch0;
- } else {
- this.touch0 = null;
- this.touch1 = null;
- }
- },
- fHideImg() {
- this.prvImg = '';
- this.pT = '-10000px';
- this.prvImgData = null;
- this.target = null;
- },
- }
- }
- </script>
- <style>
- .croppage {
- width: 100%;
- height: 100%;
- box-sizing: border-box;
- background-color: #000000;
- position: relative;
- }
- .my-canvas {
- /* display: flex; */
- position: absolute !important;
- left: 0;
- z-index: 100000;
- width: 100%;
- }
- .my-avatar {
- width: 150upx;
- height: 150upx;
- border-radius: 100%;
- }
- .oper-canvas {
- /* display: flex; */
- position: absolute !important;
- left: 0;
- z-index: 100000;
- width: 100%;
- }
- .prv-canvas {
- display: flex;
- position: fixed !important;
- background: #000000;
- left: 0;
- z-index: 200000;
- width: 100%;
- }
- .oper-wrapper {
- height: 100rpx;
- position: fixed !important;
- box-sizing: border-box;
- border: 1px solid #F1F1F1;
- background: #ffffff;
- width: 100%;
- left: 0;
- bottom: 0;
- z-index: 99999999;
- flex-direction: row;
- }
- .oper {
- display: flex;
- flex-direction: column;
- justify-content: center;
- padding: 10upx 20upx;
- width: 100%;
- height: 100%;
- box-sizing: border-box;
- align-self: center;
- }
- .btn-wrapper {
- display: flex;
- flex-direction: row;
- flex-grow: 1;
- height: 50px;
- justify-content: space-between;
- }
-
- .btn-wrapper view {
- display: flex;
- align-items: center;
- justify-content: center;
- font-size: 16px;
- color: #3b7ffb;
- border-radius: 6%;
- }
- .hover {
- background: #f1f1f1;
- border-radius: 6%;
- }
- .clr-wrapper {
- display: flex;
- flex-direction: row;
- flex-grow: 1;
- }
- .clr-wrapper view {
- display: flex;
- align-items: center;
- justify-content: center;
- font-size: 16px;
- color: #333;
- border: 1px solid #f1f1f1;
- border-radius: 6%;
- }
- .my-slider {
- flex-grow: 1;
- }
- </style>
2.在需要使用的页面引入组件
- <template>
- <view>
- <Cropping @upload="doUpload" ref="cropping" :selWidth="screenWidth" selHeight="60rpx" />
- </view>
- </template>
-
- <script>
- import Cropping from "@/components/cropping/cropping.vue";
- export default {
- components: {
- Cropping
- },
- data() {
- return {
- result:'',
- screenWidth: ''
- }
- },
- onLoad(option) {
-
- let getWindowInfo = uni.getWindowInfo()
- this.screenWidth = getWindowInfo.screenWidth
- //screenWidth定义需要裁剪的宽度 selHeight为高度 自行定义传入
- this.$nextTick(()=>{
- //调用组件的fSelect方法传入需要裁剪的图片路径
- //option.img为需要裁剪的图片路径 自行定义传入
- this.$refs.cropping.fSelect(option.img)
- })
- },
- methods: {
- //剪裁确认
- doUpload(rsp) {
- console.log(rsp)
- //rsp.path为截取的图片路径
- },
- }
- }
- </script>