You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
483 lines
12 KiB
483 lines
12 KiB
import words from "@config/words";
|
|
import {
|
|
wxLogin,
|
|
wxChooseImage,
|
|
wxUploadFile,
|
|
wxRequestPost,
|
|
} from "@utils/promise-wx-api";
|
|
import promiseWxApi from "@utils/promise-wx-api";
|
|
|
|
import nextTick from "@npm/dai-mp/tools/nextTick.js";
|
|
import translateStyle from "@npm/dai-mp/tools/translateStyle.js";
|
|
import computedBehavior from "@npm/dai-mp/mixins/computed-component/index.js";
|
|
|
|
const app = getApp();
|
|
const systemInfo = wx.getSystemInfoSync();
|
|
const windowWidth = systemInfo.windowWidth;
|
|
const dpr = systemInfo.pixelRatio > 2 ? 2 : systemInfo.pixelRatio;
|
|
let showImageWidth = (600 / 750) * windowWidth;
|
|
let showImageHeight = (600 / 750) * windowWidth;
|
|
|
|
Component({
|
|
behaviors: [computedBehavior],
|
|
properties: {
|
|
src: {
|
|
type: String,
|
|
value: "",
|
|
},
|
|
// 以rpx为单位,最多750
|
|
width: {
|
|
type: Number,
|
|
value: 600,
|
|
},
|
|
height: {
|
|
type: Number,
|
|
value: 600,
|
|
},
|
|
showCircle: {
|
|
type: Boolean,
|
|
value: false,
|
|
},
|
|
uploadUrl: {
|
|
type: String,
|
|
value: "oss/file/uploadimg",
|
|
},
|
|
},
|
|
|
|
data: {
|
|
dpr,
|
|
|
|
avatar: {
|
|
tempFilePath: "",
|
|
x: 0,
|
|
y: 0,
|
|
width: 0,
|
|
height: 0,
|
|
rotate: 0, // 90 180 270
|
|
zoom: 1, // 1-10
|
|
srcWidth: 0,
|
|
srcHeight: 0,
|
|
},
|
|
|
|
// 单点触摸移动
|
|
singleTouchData: {
|
|
isActived: false,
|
|
srcX: 0,
|
|
srcY: 0,
|
|
x: 0,
|
|
y: 0,
|
|
},
|
|
|
|
// 单点触摸移动
|
|
doubleTouchData: {
|
|
isActived: false,
|
|
srcDistance: 0,
|
|
distance: 0,
|
|
},
|
|
|
|
fmData: {
|
|
wx_form_id: "",
|
|
avatar_url: "",
|
|
},
|
|
},
|
|
|
|
computed: {
|
|
imgStyle(data) {
|
|
const {
|
|
x,
|
|
y,
|
|
width,
|
|
height,
|
|
zoom,
|
|
rotate,
|
|
srcWidth,
|
|
srcHeight,
|
|
} = data.avatar;
|
|
|
|
const ratio = srcWidth / width;
|
|
const afterX = x - (srcWidth - width) / 2;
|
|
const afterY = y - (srcHeight - height) / 2;
|
|
const afterZoom = Math.round((10000 * zoom) / ratio) / 10000;
|
|
|
|
let styleObj = {
|
|
width: `${srcWidth}px`,
|
|
height: `${srcHeight}px`,
|
|
transform: `translate3d(${afterX}px, ${afterY}px, 0) rotate(${rotate}deg) scale(${afterZoom})`,
|
|
};
|
|
|
|
return translateStyle(styleObj);
|
|
},
|
|
},
|
|
|
|
watch: {},
|
|
|
|
lifetimes: {
|
|
attached() {
|
|
this.init();
|
|
},
|
|
},
|
|
|
|
methods: {
|
|
async init() {
|
|
const { src, width, height } = this.data;
|
|
|
|
// 根据配置更新图片默认高度
|
|
showImageWidth = (width / 750) * windowWidth;
|
|
showImageHeight = (height / 750) * windowWidth;
|
|
|
|
// 初始化默认图片
|
|
if (src) {
|
|
console.log("初始化截图", src);
|
|
this.iniHttpImage(src);
|
|
} else {
|
|
this.handleTapReselectBtn();
|
|
}
|
|
},
|
|
|
|
async selectOneImage() {
|
|
const {
|
|
data: { tempFilePaths },
|
|
} = await wxChooseImage({
|
|
count: 1,
|
|
sizeType: ["compressed"],
|
|
});
|
|
return tempFilePaths[0];
|
|
},
|
|
|
|
// 获取canvas对象
|
|
async getCanvas(id = "#myCanvas") {
|
|
return new Promise((reslove) => {
|
|
this.createSelectorQuery()
|
|
.select(id)
|
|
.fields({ node: true, size: true })
|
|
.exec((res) => {
|
|
reslove(res[0].node);
|
|
});
|
|
});
|
|
},
|
|
|
|
// 或许canvas创建的图片对象
|
|
async getCanvasImg(canvas, src) {
|
|
const img = canvas.createImage();
|
|
return new Promise((reslove) => {
|
|
img.src = src;
|
|
img.onload = () => {
|
|
reslove(img);
|
|
};
|
|
});
|
|
},
|
|
|
|
async createImage() {
|
|
const { avatar } = this.data;
|
|
const { x, y, width, height, zoom, rotate } = avatar;
|
|
const destWidth = 600;
|
|
const destHeight = 600 * (showImageHeight / showImageWidth);
|
|
|
|
const canvas = await this.getCanvas();
|
|
// 不赋值就会显示默认宽高。。
|
|
(canvas.width = showImageWidth * dpr),
|
|
(canvas.height = showImageHeight * dpr);
|
|
|
|
const ctx = canvas.getContext("2d");
|
|
ctx.scale(dpr, dpr);
|
|
|
|
const img = await this.getCanvasImg(canvas, avatar.tempFilePath);
|
|
|
|
// console.log(
|
|
// x - ((zoom - 1) * width) / 2,
|
|
// y - ((zoom - 1) * height) / 2,
|
|
// width * zoom,
|
|
// height * zoom,
|
|
// canvas.width,
|
|
// canvas.height
|
|
// );
|
|
|
|
ctx.drawImage(
|
|
img,
|
|
//这里的位置处理很奇怪吧,没办法,显示用的是css3中的transfrom,它的原理是居中缩放
|
|
x - ((zoom - 1) * width) / 2,
|
|
y - ((zoom - 1) * height) / 2,
|
|
width * zoom,
|
|
height * zoom
|
|
);
|
|
|
|
// ctx.restore();
|
|
|
|
await nextTick(1000);
|
|
|
|
const {
|
|
data: { tempFilePath },
|
|
} = await promiseWxApi("canvasToTempFilePath")({
|
|
canvas,
|
|
});
|
|
|
|
ctx.clearRect(0, 0, showImageWidth * dpr, showImageHeight * dpr);
|
|
ctx.scale(1 / dpr, 1 / dpr);
|
|
|
|
// wx.previewImage({
|
|
// urls: [tempFilePath],
|
|
// });
|
|
return tempFilePath;
|
|
},
|
|
|
|
// 旋转图片
|
|
async rotateImg() {
|
|
const { avatar } = this.data;
|
|
if (!avatar.tempFilePath) return;
|
|
|
|
wx.showLoading();
|
|
|
|
const canvas = await this.getCanvas("#rotateCanvas");
|
|
let { srcWidth, srcHeight } = avatar;
|
|
|
|
// 不赋值就会显示默认宽高。。
|
|
(canvas.width = srcHeight), (canvas.height = srcWidth);
|
|
|
|
const ctx = canvas.getContext("2d");
|
|
const img = await this.getCanvasImg(canvas, avatar.tempFilePath);
|
|
|
|
// 旋转90度
|
|
ctx.translate(srcHeight, 0);
|
|
ctx.rotate((Math.PI * 90) / 180);
|
|
|
|
ctx.drawImage(img, 0, 0, srcWidth, srcHeight);
|
|
|
|
// ctx.restore();
|
|
|
|
await nextTick(1000);
|
|
|
|
const {
|
|
data: { tempFilePath },
|
|
} = await promiseWxApi("canvasToTempFilePath")({
|
|
canvas,
|
|
});
|
|
|
|
ctx.clearRect(0, 0, srcWidth, srcHeight);
|
|
ctx.rotate((Math.PI * -90) / 180);
|
|
ctx.translate(-srcHeight, 0);
|
|
|
|
console.log(tempFilePath);
|
|
wx.hideLoading();
|
|
|
|
this.reselectImage(tempFilePath);
|
|
},
|
|
|
|
async reselectImage(argSrcImage) {
|
|
const srcImage = argSrcImage || (await this.selectOneImage());
|
|
const {
|
|
data: { width, height },
|
|
} = await promiseWxApi("getImageInfo")({ src: srcImage });
|
|
const ratio = width / height;
|
|
const defaultRatio = showImageWidth / showImageHeight;
|
|
|
|
let avatarWidth, avatarHeight;
|
|
if (ratio > defaultRatio) {
|
|
// 图片更宽
|
|
avatarWidth = showImageHeight * ratio;
|
|
avatarHeight = showImageHeight;
|
|
} else {
|
|
avatarWidth = showImageWidth;
|
|
avatarHeight = showImageWidth / ratio;
|
|
}
|
|
|
|
let avatarX = 0,
|
|
avatarY = 0;
|
|
if (ratio > defaultRatio) {
|
|
// 图片更宽
|
|
avatarX = (showImageWidth - avatarWidth) / 2;
|
|
} else {
|
|
avatarY = (showImageHeight - avatarHeight) / 2;
|
|
}
|
|
|
|
let { avatar } = this.data;
|
|
|
|
avatar.tempFilePath = srcImage;
|
|
avatar.width = avatarWidth;
|
|
avatar.height = avatarHeight;
|
|
|
|
avatar.x = avatarX;
|
|
avatar.y = avatarY;
|
|
avatar.zoom = 1;
|
|
avatar.rotate = 0;
|
|
|
|
// 太大的图片容易崩溃
|
|
let srcWidth = width;
|
|
let srcHeight = height;
|
|
if (srcWidth > 1200) {
|
|
srcHeight = (1200 * srcHeight) / srcWidth;
|
|
srcWidth = 1200;
|
|
}
|
|
avatar.srcWidth = srcWidth;
|
|
avatar.srcHeight = srcHeight;
|
|
|
|
this.setData({ avatar });
|
|
},
|
|
|
|
handleTapReselectBtn() {
|
|
this.reselectImage();
|
|
},
|
|
|
|
handelTouchstartCropper(e) {
|
|
const { touches } = e;
|
|
const { singleTouchData, doubleTouchData } = this.data;
|
|
|
|
if (touches.length === 1) {
|
|
// 单点位移
|
|
const { clientX, clientY } = touches[0];
|
|
singleTouchData.isActived = true;
|
|
singleTouchData.srcX = clientX;
|
|
singleTouchData.srcY = clientY;
|
|
singleTouchData.x = clientX;
|
|
singleTouchData.y = clientY;
|
|
} else if (touches.length > 1) {
|
|
// 两点缩放
|
|
doubleTouchData.isActived = true;
|
|
const t1 = touches[0];
|
|
const t2 = touches[1];
|
|
// 两指间距离
|
|
const distance = parseInt(
|
|
Math.sqrt(
|
|
Math.pow(t1.clientX - t2.clientX, 2) +
|
|
Math.pow(t1.clientY - t2.clientY, 2)
|
|
)
|
|
);
|
|
doubleTouchData.srcDistance = distance;
|
|
doubleTouchData.distance = distance;
|
|
}
|
|
this.setData({
|
|
doubleTouchData,
|
|
singleTouchData,
|
|
});
|
|
},
|
|
|
|
handelTouchendCropper() {
|
|
const { singleTouchData, doubleTouchData } = this.data;
|
|
|
|
singleTouchData.isActived = false;
|
|
doubleTouchData.isActived = false;
|
|
this.setData({
|
|
doubleTouchData,
|
|
singleTouchData,
|
|
});
|
|
},
|
|
|
|
handelTouchmoveCropper(e) {
|
|
const { touches } = e;
|
|
const { avatar, singleTouchData, doubleTouchData } = this.data;
|
|
|
|
if (touches.length === 1) {
|
|
// 单点位移
|
|
const { clientX, clientY } = touches[0];
|
|
if (singleTouchData.isActived) {
|
|
// 边界
|
|
const { zoom, width, height } = avatar;
|
|
const maxX = ((zoom - 1) * width) / 2,
|
|
minX = showImageWidth - width - maxX,
|
|
maxY = ((zoom - 1) * height) / 2,
|
|
minY = showImageHeight - height - maxY;
|
|
let { x, y } = avatar;
|
|
x += clientX - singleTouchData.x;
|
|
y += clientY - singleTouchData.y;
|
|
x = Math.min(x, maxX);
|
|
x = Math.max(x, minX);
|
|
y = Math.min(y, maxY);
|
|
y = Math.max(y, minY);
|
|
|
|
avatar.x = x;
|
|
avatar.y = y;
|
|
singleTouchData.x = clientX;
|
|
singleTouchData.y = clientY;
|
|
}
|
|
} else if (touches.length > 1) {
|
|
// 两点缩放
|
|
doubleTouchData.isActived = true;
|
|
const t1 = touches[0];
|
|
const t2 = touches[1];
|
|
if (doubleTouchData.isActived) {
|
|
// 两指间距离
|
|
const distance = parseInt(
|
|
Math.sqrt(
|
|
Math.pow(t1.clientX - t2.clientX, 2) +
|
|
Math.pow(t1.clientY - t2.clientY, 2)
|
|
)
|
|
);
|
|
// 放大比例
|
|
let { zoom } = avatar;
|
|
zoom += ((distance - doubleTouchData.distance) * 2) / windowWidth;
|
|
zoom = Math.max(zoom, 1);
|
|
zoom = Math.min(zoom, 8);
|
|
|
|
// 边界
|
|
const { width, height } = avatar;
|
|
const maxX = ((zoom - 1) * width) / 2,
|
|
minX = showImageWidth - width - maxX,
|
|
maxY = ((zoom - 1) * height) / 2,
|
|
minY = showImageHeight - height - maxY;
|
|
let { x, y } = avatar;
|
|
x = Math.min(x, maxX);
|
|
x = Math.max(x, minX);
|
|
y = Math.min(y, maxY);
|
|
y = Math.max(y, minY);
|
|
|
|
avatar.x = x;
|
|
avatar.y = y;
|
|
avatar.zoom = zoom;
|
|
doubleTouchData.srcDistance = distance;
|
|
doubleTouchData.distance = distance;
|
|
}
|
|
}
|
|
this.setData({ avatar, doubleTouchData, singleTouchData });
|
|
},
|
|
|
|
// 提交信息
|
|
async submit(e) {
|
|
const { avatar, uploadUrl } = this.data;
|
|
|
|
if (avatar.tempFilePath == "") {
|
|
return wx.showToast({
|
|
title: "请先选择图片",
|
|
icon: "none",
|
|
duration: 1500,
|
|
});
|
|
}
|
|
|
|
wx.showLoading({
|
|
title: "保存中…",
|
|
mask: true,
|
|
});
|
|
|
|
// 生成头像图片本地地址
|
|
const avatarTempUrl = await this.createImage();
|
|
console.log("剪裁成功图片临时地址", avatarTempUrl);
|
|
|
|
const {
|
|
data: {
|
|
data: { code, data },
|
|
},
|
|
msg,
|
|
} = await wxUploadFile(uploadUrl, avatarTempUrl, {
|
|
// isMock: true
|
|
});
|
|
|
|
wx.hideLoading();
|
|
|
|
if (msg === "success" && code === 0) {
|
|
console.log(data);
|
|
this.triggerEvent("cropSuccess", { ...data });
|
|
}
|
|
},
|
|
|
|
// 初始化网络图片为默认图片
|
|
async iniHttpImage(src) {
|
|
const {
|
|
msg,
|
|
data: { tempFilePath },
|
|
} = await promiseWxApi("downloadFile")({
|
|
url: src,
|
|
});
|
|
if (msg == "success") {
|
|
this.reselectImage(tempFilePath);
|
|
}
|
|
},
|
|
},
|
|
});
|
|
|