uniapp微信小程序canvas生成简单海报并下载

今天项目接到个任务,就是手写canvas海报,并能下载图片。百度了很多海报组件,都不尽人意。萌生了自己手写海报的想法。

话不多说先贴文档 uni-app官网

其实该文档和微信小程序类似

最终效果,微信小程序可用,一个一个代码敲出来的。只要把原理掌握了,任何海报都能迎刃而解

1.准备环境

因为海报涉及到第三方的图片存储地址,所以要加白名单

downloadFile 里加你海报的根域名

 记住一定要在这看看有没有加好白名单,刷新一下要不然还是会加载中空白的效果

话不多说上代码

解读代码块1

// 加微信头像
					context.save()  // 对当前画布区域进行保存
					context.beginPath()
					context.arc(185, 160, 50, 0, 2 * Math.PI)
					context.clip()
					let touImg = await this.getImageInfo(this.touimg)
					context.drawImage(touImg.tempFilePath, 135, 110)					 
					context.restore()  //对其进行恢复

 如果想要让将图片转为圆,并且居中,context.arc(185, 160, 50, 0, 2 * Math.PI)这个方法是关键

因为画布为370px,所以我的X轴坐标 一定为185,Y轴的话看需求但是一定要大于半径(50px)。

我的圆半径为50,想象成一个正方形,它的右上角点的X轴位置就是185-50=185,Y轴160-50=110所以context.drawImage(touImg.tempFilePath, 135, 110)    就得这样写

解读代码块2

// 加活动标题
					context.beginPath()
					context.setFontSize(25)
					context.setFillStyle('#ffffff')
					context.setTextAlign('center')
					context.fillText(this.title, 185,80)

如果想让字体居中显示,你的字体偏移量记得也得偏移到中间

 话不多说上代码

<template>
	<view>
		<button type="default" @click="show">生成</button>
		<canvas style="width: 370px; height: 580px;" canvas-id="firstCanvas" id="firstCanvas" v-if="isshow"></canvas>
		<button type="default" @click="saveImage">下载</button>
	</view>
</template>

<script>

export default {
	components:{
		 
	},
	data() {
		return {
			
			ctx:null,
			isshow:false,
			title:"草莓千层蛋糕",
			touimg:"https://thirdwx.qlogo.cn/mmopen/vi_32/rmPJibkdEnAe9lss490vrCuGTS4X6lhY63XsbBOodXzTGVtwHmTyolbJfOVUq8cNUpzkOugqGqrzCxEhYUTGu1Q/132",
			bg:"https://ss2.meipian.me/users/49976449/1a3553b0-affa-11ec-b874-df8ffd888308.png"
		}
	},
	mounted() {
		// this.show()
	},
	
		methods: {
			async show(){
				this.isshow=true
				uni.showLoading({
				    title: '加载中...',
					mask: true
				})
				var context = uni.createCanvasContext('firstCanvas')
				
					// 加背景颜色
					context.save()  // 对当前画布区域进行保存 
					context.rect(0, 0,300, 200)  // begin path
					context.setFillStyle('red')  //canvas背景颜色
					context.fill()				 //	填充块的合成
					context.restore()  //对其进行恢复
					
					// 加背景图片
					context.save()  // 对当前画布区域进行保存
					context.beginPath()					 
					let bgImg = await this.getImageInfo(this.bg)
					context.drawImage(bgImg.tempFilePath, 0, 0)					 
					context.restore()  //对其进行恢复
					
					// 加活动标题
					context.beginPath()
					context.setFontSize(25)
					context.setFillStyle('#ffffff')
					context.setTextAlign('center')
					context.fillText(this.title, 185,80)
					
					// 加微信头像
					context.save()     // 对当前画布区域进行保存
					context.beginPath() //开始创建一个路径
					context.arc(185, 160, 50, 0, 2 * Math.PI)  //画一条弧线,2 * Math.PI 就是正圆
					context.clip()  //从原始画布中剪切任意形状和尺寸
					let touImg = await this.getImageInfo(this.touimg) //获取图片信息
					context.drawImage(touImg.tempFilePath, 135, 110)  //将图片临时路径放入canvas				 
					context.restore()  //对其进行恢复
					
					// 加微信昵称
					context.beginPath()
					context.setFontSize(18)
					context.setTextAlign('center')
					context.fillText('三线码工', 185, 250)
					
					// 加二维码图片(CSDN不然显示二维码,我就用头像的地址模拟二维码,到时候换个URL就OK了)
					context.save()  // 对当前画布区域进行保存					
					let ewmImg = await this.getImageInfo(this.touimg)
					context.drawImage(ewmImg.tempFilePath, 100, 370,170,170)					 
					context.restore()  //对其进行恢复
					
					// context.draw()
					setTimeout(()=>{ 
						context.draw(true,()=>{  // 因为有图片下载,我一般延迟0.5秒
							uni.hideLoading()
						})
					},500)
					 
			},
			//获取图片
			 getImageInfo(imgSrc){
				return new Promise((resolve, reject) => {
					uni.downloadFile({
						url: imgSrc,
						success: function (res) {
							resolve(res);
							console.log('获取图片成功',res) 
						},
						fail: (err) => {
							reject(err);
							console.log('获取图片失败',err)
						}
					})
				});
			},
			//保存图片到相册
			saveImage(){
				//判断用户授权
				uni.getSetting({
				   success(res) {
				      console.log('获取用户权限',res.authSetting)
					  if(Object.keys(res.authSetting).length>0){
						  //判断是否有相册权限
						  if(res.authSetting['scope.writePhotosAlbum']==undefined){
							  //打开设置权限
							  uni.openSetting({
							    success(res) {
							      console.log('设置权限',res.authSetting)
							    }
							  })
						  }else{
							  if(!res.authSetting['scope.writePhotosAlbum']){
								  //打开设置权限
								  uni.openSetting({
								    success(res) {
								      console.log('设置权限',res.authSetting)
								    }
								  })
							  }
						  }
					  }else{
						  return
					  }
				   }
				})
				var that = this
				uni.canvasToTempFilePath({
					canvasId: 'firstCanvas',
					quality: 1,
					complete: (res) => {
						console.log('保存到相册',res);
						uni.saveImageToPhotosAlbum({
							filePath: res.tempFilePath,
							success(res) {
								uni.showToast({
									title: '已保存到相册',
									icon: 'success',
									duration: 2000
								})
								setTimeout(()=>{
									that.isShow = false
								},2000)
							}
						})
					}
				},this);
			}
		}
}
</script>

