小程序使用canvas生成海报以及保存相册授权,上代码
生成海报
保存图片
请在设置中打开相册权限~
下面是wxss
/* 整体遮罩层 */
.modals-cancel{
position:fixed;
z-index:101;
top:0;
left: 0;
right:0;
bottom: 0;
background-color: rgba(0,0,0,.6);
}
.Bottomframe{
position:fixed;
z-index:102;
bottom:0;
left:0;
right:0;
padding: 27rpx 0;
background-color: #fff;
display: flex;
justify-content: space-around;
}
.bottom-pos-icon{
width: 100rpx;
height: 100rpx;
margin-bottom: 28rpx;
}
/*动画前初始位置*/
.bottom-pos{
-webkit-transform:translateY(100%);transform:translateY(100%);
}
.ShareFriendsBtn, .CreatePosterBtn{
background-color: #ffffff;
color: #333333;
line-height: 0.5;
height: 146rpx;
font-size: 26rpx;
}
.ShareFriendsBtn::after{
border: none;
}
.CreatePosterBtn::after{
border: none;
}
/* 生成海报 */
.imgBox{
text-align: center;
width: 100%;
margin-top:60rpx;
padding-bottom: 120rpx;
}
.imagePathBox{
width: 100%;
height: 100%;
background: rgba(0,0,0,0.7);
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 103;
}
.CreateImg{
position:fixed;
left: 50%;
top: 45%;
}
.saveImage{
position:fixed;
width:490rpx;
height:97rpx;
bottom:50rpx;
left:50%;
margin-left:-245rpx;
}
.saveImage text{
position:absolute;
top:0;
left:0;
right:0;
color:#fff;
font-size:34rpx;
line-height:97rpx;
text-align:center;
}
.btn_login_bg{
height:100%;
width:100%;
}
.closeCanvas{
position:absolute;
right:20px;
top:20px;
width:20px;
height:20px;
}
/* 授权弹窗 */
.open-seting-bg {
position: fixed;
width: 100%;
height: 100%;
top: 0;
left: 0;
background: rgba(0, 0, 0, 0.6);
z-index:10000;
}
.btn-openset {
border-top:1px solid #f4f4ef;
}
.set-title {
margin: 40rpx 0;
}
.open-set-inner {
width: 520rpx;
height: 220rpx;
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
margin: auto;
background: #ffffff;
border-radius:6rpx;
text-align: center;
font-size:32rpx;
}
.button-style {
width: 100%;
height: 100%;
background: #fff;
color:#48a2f8;
}
button::after {
border: 0;
}
下面是重点
const app = getApp()
Page({
data: {
hideModal: true,
animationData: {},
maskHidden: false,
canvasHeight: "",
canvasWidth: "",
openSet: false,//授权弹窗
firstNo: true,
pixelRatio: '',//像素
photourl:"https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=659048405,486552330&fm=115&gp=0.jpg",
snapshotUrl:"http://res.iiiview.net/ugc/file/2017/08/11/i52a077rui955x7v0123hwhtoq353m80.jpg"
},
onLoad: function (options) {
wx.getSystemInfo({ // 获取页面可视区域的高度
success: (res) => {
this.setData({
height: res.screenHeight,
// canvasHeight: res.screenWidth * 0.8 * 8 / 5, 8 / 5为设计图的比例 //画布高
// canvasWidth: res.screenWidth * 0.8,//画布宽
canvasHeight: 480,
canvasWidth: 300,//画布宽
pixelRatio: res.pixelRatio,//像素
})
},
})
},
// 调起底部分享操作
// 显示遮罩层
showModal: function (res) {
var that = this;
var animation = wx.createAnimation({
duration: 400,//动画的持续时间 默认400ms 数值越大,动画越慢 数值越小,动画越快
timingFunction: 'ease'
})
this.animation = animation
setTimeout(function () {
that.fadeIn();//调用
}, 200)
},
// 隐藏遮罩层
hideModal: function () {
var that = this;
var animation = wx.createAnimation({
duration: 400,
timingFunction: 'ease',
})
this.animation = animation
that.fadeDown();//调用隐藏动画
setTimeout(function () {
that.setData({
hideModal: true
})
}, 200)//先执行下滑动画,再隐藏模块
},
//动画集 显示动画
fadeIn: function () {
this.animation.translateY(0).step()
this.setData({
animationData: this.animation.export()//动画实例的export方法导出动画数据传递给组件的animation属性
})
},
//动画集 隐藏动画
fadeDown: function () {
this.animation.translateY(300).step()
this.setData({
animationData: this.animation.export(),
})
},
// 生成海报
//点击生成
createPoster: function (e) {
var that = this;
this.setData({
maskHidden: false
});
// 调用生成海报方法
that.createNewImg();
},
//生成海报
createNewImg: function () {
wx.showLoading({
title: '海报生成中...',
mask: true,
});
/* 注意画布画的字体 x 轴 需要加上字的高度 */
let that = this,
canvasWidth = that.data.canvasWidth,
canvasHeight = that.data.canvasHeight;
that.hideModal()//隐藏遮罩层
let qrcode = "/images/qrcode.jpg";//二维码
// 下载头像图片
wx.downloadFile({
url: that.data.photourl,
success(res) {
let photourl = res.tempFilePath;//二维码
// 下载封面
wx.getImageInfo({
src: that.data.snapshotUrl,
success(res) {
let snapshotUrl = res.path;//封面
//创建画布
let ctx = wx.createCanvasContext('mycanvas');
ctx.clearRect(0, 0, 300, 480)//清除画布
ctx.drawImage("/images/canvasBg.png", 0, 0, canvasWidth, canvasHeight);
let textSize = canvasHeight * 28 / 960;/*文字大小 */
let tipsSize = canvasHeight * 24 / 960;/*文字大小 */
console.log(photourl)
// // 绘制头像
ctx.save(); // 保存当前ctx的状态
ctx.beginPath();
let headL = canvasWidth / 2,
headR = canvasHeight * 62 / 960, /* 头像半径,*/
headT = headR + canvasHeight * 48 / 960; /* 120rpx头像高,*/
ctx.arc(headL, headT, headR, 0, 2 * Math.PI, false); //画出圆
ctx.setFillStyle('lightgreen')
ctx.fill()
ctx.clip(); //裁剪上面的圆形
ctx.drawImage(photourl, headL - headR, canvasHeight * 48 / 960, 2 * headR, 2 * headR);
ctx.restore(); // 还原状态
// 绘制名字
ctx.setFontSize(textSize);
ctx.setFillStyle("#333333");
ctx.setTextAlign('center');
ctx.fillText("canvas", headL, canvasHeight * 218 / 960)
//绘制封面
let clip_left = canvasWidth * 36 / 600, /* 左偏移值 */
clip_top = canvasHeight * 242 / 960, /* 上偏移值 */
clip_width = canvasWidth * 528 / 600, /* 截取宽度 */
clip_height = canvasHeight * 297 / 960, /* 截取高度 */
imgH = res.height,//图片高
imgW = res.width;//图片宽
// 绘制封面圆角
let x = clip_left, y = clip_top, r = 6, w = clip_width, h = clip_height
ctx.save()//先保存
ctx.beginPath() // 开始绘制
ctx.setFillStyle('white')// 因为边缘描边存在锯齿,最好指定使用 transparent 填充,但是transparent在安卓上有兼容问题,导致图片不能显示
// 左上角
ctx.arc(x + r, y + r, r, Math.PI, Math.PI * 1.5)
// border-top
ctx.moveTo(x + r, y)
ctx.lineTo(x + w - r, y)
ctx.lineTo(x + w, y + r)
// 右上角
ctx.arc(x + w - r, y + r, r, Math.PI * 1.5, Math.PI * 2)
// border-right
ctx.lineTo(x + w, y + h - r)
ctx.lineTo(x + w - r, y + h)
// 右下角
ctx.arc(x + w - r, y + h - r, r, 0, Math.PI * 0.5)
// border-bottom
ctx.lineTo(x + r, y + h)
ctx.lineTo(x, y + h - r)
// 左下角
ctx.arc(x + r, y + h - r, r, Math.PI * 0.5, Math.PI)
// border-left
ctx.lineTo(x, y + r)
ctx.lineTo(x + r, y)
ctx.fill()
ctx.closePath()
ctx.clip()// 剪切
// 按短边计算
if (imgW * 9 / 16 > imgH) {
let sx = (imgW - imgH * 16 / 9) / 2
ctx.drawImage(snapshotUrl, sx, 0, imgH * 16 / 9, imgH, clip_left, clip_top, clip_height * 16 / 9, clip_height);
} else {
let sy = (imgH - imgW * 9 / 16) / 2
ctx.drawImage(snapshotUrl, 0, sy, imgW, imgW * 9 / 16, clip_left, clip_top, clip_width, clip_width * 9 / 16);
}
ctx.restore(); // 还原状态
// 绘制作品名称
ctx.setFontSize(canvasHeight * 30 / 960);
ctx.setFillStyle("#333333");
ctx.setTextAlign('left');
ctx.fillText("绘制canvas作品名称", clip_left, canvasHeight * 599 / 960)
// 绘制二维码
let qrcodeW = canvasHeight * 200 / 960;/*二维码宽高 */
ctx.drawImage(qrcode, headL - qrcodeW / 2, canvasHeight * 629 / 960, qrcodeW, qrcodeW);
// 绘制文字描述
ctx.setFontSize(tipsSize);
ctx.setFillStyle("#373737");
ctx.setTextAlign('center');
ctx.fillText("长按识别小程序,进入页面观看作品", headL, canvasHeight * 885 / 960)
ctx.setFontSize(tipsSize);
ctx.setFillStyle("#373737");
ctx.setTextAlign('center');
ctx.fillText("还能给作者点赞鼓励哦!", headL, canvasHeight * 916 / 960)
ctx.draw()
}
})
//将生成好的图片保存到本地,需要延迟一会,绘制期间耗时
let pixelRatio = that.data.pixelRatio;
setTimeout(function () {
wx.canvasToTempFilePath({
x: 0,
y: 0,
width: canvasWidth,
height: canvasHeight,
destWidth: canvasWidth * pixelRatio,
destHeight: canvasHeight * pixelRatio,
canvasId: 'mycanvas',
success: function (res) {
var tempFilePath = res.tempFilePath;
that.setData({
imagePath: tempFilePath,
maskHidden: true
});
wx.hideLoading()
},
fail: function (res) {
console.log(res);
}
});
}, 2000);
}
})
},
//预览图片
preview_img: function () {
var that = this;
console.info("preview_img", that.data.imagePath);
wx.previewImage({
current: that.data.imagePath,
urls: [that.data.imagePath]
})
},
touchStart: function () {
var that = this;
console.info("touchStart", that.data.imagePath);
wx.previewImage({
current: that.data.imagePath,
urls: [that.data.imagePath]
})
},
// 授权弹窗
cancleSet() {
this.setData({
openSet: false
})
},
// 关闭海报
closeCanvas: function () {
this.setData({
maskHidden: false
})
},
//点击保存到相册
saveImage: function () {
var that = this;
wx.getSetting({
success(res) {
console.log("没有则获取授权")
// 如果没有则获取授权
if (!res.authSetting['scope.writePhotosAlbum']) {
console.log("没有则获取授权")
wx.authorize({
scope: 'scope.writePhotosAlbum',
success() {
console.log(res)
wx.saveImageToPhotosAlbum({
filePath: that.data.imagePath,
success(res) {
wx.showModal({
content: '图片已保存到相册,去晒一下吧~',
showCancel: false,
confirmText: '好的',
confirmColor: '#333',
success: function (res) {
if (res.confirm) {
that.setData({
maskHidden: false
})
}
},
fail: function (res) {
console.log(res)
}
})
}
})
},
fail(error) {
console.log(error)
// 处理用户第一次拒绝后禁止调用授权弹窗
if (that.data.firstNo == true) {
that.setData({
maskHidden: false,
firstNo: false
})
} else {
that.setData({
maskHidden: false,
openSet: true
})
}
}
})
} else {
// 授权成功保存
wx.saveImageToPhotosAlbum({
filePath: that.data.imagePath,
success(res) {
wx.showModal({
content: '图片已保存到相册,去晒一下吧~',
showCancel: false,
confirmText: '好的',
confirmColor: '#333',
success: function (res) {
if (res.confirm) {
that.setData({
maskHidden: false
})
}
},
fail: function (res) {
console.log(res)
}
})
}
})
}
},
fail(error){
console.log(error)
}
})
},
});
这里面的960和600是我设计图的比例,宽600,高960,具体根据设计图来,9/16也是我封面图片的比例 528 和 297 是封面的宽和高,最大的难点我个人感觉就是计算图片的的所在位置了,值得注意的是画布的宽和高最好按照设备的一边来取,不要既取设备的宽又取设备的高,我在这上面爬坑爬了好久
// canvasHeight: res.screenWidth * 0.8 * 8 / 5, 8 / 5为设计图的比例 //画布高
// canvasWidth: res.screenWidth * 0.8,//画布宽
可以按这种取法,或者写死
以上代码拿下来就就可以用,有不对的地方欢迎指出来
最后上几张图看下效果
1.生成图片
2.获取授权
3.用户拒绝授权之后,再次生成图片提示用户去授权
4.用户打开授权
5.授权成功保存到相册