小程序中经常会有海报的生成,为了使用方便就自己封装了js文件方便调用
文件名为creatMaterialImage.js
海报图效果如下:
1,海报生成方法的具体实现
// 用canvas生成分享图片,返回src
const createShareImg = function(options, that) {
return new Promise(function(resolve, reject) {
getImgsInfo([options.productImg, options.qrCode, options.bg, options.headUrl]).then(function(res) {
//底部颜色
let context = uni.createCanvasContext(options.canvasId, that);
context.setFillStyle('#fff')
context.fillRect(0, 0, 600, 1008);
context.save();
//产品图片
if (res[0]) {
context.drawImage(res[0], 0, 0, 600, 830);
}
//头像
if (res[3]) {
roundRect(context, 30, 918, 36, 36, 18)
context.drawImage(res[3], 30, 918, 36, 36, 18);
context.restore();
}
var name = options.memberName.length > 5 ? options.memberName.substring(0, 4) + '...' : options.memberName;
context.setFillStyle('#333333');
context.setFontSize(24);
context.setTextAlign('left');
context.fillText('我是您的专属顾问 ' + name, 80, 945);
context.save();
context.setFillStyle('#999999');
context.setFontSize(22);
context.setTextAlign('right');
context.fillText('长按扫码进店', 550, 945);
context.save();
//二维码
if (res[1]) {
roundRect(context, 390, 720, 180, 180, 90)
context.drawImage(res[1], 390, 720, 180, 180, 90);
context.restore();
}
var shopname = options.productName.length > 7 ? options.productName.substring(0, 6) + '...' : options.productName;
context.setFillStyle('#333');
context.setFontSize(32);
context.setTextAlign('left');
context.font = 'normal bold 32px sans-serif';
// context.setTextAlign('center');
context.fillText('欢迎光临' + shopname, 30, 900);
context.save();
const result = uni.getSystemInfoSync();
const platform = result.platform;
let time = 0;
if (platform === 'android') {
// 在安卓平台,经测试发现如果海报过于复杂在转换时需要做延时,要不然样式会错乱
time = 300;
}
//console.log(context);
context.draw(false, function() {
//console.log('222');
setTimeout(function() {
//将生成好的图片保存到本地
uni.canvasToTempFilePath({
canvasId: options.canvasId,
fileType: 'jpg',
success(res) {
//console.log(res.tempFilePath, '111');
resolve(res.tempFilePath);
},
fail(err) {
console.log('canvasToTempFilePath err', err);
reject(err);
}
}, that);
}, time);
});
}).catch(function(err) {
console.log('getImgsInfo err', err);
reject(err);
})
})
}
/**
*
* @param {CanvasContext} ctx canvas上下文
* @param {number} x 圆角矩形选区的左上角 x坐标
* @param {number} y 圆角矩形选区的左上角 y坐标
* @param {number} w 圆角矩形选区的宽度
* @param {number} h 圆角矩形选区的高度
* @param {number} r 圆角的半径
*/
function roundRect(ctx, x, y, w, h, r) {
// 开始绘制
ctx.beginPath()
// 因为边缘描边存在锯齿,最好指定使用 transparent 填充
// 这里是使用 fill 还是 stroke都可以,二选一即可
ctx.setFillStyle('#f94335')
// ctx.setStrokeStyle('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)
// 这里是使用 fill 还是 stroke都可以,二选一即可,但是需要与上面对应
ctx.fill()
// ctx.stroke()
ctx.closePath()
// 剪切
ctx.clip()
}
/**
* [分割字符串]
* @param {[type]} str [字符串]
* @param {[type]} num [每行个数]
* @param {[type]} line [行数]
* @return {[type]} [返回字符串数组]
*/
const splitStr = (str, num, line) => {
let result = [];
for (let i = 0; i < line; i++) {
result.push(str.substr(i * num, num));
}
if (result[line - 1].length == num) {
result[line - 1] = result[line - 1].substr(0, num - 1) + '...';
}
// console.log(result);
return result;
}
// 获取单张图片信息
const getImgInfo = (path) => {
return new Promise((resolve, reject) => {
if (path) {
uni.getImageInfo({
src: path,
success(res) {
// console.log(res);
resolve(res.path);
},
fail(err) {
//console.log(err);
// reject(err);
resolve('');
}
})
} else {
resolve('');
}
})
}
// 获取多张图片的信息
const getImgsInfo = (paths) => {
// console.log(paths);
return new Promise((resolve, reject) => {
let promises = paths.map(item => {
return getImgInfo(item);
});
Promise.all(promises).then(res => {
// console.log(res);
resolve(res);
}).catch(err => {
reject(err);
});
})
}
export default {
createShareImg
}
2、具体使用方法
<template>
<view style="background: #f5f5f5;display: flex;flex-direction: column;min-height: 100vh;">
<view class="scroll_image">
<view class="item_page">
<swiper class="swiper" style="height: 1008upx;" @change="pageChange" :current="pageIndex" previous-margin="75rpx" next-margin="45rpx" :duration="duration">
<swiper-item v-for="(item, index) in posterData.InfoPosterUrlList" :key="index">
<view class="item_style"><image style="width: 600upx;height: 830upx;border-radius: 15upx 15upx 0 0;" :src="imageUrl + item" mode="aspectFill"></image></view>
<view style="background: #fff;height: 153upx;width: 580upx;padding-left: 20rpx;padding-top: 25rpx;border-radius: 0 0 15upx 15upx;">
<view style="font-size: 32rpx;font-weight: bold;">欢迎光临{{posterData.MallName}}</view>
<view style="font-size: 24upx;color: #333;display: flex;align-items: center;margin-top: 20rpx;">
<image :src="posterData.WxHeadUrl" mode="aspectFill" style="width: 36rpx;height: 36rpx;border-radius: 100%;margin-right: 10rpx;"></image>
<view style="font-size: 24rpx;color: #999;">我是您的专属顾问 {{ posterData.EmployeeName }}</view>
</view>
</view>
<view style="position: absolute;right: 60rpx;top: 740rpx;">
<image :src="miUrl" mode="aspectFill" style="width: 180rpx;height: 180rpx;border-radius: 90rpx;"></image>
<view style="font-size: 24rpx;color: #999;text-align: center;margin-top: 10rpx;">长按扫码识别</view>
</view>
</swiper-item>
</swiper>
</view>
</view>
<!-- <view style="margin-bottom: 200upx;" >
<image :src="canvasImage" mode="widthFix" style="width: 600upx;"></image>
</view> -->
<view style="height: 100rpx;">
</view>
<view style="height: 150rpx;">
</view>
<view class="fixed_bottom">
<view class="btn_save" @click="savePic">保存图片</view>
</view>
<canvas canvas-id="canvas" style="position: fixed;left: -100000px;width: 600px;height: 1008px;"></canvas>
</view>
</template>
<script>
var userInfo;
import config from '../../../common/js/config.js';
import createImage from '../../../common/js/creatMaterialImage.js';
export default {
data() {
return {
duration: 500,
canvasImage: '',
posterData: {},
pageIndex: 0,
imageUrl: config.imgUrl,
qrCodeUrl: '',
miUrl: '',
isFirstSave: true,
isQyWx:true
};
},
onLoad() {
userInfo = uni.getStorageSync('userInfo');
var that = this;
uni.getSystemInfo({
success (res) {
if(res.environment && res.environment=="wxwork") {
that.isQyWx=true;
}else{
that.isQyWx=false;
}
}
})
uni.showShareMenu({
// 是否使用带 shareTicket 的转发
withShareTicket: true
});
this.getQrCodeData();
},
methods: {
async getData() {
try {
let data = {};
let res = await this.$ajax({
url: 'infoPosterIndex',
data: data
});
if (res.IsSuccess) {
res.Result.MallName = res.Result.MallName.length>7 ? res.Result.MallName.substring(0,6)+'...' : res.Result.MallName;
res.Result.EmployeeName = res.Result.EmployeeName.length>5 ? res.Result.EmployeeName.substring(0,4)+'...' : res.Result.EmployeeName;
this.posterData = res.Result;
this.getMaterialImg(false);
}
} catch (e) {
//TODO handle the exception
}
},
//获取小程序码
async getQrCodeData() {
try {
let data = {
Page: 'pages/index/index',
Scene: 'f=' + userInfo.EmployeeId + '&t=0',
IsLimited: false,
ProgramType: 0 //小程序类型
};
let res = await this.$ajax({
url: 'getQrCode',
data: data
});
if (res.IsSuccess) {
this.qrCodeUrl = JSON.parse(res.Result);
this.miUrl = this.imageUrl + this.qrCodeUrl;
this.getData();
console.log(this.miUrl);
}
} catch (e) {
//TODO handle the exception
}
},
pageChange(e) {
this.pageIndex = e.detail.current;
this.getMaterialImg(false);
},
getMaterialImg(type) {
if (type) {
//显示加载
uni.showLoading({
title: '正在生成图片...'
});
}
const params = {
canvasId: 'canvas',
productImg: this.imageUrl + this.posterData.InfoPosterUrlList[this.pageIndex],
productName: this.posterData.MallName,
qrCode: this.imageUrl + this.qrCodeUrl,
bg: 'https://cdn.dkycn.cn/images/melyemplyee/shareMall_bg.png',
headUrl: this.posterData.WxHeadUrl,
memberName: this.posterData.EmployeeName
};
var that = this;
createImage
.createShareImg(params, that)
.then(function(res) {
if (type) {
//显示加载
uni.hideLoading();
}
that.canvasImage = res;
// that.saveToAlbum();
console.log(that.canvasImage, 'createImg')
})
.catch(err => {
uni.showToast({
title: '图片生成失败',
icon: 'none'
});
});
},
savePic() {
this.saveToAlbum();
},
saveToAlbum() {
var that = this;
if(that.isQyWx){
that.SaveImage();
}else{
wx.getSetting({
success(res) {
if (that.isFirstSave) {
if (typeof res.authSetting['scope.writePhotosAlbum'] == 'undefined') {
that.SaveImage();
} else if (!res.authSetting['scope.writePhotosAlbum']) {
uni.showModal({
title: '提示',
content: '保存图片需要开启权限,是否前往设置?',
confirmText: '前往',
cancelText: '取消',
success(res) {
if (res.confirm) {
uni.openSetting({});
}
}
});
} else {
that.SaveImage();
}
that.isFirstSave = false;
} else {
if (!res.authSetting['scope.writePhotosAlbum']) {
uni.showModal({
title: '提示',
content: '保存图片需要开启权限,是否前往设置?',
confirmText: '前往',
cancelText: '取消',
success(res) {
if (res.confirm) {
uni.openSetting({});
}
}
});
} else {
that.SaveImage();
}
}
},
fail() {}
});
}
},
SaveImage() {
var that = this;
wx.getImageInfo({
src: that.canvasImage,
success(files) {
uni.showLoading({
title: '图片保存中...'
});
wx.saveImageToPhotosAlbum({
filePath: files.path,
success(res) {
console.log('--saveImageToPhotosAlbum----', res);
uni.hideLoading();
setTimeout(function() {
wx.showModal({
content: '图片已保存,赶紧去分享吧~',
showCancel: false,
confirmText: '好的',
confirmColor: '#333',
success: function(res) {
if (res.confirm) {
}
}
});
uni.hideLoading();
}, 200);
},
complete() {
uni.hideLoading();
}
});
}
});
}
},
onShareAppMessage(e) {
if (e.from == 'button') {
console.log(this.canvasImage);
return {
title: '欢迎光临' + this.posterData.MallName + ',我是您的专属顾问' + this.posterData.EmployeeName,
imageUrl: this.canvasImage,
path: '/pages/errPage/errPage?fromMemberType=0&fromMemberId=' + this.posterData.EmployeeId
};
}
}
};
</script>
<style lang="scss" scoped>
.scroll_image {
// margin-bottom: 200upx;
height: 85vh;
.item_page {
height: 85vh;
.swiper {
margin-top: 5vh;
}
}
}
.fixed_bottom {
height: 10vh;
// margin-top: 40upx;
background: #fff;
display: flex;
position: fixed;
// position: absolute;
bottom: 0;
left: 0;
right: 0;
justify-content: center;
padding: 0 100upx;
align-items: center;
.bottom_item {
display: flex;
flex-direction: column;
align-items: center;
font-size: 24upx;
color: #666;
}
.btn_save {
width: 550upx;
height: 70upx;
border-radius: 50upx;
background: #418cf6;
font-size: 26upx;
color: #fff;
text-align: center;
line-height: 70upx;
}
}
.item_style {
width: 600upx;
background: #fff;
border-radius: 15upx;
}
.post_center{
width: 540rpx;
height: 550rpx;
border-radius: 10rpx;
position: absolute;
top: 315rpx;
margin-left: 30rpx;
background: #fff;
box-shadow: 0px 0px 25px 0px
rgba(0, 0, 0, 0.08);
}
.item_bottom {
height: 15vh;
display: flex;
width: 600upx;
align-items: center;
background: #fff;
justify-content: space-between;
margin-right: 50upx;
border-radius: 0 0 15upx 15upx;
}
</style>