<style scoped>

</style>

第二次更新,升级版,适应当前手机屏幕,夹杂其他代码仅供参考

<template>
  <view class="pages" :style="{ 'backgroundImage': `url(${imgurl})` }">

    <view class="fixed">
      <button open-type="share" plain=none class="sharebutton">
        <image src="db3.png" mode="widthFix">
        </image>
      </button>
    </view>

    <view class="button">
      <view class="but" @click="showposter"> 点击生成海报</view>
    </view>
    <view v-if="isshow" class="model">
      <view class="bg" @click="isshow = false"></view>
      <view :style="{ 'width': `${width}px`, 'height': `${height}px` }" class="poster">
        <canvas :style="{ 'width': `${width}px`, 'height': `${height}px` }" canvas-id="firstCanvas" id="firstCanvas"
          v-if="isshow"></canvas>
      </view>
      <view class="save" @click="save"> 保存至相册转发朋友圈</view>
    </view>
  </view>
</template>

<script>
import { getCurrentUser } from "@/api/App";
import { getSharePost } from "../api";
import { base_url } from "@/common/togo.js";
export default {
  data() {
    return {
      show: false,
      imgurl: "",
      ctx: null,
      isshow: false,
      name: "",
      bankName: "",
      width: "370",
      height: 580
    };
  },
  onLoad(option) {
    this.activeId = option.activeId
    this.sharePost(false)
  },
 
  created() {
    this.getinfo()
  },
onShareAppMessage() {

    var list = ["千万消费券派送中,快来拼手气领取!", "您有消费券待领取,戳这里>>", "↓↓千万消费券已备好,等你哟↓↓您有一张消费券待领取,速来>"]
    var sjs = Math.floor(Math.random() * Math.floor(3));
    this.shareUrl = `active0601/invited/getInvited?activityId=${this.activeId}&shareUnionId=${this.$store.state.app.selfUnionId}&shareType=0`;
    
    return {
      path: this.shareUrl,
      title:`【待领取】xxxx ${this.name}邀请您参与xxxx福利活动>>`,
     imageUrl:"https://xxxxxxxxx.jpg"
    };
  },
  methods: {
    getinfo() {
      getCurrentUser().then(res => {
        this.name = res.data.realName
        this.bankName = res.data.deptName
        console.log(res)
      })
    },
    showposter() {
      this.showpost()
      // this.sharePost(true)
    },
    async showpost() {
      let device = uni.getSystemInfoSync();
      let wid = (device.windowWidth) / 800;
      this.width = wid * 750
      this.height = wid * 1140

      var w = wid * 2
      console.log(wid)
      this.isshow = true
      uni.showLoading({
        title: '加载中...',
        mask: true
      })
      var context = uni.createCanvasContext('firstCanvas')
      context.width = this.width * 5; // 实际渲染像素
      context.height = this.height * 5; // 实际渲染像素

      // 加背景图片
      context.save()  // 对当前画布区域进行保存
      context.beginPath()
      let bgImg = await this.getImageInfo(this.imgurl)
      context.drawImage(bgImg.tempFilePath, 0, 0, this.width, this.height)
      context.restore()  //对其进行恢复

      // 加 
      context.beginPath()
      context.setFontSize(12 * w)
      context.setFillStyle('#555555')
      context.setTextAlign('right')
      context.fillText(this.bankName, 320 * w, 360 * w)

      // 加 
      context.beginPath()
      context.setFontSize(12 * w)
      context.setFillStyle('#555555')
      context.setTextAlign('right')
      context.fillText(this.name, 320 * w, 380 * w)

      // context.draw()
      setTimeout(() => {
        context.draw(true, () => {  // 因为有图片下载,我一般延迟0.5秒
          uni.hideLoading()
        })
      }, 500)

    },
    //获取图片
    getImageInfo(imgSrc) {
      return new Promise((resolve, reject) => {
        uni.downloadFile({
          url: imgSrc,
          success: function (res) {
            resolve(res);
            console.log('获取图片成功', res)
          },
          fail: (err) => {
            reject(err);
            console.log('获取图片失败', err)
          }
        })
      });
    },
    async sharePost(flag) {
      var obj = wx.getLaunchOptionsSync();
      console.log(obj, "场景值");
      const data = {
        path: "active0601/invited/getInvited",
        activityId: this.activeId,
        type: 7,
        scene: obj.scene,
        referrerInfo: JSON.stringify(obj.referrerInfo),
        shareTicket: obj.shareTicket && JSON.stringify(obj.shareTicket),

      };

      uni.showLoading({ title: " 海报生成中..." });
      try {
        const resp = await getSharePost(data);
        if (resp.code == 0) {
          uni.hideLoading();

          this.imgurl = base_url + resp.data[0].url
          this.show = flag;
        } else {
          uni.hideLoading();
          uni.showToast({
            title: resp.msg || "服务器开小差啦",
            icon: "none",
          });
        }
      } catch (e) {
        uni.hideLoading();
        uni.showToast({
          title: e.msg || "服务器开小差啦",
          icon: "none",
        });
      }
    },
    save() {
      
      var that = this
      uni.canvasToTempFilePath({
        canvasId: 'firstCanvas',
        width: this.width * 3,
        height: this.height * 3,
        destWidth: this.width * 3,
        destHeight: this.height * 3,
        quality: 1,
        complete: (res) => {
          console.log('保存到相册', res);
          uni.saveImageToPhotosAlbum({
            filePath: res.tempFilePath,
            success(res) {
              uni.showToast({
                title: '已保存到相册',
                icon: 'success',
                duration: 2000
              })
              setTimeout(() => {
                that.isShow = false
              }, 2000)
            }
          })
        }
      }, this);
    },

  },
};
</script>

