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); } }, }, });