赞
踩
最近项目中有一个下载图片和一个分享海报的功能,遇到了一些问题,比如海报图片生成失败、图片下载是空白的、多张图片下载时下载的还是前一张图片等等。觉得过程挺有意思的,记录一下。
- <view class="canvas-container">
- <canvas
- :style="{ width: canvasW + 'px', height: canvasH + 'px' }"
- canvas-id="myCanvas"
- id="myCanvas01"
- >
- </canvas>
- </view>
- <view class="convas-image">
- <canvas :style="{ width: canvasW + 'px',height: canvasH + 'px'}" canvasid="myCanvas2" id="myCanvas02">
- </canvas>
- </view>
由于要做移动端适配,故容器的宽高是动态的。
- .canvas-container {
- position: fixed;
- top: 9999999px;
- #myCanvas01 {
- background: rgba(255, 255, 255, 0); /*关键点*/
- }
- }
- .convas-image {
- position: fixed;
- top: 9999999px;
- #myCanvas02 {
- background: rgba(255, 255, 255, 0); /*关键点*/
- }
- }
因为这两个容器实际上都是不显示在页面上的,如果使用hidden或者v-if隐藏的话多多少少都会有些问题,所以使用固定定位使容器脱离文档流。
- // 获取设备信息
- getSystemInfo() {
- return new Promise((req, rej) => {
- uni.getSystemInfo({
- success: function (res) {
- req(res);
- },
- });
- });
- },
- getImageInfo(image) {
- console.log(`output->image`, image);
- return new Promise((req, rej) => {
- uni.getImageInfo({
- src: image,
- success: function (res) {
- req(res);
- },
- fail: (err) => {
- uni.showToast({
- icon: "error",
- mask: true,
- title: "获取照片失败",
- });
- console.log("****", err);
- },
- });
- });
- },
获取设备信息及辅助函数,因为canvas中使用的图片为网络方式返回,此函数确保图片被正常读取后再绘制,后文有用到。
- async canvas2dFun(retryCount = 3) {
- this.SystemInfo = await this.getSystemInfo();
- const w = this.SystemInfo.windowWidth / 750;
- let goodsImgPath = await this.getImageInfo(this.imgObj.urls[0]);//海报主图
- let ewmImgPath = await this.getImageInfo(this.imgObj.qrCode);//海报二维码
- let posterPath = await this.getImageInfo(
- `${this.imgpath}/static_pro/create/poster.png`
- );//海报边框
- let logoPath = await this.getImageInfo(
- `${this.imgpath}/static_pro/create/logo.png`
- );//logo
-
- this.canvasW = 710 * w;
- this.canvasH = 1124 * w;
-
- if (this.SystemInfo.errMsg == "getSystemInfo:ok") {
- console.log("读取图片信息成功");
- var ctx = uni.createCanvasContext("myCanvas", this);
- // 1.填充背景色,白色
- // 设置画布透明度
- ctx.setFillStyle("rgba(255, 255, 255, 1)"); // 默认白色
- ctx.fillRect(0, 0, this.canvasW, this.canvasH); // fillRect(x,y,宽度,高度)
-
- ctx.drawImage(
- goodsImgPath.path,
- 5 * w,
- 5 * w,
- this.canvasW - 15 * w,
- this.canvasH - 15 * w
- ); // drawImage(图片路径,x,y,绘制图像的宽度,绘制图像的高度)
- ctx.drawImage(posterPath.path, 0, 0, this.canvasW, this.canvasH); // drawImage(图片路径,x,y,绘制图像的宽度,绘制图像的高度,二维码的宽,高)
- ctx.drawImage(
- logoPath.path,
- this.canvasW - this.ewmW * w - 460 * w,
- this.canvasH - this.ewmW * w - 100 * w,
- this.logoW * w - 30 * w,
- this.logoH * w - 30 * w
- ); //logo
- ctx.drawImage(
- ewmImgPath.path,
- this.canvasW - this.ewmW * w - 460 * w,
- this.canvasH - this.ewmW * w - 16 * w,
- this.ewmW * w - 30 * w,
- this.ewmW * w - 30 * w
- ); // drawImage(图片路径,x,y,绘制图像的宽度,绘制图像的高度,二维码的宽,高)
- ctx.font = "bold 18px Arial";
- ctx.fillStyle = "#222222";
- ctx.fillText(
- "微信扫码 XX相机",
- this.canvasW - this.ewmW * w - 280 * w,
- this.canvasH - this.ewmW * w + 50 * w
- );
- ctx.font = "normal 12px Arial";
- ctx.fillText(
- "这是我的写真,你也来试一试吧",
- this.canvasW - this.ewmW * w - 280 * w,
- this.canvasH - this.ewmW * w + 100 * w
- );
- // draw方法 把以上内容画到 canvas 中
- ctx.draw(true, (ret) => {
- uni.canvasToTempFilePath(
- {
- // 保存canvas为图片
- canvasId: "myCanvas",
- quality: 1,
- success: (res) => {
- console.log("生成海报-》", res);
- uni.setStorageSync("filePath", res.tempFilePath); // 保存临时文件路径到缓存
- },
- fail: (error) => {
- console.log("生成海报失败-》", error);
- if (retryCount > 0) {
- console.log(`重试剩余次数: ${retryCount - 1}`);
- this.canvas2dFun(retryCount - 1)
- }
- },
- },
- this
- );
- });
- } else {
- console.log("读取图片信息失败");
- }
- },
绘制完成之后将绘制的图片存入filePath临时路径中,方便后面下载等操作。
测试阶段发现,部分安卓机型在canvas第一次绘制时会报wx.canvasToTempFilePath:create bitmap failed,可能是不分机型的性能使得图形绘制失败,图片保存下来也是纯白色的底图。在这里fail回调处理了如果生成失败再生成一次的操作。
- shareFirendCircle() {
- let posterUrl = uni.getStorageSync("filePath");
- uni.showShareImageMenu({
- path: posterUrl, //图片地址必须为本地路径或者临时路径
- success: (re) => {
- console.log({ success: re });
- },
- fail: (re) => {
- console.log({ fail: re });
- },
- });
- },
页面按钮点击后调用函数唤起分享框,函数中的posterUrl为canvas绘制的图片。
- async canvas2dImage(retryCount = 3) {
- this.SystemInfo = await this.getSystemInfo();
- const w = this.SystemInfo.windowWidth / 750;
- let goodsImgPath = await this.getImageInfo(this.imgObj.urls[this.swiperIndex]);
- let logoPath = await this.getImageInfo(
- `${this.imgpath}/static_pro/create/logo.png`
- );
-
- this.canvasW = 710 * w;
- this.canvasH = 1124 * w;
-
- if (this.SystemInfo.errMsg == "getSystemInfo:ok") {
- console.log("读取图片信息成功");
- var ctx = uni.createCanvasContext("myCanvas2", this);
- // 1.填充背景色,白色
- // 设置画布透明度
- ctx.setFillStyle("rgba(255, 255, 255, 1)"); // 默认白色
- ctx.fillRect(0, 0, this.canvasW, this.canvasH); // fillRect(x,y,宽度,高度)
-
- ctx.drawImage(
- goodsImgPath.path,
- 5 * w,
- 5 * w,
- this.canvasW - 15 * w,
- this.canvasH - 15 * w
- ); // drawImage(图片路径,x,y,绘制图像的宽度,绘制图像的高度)
- ctx.drawImage(
- logoPath.path,
- this.canvasW - this.ewmW * w - 480 * w,
- this.canvasH - this.ewmW * w + 100 * w,
- this.logoW * w - 30 * w,
- this.logoH * w - 30 * w
- ); //logo
- // draw方法 把以上内容画到 canvas 中
- return new Promise((resolve, reject) => {
- ctx.draw(false, (ret) => {
- uni.canvasToTempFilePath(
- {
- // 保存canvas为图片
- canvasId: "myCanvas2",
- quality: 1,
- success: (res) => {
- console.log("生成高清图-》", res);
- uni.setStorageSync("ImagePath", res.tempFilePath); // 保存临时文件路径到缓存
- resolve();
- },
- fail: (error) => {
- console.log("生成高清图失败-》", error);
- if (retryCount > 0) {
- console.log(`重试剩余次数: ${retryCount - 1}`);
- this.canvas2dImage(retryCount - 1)
- .then(resolve)
- .catch(reject);
- } else {
- reject(error);
- }
- },
- },
- this
- );
- });
- });
- } else {
- console.log("读取图片信息失败");
- }
- },
这里使用Promise的原因是有多张图片需要单独下载,切换图片点击下载按钮开始绘制图像,确保在图像canvas生成完毕后继续下载的步骤。
canvas绘制方面与上面大差不差,主要是下载方面需要单独配置
- async dowloadImg() {
- if (!this.$utils.getUserInfo().isVip) {
- //无会员
- this.openPlusAlert = true;
- } else {
- uni.showLoading({
- title: "照片保存中",
- });
- await this.canvas2dImage();
- let that = this;
- // 获取用户是否开启 授权保存图片到相册。
- uni.getSetting({
- success(res) {
- console.log("已知权限", res);
- uni.hideLoading();
- // 如果没有授权
- if (!res.authSetting["scope.writePhotosAlbum"]) {
- // 则拉起授权窗口
- uni.authorize({
- scope: "scope.writePhotosAlbum",
- success() {
- that.saveImage();
- },
- fail(error) {
- //点击了拒绝授权后--就一直会进入失败回调函数--此时就可以在这里重新拉起授权窗口
- console.log("拒绝授权则拉起弹框", error);
- uni.showModal({
- title: "提示",
- content: "若点击不授权,将无法保存图片",
- cancelText: "不授权",
- cancelColor: "#999",
- confirmText: "授权",
- confirmColor: "#f94218",
- success(res) {
- console.log(res);
- if (res.confirm) {
- // 选择弹框内授权
- uni.openSetting({
- success(res) {
- console.log(res.authSetting);
- },
- });
- } else if (res.cancel) {
- // 选择弹框内 不授权
- console.log("用户点击不授权");
- }
- },
- });
- },
- });
- } else {
- // 有权限--直接保存
- console.log("有权限 直接调用相应方法");
- uni.hideLoading();
- that.saveImage();
- }
- },
- fail: (error) => {
- console.log(
- "调用微信的查取权限接口失败,并不知道有无权限!只有success调用成功才只知道有无权限",
- error
- );
- uni.hideLoading();
- uni.showToast({
- title: error.errMsg,
- icon: "none",
- duration: 1500,
- });
- },
- });
- }
- },
这里是点击下载绑定的函数,主要作用是判断是否开启了权限。
- saveImage() {
- let filePath = uni.getStorageSync("ImagePath"); //从缓存中读取临时文件路径
- wx.saveImageToPhotosAlbum({
- filePath: filePath,
- success(res) {
- uni.showToast({
- icon: "success",
- mask: true,
- title: "保存到相册了",
- });
- },
- fail(res) {
- console.log(res.errMsg);
- },
- });
- },
保存图片到本地的操作。中心思想还是把canvas生成的临时路径下载保存到本地。
这样就完成了上面轮播图切换时下载对应的图片,以及海报的分享与下载功能。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。