<style  lang="scss" scoped>
.pages {
  padding: 10rpx 30rpx;
  background: #f8f8f8;
  min-height: 100vh;
  background: #700704 0 0 no-repeat;
  background-size: contain;

  .button {
    position: absolute;
    bottom: 70rpx;
    left: 0;
    width: 100vw;

    view {
      width: 90%;
      background: linear-gradient(180deg, #f6a900 0%, #eb6120 100%);
      box-shadow: 0px 2px 21px 0px #e74442;
      border-radius: 20rpx;
      line-height: 90rpx;
      text-align: center;
      margin: 0 auto;
      font-weight: bold;
      color: #ffffff;
    }
  }

  .bg {
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background-color: RGBA(0, 0, 0, 0.7);
    z-index: 2;
  }

  .model {
    display: flex;
  }

  .poster {
    margin: 110rpx auto;
    background-color: #c11920;
    position: relative;
    z-index: 99;

  }

  .save {
    position: fixed;
    bottom: 0;
    left: 0;
    width: 100%;
    height: 100rpx;
    background: linear-gradient(90deg, #f6a900 0%, #eb6120 100%);
    box-shadow: 0px -4px 6px 0px rgba(186, 0, 12, 0.31);
    z-index: 11;
    font-weight: 600;
    color: #ffffff;
    line-height: 100rpx;
    text-align: center;
    text-shadow: 0px -4px 6px rgba(186, 0, 12, 0.31);
  }
}

.fixed {
  position: fixed;
  right: 30rpx;
  bottom: 200rpx;
  width: 145rpx;
}

.sharebutton {

  border: none;
  padding: 0;
  margin: 0;
  display: flex;
  align-items: center;
  background-color: rgba($color: #000000, $alpha: 0);
}
</style> 

  • 2
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

三线码工

码字不易,有钱打赏,没打钱点赞

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值