微信小程序 canvas生成海报图片模糊问题

时间:2024-02-25 10:27:01

一、制作正常显示海报,生成二倍海报隐藏 代码如下

<!--index.wxml-->
<view class="container">
    <view class="show">
      <image src="{{cardPath}}" alt="" class="card"></image>
      <text class="name">{{sendName}}</text>
      <image src="{{headPath}}" class="header"></image>
    </view>
    <view class="btn" bindtap="saveImage">保存图片</view>
    <view class="canvasBox" style="width:0;height:0;overflow: hidden;opacity:0;position:absolute;left:-750px;top:0;">
      <canvas canvas-id=\'myCanvas\' style=\'width:750px;height:1000px;\'></canvas>
    </view>
</view>

  

/*css*/
.btn {
  width: 300rpx;
  height: 90rpx;
  line-height: 90rpx;
  text-align: center;
  color: #fff;
  font-size: 38rpx;
  border-radius: 10rpx;
  background: #f9c22e;
}
/*  */
.show{
  width:750rpx;
  height:1000rpx;
  background: #fff;
  border:1px solid red;
  position: relative;
}
.show .card{
  display: block;
  width:690rpx;
  height: 940rpx;
  margin:20rpx auto 0;
}
.show .name,.show .header{
  position: absolute;
}
.show .name{
  width:100%;
  text-align: center;
  color:red;
  top:26rpx;
}
.show .header{
  width:100rpx;
  height: 100rpx;
  border-radius: 100%;
  top:500rpx;
  left:100rpx;
}

  

二、canvas 画二倍图  文字居中  实际展示正常海报为375*470 绘制中生成图模糊(文字有锯齿边,图片模糊)。绘制过程中绘制二倍图大小750*940 并保存,则能解决该问题

