在使用自定义组件绘制海报时,一定要传第二个参数this,不然绘制不成功。
所有网络图片都需要通过wx.downloadFile下载下来,如果是本地图片则不需要下载,本示例采用组件方式
目录结构
组件canvas
<view class="web" catchtouchmove='stopMove' wx:if="{{isShow}}">
<view class="foot" animation="{{animation}}">
<view class="foot-list">
<button class="" hover-class="none" open-type="share">
<view class="iconfont icon-weixin wx" ></view>
<view class="title">分享给朋友</view>
</button>
<view class="" bindtap='creatPosters'>
<view class="iconfont icon-ico goods-icon"></view>
<view class="title">生成商品海报</view>
</view>
</view>
<view class="cancel" bindtap='cancel'>关闭</view>
</view>
</view>
<canvas
canvas-id="myCanvas"
style="width:{{screen_width*375+'px'}}; height:{{screen_height-100+'px'}}"
></canvas>
<!-- 海报展示 -->
<view class="posters-content" catchtouchmove='stopMove' wx:if="{{posters}}">
<view class="poster-warpp">
<view class="posters-img">
<image class="posters"
style="width:{{screen_width*375+180+'rpx !important'}};height:{{screen_height+150+'rpx'}}"
src="{{posters}}"
mode='aspectFill'
bindtap='preview'
/>
</view>
<button
class="save"
open-type="openSetting"
wx:if="{{isShare}}"
bindopensetting='openSetting'
>
保存图片
</button>
<view class="save"wx:else catchtap="save">保存图片</view>
<view class="call-off" catchtap="callOff">取消保存</view>
</view>
</view>
组件样式
@import '../../app.wxss';
.web{
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 100;
background: rgba(0, 0, 0, 0.7);
}
.foot{
width: 100%;
height: 300rpx;
background: #f3f1f2;
position: absolute;
left: 0;
bottom: -100%;
}
.foot-list{
height: 200rpx;
display: flex;
align-items: center;
justify-content: space-around;
}
.wx{
color: #50b674;
font-size: 60rpx;
text-align: center;
}
button{
line-height: unset;
background: transparent;
padding: 0;
margin: 0;
}
.button-hover{
color: transparent !important;
background: transparent !important;
}
button::after{
display: none;
}
.title{
font-size: 26rpx;
color: #333;
margin-top: 20rpx;
}
.goods-icon{
color: #3c7cfc;
font-size: 60rpx;
text-align: center;
}
.cancel{
width: 100%;
height: 100rpx;
text-align: center;
line-height: 100rpx;
font-size: 32rpx;
color: #333;
background: #fff;
}
canvas{
position: fixed;
bottom: -100%;
}
.posters-content{
position: fixed;
width: 100%;
left: 0;
top: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.4);
z-index: 100;
}
.poster-warpp{
position: absolute;
top: 50%;
left: 50%;
transform:translate(-50%,-50%);
-webkit-transform:translate(-50%,-50%);
-moz-transform:translate(-50%,-50%);
-ms-transform:translate(-50%,-50%);
-o-transform:translate(-50%,-50%);
padding: 60rpx 24rpx 30rpx 24rpx;
background: #fff;
}
.posters-img{
display: block;
width: 550rpx;
margin: auto;
background: #fff;
height: 635rpx;
overflow: hidden;
opacity: 1;
text-align: center;
}
.save{
font-size: 32rpx;
color: #fff;
background: #3c7cfc;
text-align: center;
height: 80rpx;
line-height: 80rpx;
margin-top: 30rpx;
border-radius: 10rpx;
-webkit-border-radius: 10rpx;
-moz-border-radius: 10rpx;
-ms-border-radius: 10rpx;
-o-border-radius: 10rpx;
}
.call-off{
font-size: 28rpx;
color: #7f7f7f;
margin-top: 20rpx;
text-align: center;
}
//引入生成canvas方法
import detailCanvas from '../../utils/shareCanvas.js';
Component({
/**
* 组件的属性列表
*/
properties: {
detailImg:{
type:String
},
codeImg:{
type:String
},
title:{
type:String
},
content:{
type:String
},
price:{
type:String
},
action:{
type:String
}
},
lifetimes:{
ready:function(){
// 获取当前手机的宽高
wx.getSystemInfo({
success: (res)=>{
this.setData({
screen_width: res.windowWidth/375,
screen_height: res.windowHeight
})
},
});
}
},
/**
* 组件的初始数据
*/
data: {
isShow:false,
animation:'',
screen_width:'',
screen_height:'',
posters:'',
isShare:false
},
组件的方法列表,Promise.all方法可以在所有图片完成加载之后再触发回调,这样就可以避免其中图片没有加载完成就开始绘制canvas
methods: {
//禁止页面滑动
stopMove(){},
cancel(){
this.setData({
isShow:false
})
this.creatAnimation("-100%");
},
showWeb(){
this.setData({
isShow:true
})
this.creatAnimation(0);
},
//取消保存
callOff(){
this.setData({
posters:''
})
},
creatPosters(){
wx.showLoading({
title: '绘制中',
mask: true,
});
let that = this;
let detailImg = this.properties.detailImg;
let title = this.properties.title;
let codeImg = this.properties.codeImg;
let price = this.properties.price;
let content = this.properties.content;
let action = this.properties.action;
Promise.all([
this.getCode(detailImg),
this.getCode(codeImg),
])
.then(res=>{
let sWidth = this.data.screen_width;
let sHeight = this.data.screen_height;
let code = res[1].tempFilePath;
let img = res[0].tempFilePath;
detailCanvas(that,sWidth,sHeight,img,code,title,action,price,content,function(res){
wx.hideLoading();
that.setData({
posters:res,
isShow:false
});
that.creatAnimation("-100%");
});
})
},
//弹窗动画效果
creatAnimation(num){
let animation= wx.createAnimation({
duration:200,
timingFunction:'ease-in'
})
animation.bottom(num).step();
this.setData({
animation:animation.export()
})
},
//把网路图片下载成本地图片
getCode(img){
return new Promise((resolve,reject)=>{
wx.downloadFile({
url: img,
success: (res) => {
resolve(res)
}
})
})
},
//保存图片
save(){
let posters = this.data.posters;
wx.saveImageToPhotosAlbum({
filePath: posters,
success: (res)=>{
console.log(res)
wx.showToast({
title:'保存成功',
icon:'success'
})
this.setData({
posters:''
})
},
fail: (err)=>{
console.log(err)
wx.showToast({
title: '请授权保存图片',
icon: 'none',
});
this.setData({
isShare:true
})
},
});
},
openSetting(e){
if(e.detail.authSetting['scope.writePhotosAlbum']){
this.setData({
isShare:false
})
}
},
preview(){
let posters = this.data.posters;
wx.previewImage({
current: posters,
urls: [posters],
});
},
}
})
创建shareCanvas.js方法,绘制canvas,canvas使用的单位是px所以在获取手机屏幕宽度时,进行像素转换,做兼容处理res.windowWidth/375,以iphone6为例,下面变量中的sWidth也就是转换之后的像素。小程序canvas支持画布绘制完成之后的回调cxt.draw(false,callback);必须在draw的callback后执行,才能调用起图片预览,展示等功能
export default function detailCanvas(that,sWidth,sHeight,imgUrl,codeImg,title,action,price,content,callback){
let rpx = sWidth;
const cxt = wx.createCanvasContext('myCanvas',that);
cxt.save();
cxt.setFillStyle('#fff')
cxt.fillRect(0, 0, sWidth*375, sHeight-100);
//商品图片
let detailImgHeight = (sWidth*375)*9/16;
cxt.drawImage(imgUrl,0,0,sWidth*375,detailImgHeight);
//二维码
cxt.drawImage(codeImg,(sWidth*375-110*rpx),310*rpx,100*rpx,100*rpx);
cxt.restore();
//标题
cxt.setFontSize(18);
cxt.setFillStyle('#333333');
if(title.length>19){
title=title.slice(0,18)+"...";
}
cxt.fillText(title,10*rpx,240*rpx);
//价格
if(price){
cxt.setFontSize(20);
cxt.setFillStyle("#eb164c");
cxt.fillText('¥'+price,10*rpx,280*rpx);
}
//副标题
if(content){
if(content.length>19){
content=content.slice(0,18)+"...";
}
cxt.setFontSize(20);
cxt.setFillStyle("#eb164c");
cxt.fillText(content,10*rpx,280*rpx);
}
cxt.setStrokeStyle('#ddd')
cxt.strokeRect(0,300*rpx,sWidth*375,0.1)
cxt.setFillStyle('#292929');
cxt.setFontSize('12');
cxt.fillText('微信扫码或长按保存图片',10*rpx,350*rpx);
cxt.fillText(action|| '微信小程序商城',10*rpx,370*rpx);
cxt.draw(false,()=>{
wx.canvasToTempFilePath({
x: 0,
y: 0,
canvasId:"myCanvas",
fileType: 'png',
success: (res)=>{
callback(res.tempFilePath)
},
}, that);
});
}