第一步:拿到图片信息
第二步:计算基准框大小,设置预览图片基准值
第三步:预览图片宽高处理
第四步:获取屏幕宽高,绘制蒙层
第五步:图片预览
第六步:预览图拖拽处理
第七步:图像裁剪
<template>
<view class="settingHeadImage" @touchstart="onTouchstart" @touchmove.stop.prevent="onTouchmove" @touchend="touchE">
<!-- 蒙层 -->
<canvas class="pre-canvas" canvas-id="firstCanvas" :style="{ width: 100 + 'vw', height: 100 + 'vh' }"></canvas>
<!-- img预览 -->
<view class="preImage" :style="{ width: preImgW + 'px' }">
<canvas canvas-id="mycanvas" class="pre-i"
:style="{ width: preImgW + 'px', height: preImgH + 'px', transform: `translate(${x}px,${y}px)` }"></canvas>
</view>
<!-- 工具 -->
<view class="setting-btns"><text @click="onCrop">确定</text></view>
</view>
</template>
<script>
export default {
data() {
return {
maxW: 250, // 最大宽度
maxH: 250,
screenWidth: '', // 屏幕宽
screenHeight: '',
xToTop: 0, // x方向距离顶部距离
scale: 1, // 缩放
preSrc: '',
preImgW: '',
preImgH: '',
x: 0,
y: 0,
oldx: 0,
oldy: 0,
isMove: false,
start: {
left: 0,
top: 0
}
};
},
computed: {},
onLoad(option) {
// 选择照片信息
let data = JSON.parse(decodeURIComponent(option.item));
const query = uni.createSelectorQuery();
query.select('.settingHeadImage').boundingClientRect();
query.exec(res => {
// 设置屏幕大小
this.screenWidth = res[0].width;
this.screenHeight = res[0].height;
// 设置图像基准值,图像基准值按屏幕宽度设置,两边留白各40
this.maxH = res[0].width - 80;
this.maxW = res[0].width - 80;
// 设置X轴值,算式:屏幕高度的一半减去基准框高度的一半
this.xToTop = this.screenHeight / 2 - this.maxH / 2;
this.setImageSize(data);
});
},
methods: {
// 宽高处理
setImageSize(data) {
const {
tempFilePath
} = data;
const {
maxH,
maxW
} = this;
uni.getImageInfo({
src: tempFilePath,
success: res => {
const {
errMsg,
path,
width,
height
} = res;
let w = '';
let h = '';
if (errMsg === 'getImageInfo:ok') {
w = width;
h = height;
// 宽大与高大于最大宽度
if (width > height && width > maxW) {
w = Math.floor((width / height) * maxW);
h = maxH;
}
// 高大于宽大于最大高度
if (height > width && height > maxH) {
h = Math.floor((height / width) * maxH);
w = maxW;
}
// 宽高相等或者宽高小于最大值
if (width === height || (width < maxW && height < maxH)) {
h = maxH;
w = maxW;
}
this.preSrc = path;
this.preImgH = h;
this.preImgW = w;
// 设置蒙层
this.setBgBox();
// 图像预览
this.previewCanvas({
w,
h,
path
});
}
}
});
},
// 设置蒙层
setBgBox() {
const {
maxW,
maxH,
screenHeight,
screenWidth,
xToTop
} = this;
const ctx = uni.createCanvasContext('firstCanvas');
// 先清除矩形
ctx.clearRect(0, 0, screenWidth, screenHeight);
// 设置canvas透明度
ctx.setGlobalAlpha(0.7);
// 设置蒙层颜色
ctx.setFillStyle('#000000');
// 绘制蒙层
ctx.fillRect(0, 0, screenWidth, screenHeight);
// 基准框留白
ctx.clearRect(40, xToTop, maxW, maxH);
// 绘制基准框
ctx.beginPath();
ctx.setStrokeStyle('#FFFFFF');
ctx.strokeRect(40, xToTop, maxW, maxH);
ctx.closePath();
ctx.draw();
},
// 预览
previewCanvas({
w,
h,
path
}) {
const ctx = uni.createCanvasContext('mycanvas');
ctx.drawImage(path, 0, 0, w, h);
ctx.draw();
},
onTouchstart(ev) {
if (ev.changedTouches.length === 1) {
this.isMove = true;
this.start.left = ev.changedTouches[0].clientX;
this.start.top = ev.changedTouches[0].clientY;
}
},
onTouchmove(ev) {
const {
maxW,
maxH,
preImgH,
preImgW,
xToTop
} = this;
if (this.isMove && ev.changedTouches.length === 1) {
let clientX = ev.changedTouches[0].clientX,
clientY = ev.changedTouches[0].clientY;
this.x = preImgW <= maxW ? 0 : this.oldx + clientX - this.start.left;
this.y = preImgH <= maxH ? 0 : this.oldy + clientY - this.start.top;
}
},
touchE(val) {
const {
preImgH,
preImgW,
maxH,
maxW
} = this;
const query = uni.createSelectorQuery();
const fx = this.x;
query.select('.pre-i').boundingClientRect();
query.exec(res => {
// x、y回弹计算
let y = (res[0].height - res[0].width) / 2;
/**
* 判断照片可移动的距离是否大于留白的值,如果大于向右划时图片的宽减去基准框的宽减去留白向左时留白,小于时按图片的可移动值
* */
let x = (preImgW - maxW) / 2 > 40 ? (fx < 0 ? preImgW - maxW - 40 : 40) : (preImgW - maxW) / 2;
if (preImgH > maxH) {
this.y = this.y > y ? y : this.y < -y ? -y : this.y;
}
if (preImgW > maxW) {
this.x = this.x > x ? x : this.x < -x ? -x : this.x;
}
this.oldx = this.x;
this.oldy = this.y;
this.isMove = false;
});
},
// 裁剪
onCrop() {
let y = 0;
let x = 0;
const query = uni.createSelectorQuery();
query.select('.pre-i').boundingClientRect();
query.exec(res => {
// 获取预览img距离左上的距离
y = Math.abs(res[0].top);
x = Math.abs(res[0].left);
const {
maxW,
maxH,
preImgH,
preImgW,
xToTop
} = this;
uni.canvasToTempFilePath({
x: Math.abs(res[0].left < 0 ? x + 40 : x - 40),
y: Math.abs(res[0].top < 0 ? xToTop + y : xToTop - y),
width: maxW,
height: maxH,
destWidth: maxW,
destHeight: maxH,
canvasId: 'mycanvas',
success: fileRes => {
console.log(fileRes);
uni.previewImage({
count: 1,
urls: [fileRes.tempFilePath]
});
},
fail: function(err) {
console.log(err);
uni.showToast({
title: '上传失败:图片生成过程中遇到错误',
icon: 'none'
});
}
});
});
}
}
};
</script>
<style lang="scss" scoped>
.settingHeadImage {
background-color: #000000;
overflow: hidden;
.pre-canvas {
position: fixed;
top: 0;
left: 0;
z-index: 20;
}
.preImage {
min-width: 100vw;
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
overflow: hidden;
z-index: 1;
.pre-i {
// transition: all 0.1s;
}
}
.setting-btns {
position: fixed;
bottom: 0;
left: 0;
z-index: 20;
font-size: 14px;
color: #ffffff;
}
}
</style>