let x = ctx.width / 2;//canvas宽的一半
//画图
  drawCanvas: function () {
    let that = this;
    let ctx = wx.createCanvasContext(\'myCanvas\');
    let ctxW = 750;
    let ctxH = 1000;
    ctx.width = 750;
    ctx.height = 1000;
    let x = ctx.width / 2;

    // 垂直渐变
    const grd = ctx.createLinearGradient(0, 0, 0, ctxH);
    grd.addColorStop(0, \'#ffffff\');
    grd.addColorStop(1, \'#ffffff\');
    ctx.setFillStyle(grd);

    ctx.fillRect(0, 0, ctxW, ctxH);

    wx.getImageInfo({
      src: that.data.cardPath,
      success: (res) => {
        ctx.drawImage(res.path, 0, 0, 750, 1000); //card
        
        ctx.setFontSize(32) //字体大小
        ctx.setFillStyle(\'red\') //字体颜色
        ctx.textAlign = "center"; //文字居中
        ctx.fillText(that.data.sendName, x, 34)
        ctx.stroke();

        wx.getImageInfo({
          src: that.data.headPath,
          success: (res) => {
            ctx.save();
            ctx.beginPath(); //开始绘制
            ctx.arc(150,358,50, 0, 2 * Math.PI)
            ctx.fill()
            ctx.clip(); //剪切
            ctx.drawImage(res.path,100, 308, 100, 100); //userHeader  // 推进去图片必须是https
            ctx.restore(); //恢复之前保存的绘图上下文 继续绘制
            /**/ 
            ctx.save();
            ctx.draw();
          }
        })

      }
    })
  },

  三、下载保存图片  

// 保存图片
  saveImage: function (e) {
    wx.canvasToTempFilePath({
      x: 0, //指定的画布区域的左上角横坐标	
      y: 0, //指定的画布区域的左上角纵坐标	
      width: 750, //指定的画布区域的宽度
      height: 940, //指定的画布区域的高度
      destWidth: 750, //输出的图片的宽度
      destHeight: 940, //输出的图片的高度
      canvasId: \'myCanvas\',
      fileType: \'jpg\', //图片的质量,目前仅对 jpg 有效。取值范围为 (0, 1],不在范围内时当作 1.0 处理。
      quality: 1,
      success: function (res) {
        wx.saveImageToPhotosAlbum({
          filePath: res.tempFilePath,
          success(result) {
            wx.showToast({
              title: \'图片保存成功\',
              icon: \'success\',
              duration: 2000
            })
          }
        })
      }
    })
  },

 结果对比:左图文字明显有锯齿,右图没有,用户头像右图更清晰。保存下来看效果更明显。

  

tips:该文档解决两个问题

(1)文字居中显示

(2)生成图模糊

(3)圆形头像绘制,另一篇文档实际上线项目中手机端头像保存为空,此版本优化。

(4)页面加载完成绘制图形,实际项目中页面加载完成但接口数据不一定返回,所以绘图过程需在接口数据成功返回中调用,避免用户头像为空,绘图为空

(5)优化上一版本大量无用代码,全程使用线上数据代码

(6)canva绘制海报时可添加透明背景。

  
// 垂直渐变
    const grd = ctx.createLinearGradient(0, 0, 0, ctxH);
    grd.addColorStop(0, \'transparent\');
    grd.addColorStop(1, \'transparent\');
    ctx.setFillStyle(grd);

(7)手机端保存图片  当用户拒绝保存申请时下次点击按钮无法自动调用新的授权申请,点击按钮无反应

  此问题已解决网上有各种教程解决方法,当前随笔未更新,只记录图片模糊问题,下个随笔增加拒绝授权至重新授权过程

 

 

 

-------- 注意:鉴于有网友反映该功能无效,声明更新下记录,以下附上全部代码,请测试使用

tap1:该项目为18年开发,请选择低版本库,基础库版本太高会导致报错原因未知,但是报错不影响图片的保存;有知道原因的的兄弟姐妹可以留言回复,谢谢。

若出现以下错误请回调基础库(注该报错依然能保存图片)

 

tap2:(1)测试1:图片为百度域名下,因此需要忽略域名校验;开发工具可以保存,手机端保存空白。

(2)测试2:使用公司域名且小程序后台已配置dowload合法域名则不需要忽略域名校验(手机端测试需使用合法域名才能进行图片的保存)

 

tap3:此前百度地址图片没有了,打开为空,找不到图片导致保存为空

 

完整代码:

<!--index.wxml-->
<view class="container">
    <view class="show">
      <image src="{{cardPath}}" alt="" class="card"></image>
      <text class="name">{{sendName}}</text>
      <image src="{{headPath}}" class="header"></image>
    </view>
    <view class="btn" bindtap="saveImage">保存图片</view>
    <view class="canvasBox">
      <view class="canvasBox" style="width:0;height:0;overflow: hidden;opacity:0;position:absolute;left:-750px;top:0;">
        <canvas canvas-id=\'myCanvas\' style=\'width:750px;height:1000px;\'></canvas>
      </view>
    </view>
</view>

  

/*css*/

.btn {
  width: 300rpx;
  height: 90rpx;
  line-height: 90rpx;
  text-align: center;
  color: #fff;
  font-size: 38rpx;
  border-radius: 10rpx;
  background: #f9c22e;
}

/*  */

.show {
  width: 750rpx;
  height: 1000rpx;
  background: #fff;
  border: 1px solid red;
  position: relative;
}

.show .card {
  display: block;
  width: 690rpx;
  height: 940rpx;
  margin: 20rpx auto 0;
}

.show .name, .show .header {
  position: absolute;
}

.show .name {
  width: 100%;
  text-align: center;
  color: red;
  top: 26rpx;
}

.show .header {
  width: 100rpx;
  height: 100rpx;
  border-radius: 100%;
  top: 100rpx;
  left: 100rpx;
}

  

//index.js
const app = getApp()
Page({
  data: {
    cardPath: \'https://www.cnblogs.com/skins/mountainink/images/top.jpg\',
    headPath: \'https://www.cnblogs.com/skins/mountainink/images/top.jpg\',
    sendName: \'姓名姓名姓名\'
  },
  onLoad: function (options) {
    var that = this;
    that.drawCanvas();
  },
  /*画图*/
  drawCanvas: function () {
    let that = this;
    let ctx = wx.createCanvasContext(\'myCanvas\');
    let ctxW = 750;
    let ctxH = 940;
    ctx.width = 750;
    ctx.height = 940;
    let x = ctx.width / 2;

    // 垂直渐变
    const grd = ctx.createLinearGradient(0, 0, 0, ctxH);
    grd.addColorStop(0, \'#ffffff\');
    grd.addColorStop(1, \'#ffffff\');
    ctx.setFillStyle(grd);

    ctx.fillRect(0, 0, ctxW, ctxH);

    wx.getImageInfo({
      src: that.data.cardPath,
      success: (res) => {
        ctx.drawImage(res.path, 0, 0, 750, 940); //card
        
        ctx.setFontSize(32) //字体大小
        ctx.setFillStyle(\'red\') //字体颜色
        ctx.textAlign = "center"; //文字居中
        ctx.fillText(that.data.sendName, x, 34)
        ctx.stroke();

        wx.getImageInfo({
          src: that.data.headPath,
          success: (res) => {
            ctx.save();
            ctx.beginPath(); //开始绘制
            ctx.arc(150,358,50, 0, 2 * Math.PI)
            ctx.fill()
            ctx.clip(); //剪切
            ctx.drawImage(res.path,100, 308, 100, 100); //userHeader  // 推进去图片必须是https
            ctx.restore(); //恢复之前保存的绘图上下文 继续绘制
            /**/ 
            ctx.save();
            ctx.draw();
          }
        })

      }
    })
  },
  /* 保存图片 */
  saveImage: function (e) {
    wx.canvasToTempFilePath({
      x: 0, //指定的画布区域的左上角横坐标	
      y: 0, //指定的画布区域的左上角纵坐标	
      width: 750, //指定的画布区域的宽度
      height: 940, //指定的画布区域的高度
      destWidth: 750, //输出的图片的宽度 
      destHeight: 940, //输出的图片的高度
      canvasId: \'myCanvas\',
      fileType: \'jpg\', //图片的质量,目前仅对 jpg 有效。取值范围为 (0, 1],不在范围内时当作 1.0 处理。
      quality: 1,
      success: function (res) {
        wx.saveImageToPhotosAlbum({
          filePath: res.tempFilePath,
          success(result) {
            wx.showToast({
              title: \'图片保存成功\',
              icon: \'success\',
              duration: 2000
            })
          }
        })
      }
    })
  },
  
})

  以下appid使用xxxx代码,请使用自己的appid,不是自己的appid没有权限登陆。

{
	"description": "项目配置文件。",
	"packOptions": {
		"ignore": []
	},
	"setting": {
		"urlCheck": false,
		"es6": true,
		"enhance": false,
		"postcss": true,
		"preloadBackgroundData": false,
		"minified": true,
		"newFeature": true,
		"coverView": true,
		"nodeModules": false,
		"autoAudits": false,
		"showShadowRootInWxmlPanel": true,
		"scopeDataCheck": false,
		"uglifyFileName": false,
		"checkInvalidKey": true,
		"checkSiteMap": true,
		"uploadWithSourceMap": true,
		"compileHotReLoad": false,
		"babelSetting": {
			"ignore": [],
			"disablePlugins": [],
			"outputPath": ""
		},
		"useIsolateContext": true,
		"useCompilerModule": false,
		"userConfirmedUseCompilerModuleSwitch": false
	},
	"compileType": "miniprogram",
	"libVersion": "2.15.0",
	"appid": "XXXXXX",
	"projectname": "canvas",
	"isGameTourist": false,
	"simulatorType": "wechat",
	"simulatorPluginLibVersion": {},
	"condition": {
		"search": {
			"current": -1,
			"list": []
		},
		"conversation": {
			"current": -1,
			"list": []
		},
		"game": {
			"currentL": -1,
			"list": []
		},
		"miniprogram": {
			"current": -1,
			"list": []
		}
	}
}