一、整體步驟流程:
1、用小程序的頁面把想要的海報(bào)格式渲染出來(有海報(bào)設(shè)計(jì)稿的話此處可以不用)。
我的海報(bào)組成:上面一張背景圖,背景圖的左下方是說明文字,說明文字可以隨意編輯,右下方是一張固定的二維碼圖片,這三部分組成

2、在第一步的頁面上放一個(gè)按鈕“生成海報(bào)”,加一個(gè)點(diǎn)擊事件,點(diǎn)擊按鈕后跳轉(zhuǎn)到海報(bào)頁面
第一步渲染的頁面上,我需要把圖片地址和錄入的文字信息傳到海報(bào)的頁面,所以跳轉(zhuǎn)時(shí)帶了兩個(gè)參數(shù):一個(gè)地址,一個(gè)文字標(biāo)題
saveImage() {
//跳轉(zhuǎn)到海報(bào)頁面
uni.navigateTo({
url: '/pages/canTest/canTest?url=' + this.url + '&title=' + this.title
})
},
3、新的海報(bào)頁面,通過canvas把海報(bào)畫出來。
4、點(diǎn)擊新生成的海報(bào)鏈接,預(yù)覽海報(bào),再把海報(bào)保存到手機(jī)相冊(cè)
難點(diǎn):canvas不會(huì)用,不知道怎么畫。
二、操作過程:
1、海報(bào)頁面寫一個(gè)canvas組件
上面第2步點(diǎn)擊“生成海報(bào)”,跳轉(zhuǎn)到畫海報(bào)的頁面,頁面上寫一個(gè)canvas組件,然后給canvas一個(gè)點(diǎn)擊事件 ,點(diǎn)擊后可以預(yù)覽畫布畫的圖片。
style里面設(shè)置的是畫布的寬度和高度
<template>
<view class="demo">
<canvas :style="{ width: canvasW + 'px', height: canvasH + 'px' }" canvas-id="myCanvas" id="myCanvas"
@click="saveHB"></canvas>
</view>
</template>
2、獲取設(shè)備的信息和照片的信息,
(我的背景圖用的是網(wǎng)絡(luò)圖片:this.url)
2.1獲得屏幕的寬和高
//獲取設(shè)備信息
this.SystemInfo = await this.getSystemInfo();
獲得了設(shè)備的寬和高就是獲得了畫布的寬和高,如果畫布高度不需要整個(gè)屏幕高,則可以減掉一部分,至于具體減掉多少自己調(diào)試就行,我減掉了150高度。
this.canvasW = this.SystemInfo.windowWidth; // 畫布寬度
this.canvasH = this.SystemInfo.windowHeight - 150; //畫布高度
2.2獲得背景圖片的實(shí)際寬和實(shí)際高
this.goodsImg = await this.getImageInfo(this.url); //獲取照片信信息
2.3計(jì)算背景圖片畫在畫布上時(shí)應(yīng)該畫的寬度和高度
這個(gè)在畫布上需要畫的背景圖片的寬度,我的設(shè)計(jì)稿左右padding :20rpx,所以實(shí)際畫的寬度是屏幕的寬度減掉兩邊一起20的寬度
this.picWidth = this.canvasW - 20
背景圖片的在畫布上的寬度確認(rèn)后,要保證原圖的縮放比,所以高度需要計(jì)算下:
原圖寬 / 原圖高=實(shí)際畫的寬 / 實(shí)際畫的高,
原圖的寬和高通過getImageInfo得到了,新需要畫的寬在上面一步得到了,所以實(shí)際需要的高為:實(shí)際畫的寬*原圖的高/原圖的寬
this.picHight = this.picWidth * this.goodsImg.height / this.goodsImg.width
3.準(zhǔn)備二維碼圖片
我沒有自動(dòng)生成二維碼,直接準(zhǔn)備了一張二維碼圖片
//二維碼的信息
this.ewmImg = '/static/erweima.png';
4.畫背景圖
// 如果主圖,二維碼圖片,設(shè)備信息都獲取成功,開始繪制海報(bào),這里需要用setTimeout延時(shí)繪制,否則可能會(huì)出現(xiàn)圖片不顯示。
//檢查系統(tǒng)信息,圖片信息都正常返回了
if (this.goodsImg.errMsg == 'getImageInfo:ok' && this
.SystemInfo.errMsg == 'getSystemInfo:ok') {
// console.log('ok')
//彈出提示,海報(bào)繪制中
uni.showToast({
icon: 'loading',
mask: true,
// duration: 5000,
title: '海報(bào)繪制中',
});
setTimeout(() => {
//準(zhǔn)備ctx畫布
var ctx = uni.createCanvasContext('myCanvas', this);
// 填充畫布的背景色為白色
ctx.setFillStyle('#fff'); // 默認(rèn)白色
//設(shè)置的畫布的高度與寬度和2.1章節(jié)的屏幕寬高保持一致
ctx.fillRect(0, 0, this.canvasW, this.canvasH) // fillRect(x,y,寬度,高度)
// 繪制主背景圖,因?yàn)橛?0rpx的padding,所以起始坐標(biāo)是10,10,
//后面兩個(gè)參數(shù)是圖片需要畫的寬度和高度
ctx.drawImage(this.goodsImg.path, 10, 10, this.picWidth, this.picHight)
5、繪制二維碼

//如果有設(shè)計(jì)圖,直接量設(shè)計(jì)圖即可,
//沒有設(shè)計(jì)圖就慢慢調(diào)試
//我的x坐標(biāo)是設(shè)備的寬度減掉前面文字組件的大概寬度:this.canvasW - 110
//我的Y坐標(biāo)是上面背景圖的高度加上中間隔離的高度:this.picHight + 45
//后面兩個(gè)參數(shù)就是需要畫的二維碼的寬和高: this.ewmW
ctx.drawImage(this.ewmImg, this.canvasW - 110, this.picHight + 45, this.ewmW, this.ewmW)
// 6、繪制文字標(biāo)題,多余文字自動(dòng)換行
ctx.setFontSize(18); // setFontSize() 設(shè)置字體字號(hào)
ctx.setFillStyle('#000000'); // setFillStyle() 設(shè)置字體顏色
//canvas不能自動(dòng)換行,需要自行計(jì)算 ,直接copy過去用
let _strlineW = 0;
let _strLastIndex = 0; //每次開始截取的字符串的索引
// let _strHeight = this.canvasW * rant + 30; //繪制字體距離canvas頂部的初始高度
let _strHeight = this.picHight + 70;
let _num = 1;
for (let i = 0; i < this.title.length; i++) {
_strlineW += ctx.measureText(this.title[i]).width;
// if (_strlineW > this.canvasW * rant - 155) {
if (_strlineW > this.canvasW - 155) {
if (_num == 4 && 4) {
//文字換行數(shù)量大于4進(jìn)行省略號(hào)處理,想幾行就改下上面的數(shù)字
ctx.fillText(this.title.substring(_strLastIndex, i - 5) + '...', 10,
_strHeight);
_strlineW = 0;
_strLastIndex = i;
_num++;
break;
} else {
ctx.fillText(this.title.substring(_strLastIndex, i), 20, _strHeight);
_strlineW = 0;
_strHeight += 20;
_strLastIndex = i;
_num++;
}
} else if (i == this.title.length - 1) {
ctx.fillText(this.title.substring(_strLastIndex, i + 1), 20, _strHeight);
_strlineW = 0;
}
}
/* end */
以下內(nèi)容就比較容易了,直接照抄即可
ctx.draw(true, (ret) => { // draw方法 把以上內(nèi)容畫到 canvas 中。
uni.showToast({
icon: 'success',
mask: true,
title: '繪制完成',
});
uni.canvasToTempFilePath({ // 保存canvas為圖片
canvasId: 'myCanvas',
quality: 1,
complete: (res) => {
this.hbUrl.push(res.tempFilePath)
},
})
});
}, 1000)
} else {
console.log('err')
}
7.給canvas綁定一個(gè)點(diǎn)擊事件,點(diǎn)擊后,可以預(yù)覽圖片,預(yù)覽時(shí)可以長按圖片進(jìn)行轉(zhuǎn)發(fā)和保存了
//預(yù)覽圖片接口,urls參數(shù)為數(shù)組,上面繪圖得到的地址push到 this.hbUrl,就可以預(yù)覽了
saveHB() {
// console.log('點(diǎn)擊了圖片')
uni.previewImage({
urls: this.hbUrl
})
},
下面時(shí)獲取圖片信息和獲取系統(tǒng)信息的方法
// 獲取圖片信息
getImageInfo(image) {
return new Promise((req, rej) => {
uni.getImageInfo({
src: image,
success: (res) => {
req(res)
},
});
})
},
// 獲取設(shè)備信息
getSystemInfo() {
return new Promise((req, rej) => {
uni.getSystemInfo({
success: function(res) {
req(res)
}
});
})
},
通過以上的方式,就可以生成海報(bào)圖片了,點(diǎn)擊海報(bào)也能預(yù)覽,在預(yù)覽頁面長按就能把圖片轉(zhuǎn)發(fā)或者保存到電腦了,后面的操作無需任何方法。
整體頁面的代碼如下,直接復(fù)制過去,稍微調(diào)試下就能使用。
<template>
<view class="demo">
<canvas :style="{ width: canvasW + 'px', height: canvasH + 'px' }" canvas-id="myCanvas" id="myCanvas"
@click="saveHB"></canvas>
</view>
</template>
<script>
export default {
components: {},
data() {
return {
canvasW: 0, // 畫布寬
canvasH: 0, // 畫布高
picWidth: '', //圖片寬
picHight: 260, //圖片高
SystemInfo: {}, // 設(shè)備信息
goodsImg: {}, // 主圖
ewmImg: {}, // 二維碼圖片
ewmW: 100, // 二維碼大小
title: '營銷是做一切的事情,讓客戶來找我;銷售是做一切的事情,我去找客戶。',
name: '周希奇', // 推薦人
hbUrl: [], //儲(chǔ)存生成的圖片地址
}
},
async onLoad(params) {
//獲取從上衣頁面?zhèn)鬟^來的圖片參數(shù)
this.title = params.title
this.url = params.url
// 獲取設(shè)備信息,獲取設(shè)備的寬高,畫布做一樣的高度
this.SystemInfo = await this.getSystemInfo();
console.log('this.SystemInfo', this.SystemInfo)
this.canvasW = this.SystemInfo.windowWidth; // 畫布寬度
this.canvasH = this.SystemInfo.windowHeight - 150; //畫布高度
//獲取背景圖片的長與寬,按畫布的比例進(jìn)行縮放
this.goodsImg = await this.getImageInfo(this.url);
console.log('this.goodsImg', this.goodsImg)
//圖片的寬度等于整個(gè)寬度減掉兩邊的padding
this.picWidth = this.canvasW - 20
//圖片的高度= this.picWidth * 實(shí)際的寬度/原來的高度
this.picHight = this.picWidth * this.goodsImg.height / this.goodsImg.width
console.log('畫的圖片寬度為:', this.picWidth)
console.log('畫的圖片高度為:', this.picHight)
//二維碼的信息
this.ewmImg = '/static/erweima.png';
// 如果主圖,二維碼圖片,設(shè)備信息都獲取成功,開始繪制海報(bào),這里需要用setTimeout延時(shí)繪制,否則可能會(huì)出現(xiàn)圖片不顯示。
if (this.goodsImg.errMsg == 'getImageInfo:ok' && this
.SystemInfo.errMsg == 'getSystemInfo:ok') {
// console.log('ok')
uni.showToast({
icon: 'loading',
mask: true,
// duration: 5000,
title: '海報(bào)繪制中',
});
setTimeout(() => {
var ctx = uni.createCanvasContext('myCanvas', this);
// 填充背景色,白色
ctx.setFillStyle('#fff'); // 默認(rèn)白色
ctx.fillRect(0, 0, this.canvasW, this.canvasH) // fillRect(x,y,寬度,高度)
// 繪制商品主圖,二維碼
ctx.drawImage(this.goodsImg.path, 10, 10, this.picWidth, this.picHight)
ctx.drawImage(this.ewmImg, this.canvasW - 110, this.picHight + 45, this.ewmW, this.ewmW)
// 3、繪制商品標(biāo)題,多余文字自動(dòng)換行
ctx.setFontSize(18); // setFontSize() 設(shè)置字體字號(hào)
ctx.setFillStyle('#000000'); // setFillStyle() 設(shè)置字體顏色
/* str 這段代碼是我百度找的,參考別人的。canvas不能自動(dòng)換行,需要自行計(jì)算 */
let _strlineW = 0;
let _strLastIndex = 0; //每次開始截取的字符串的索引
// let _strHeight = this.canvasW * rant + 30; //繪制字體距離canvas頂部的初始高度
let _strHeight = this.picHight + 70;
let _num = 1;
for (let i = 0; i < this.title.length; i++) {
_strlineW += ctx.measureText(this.title[i]).width;
// if (_strlineW > this.canvasW * rant - 155) {
if (_strlineW > this.canvasW - 155) {
if (_num == 4 && 4) {
//文字換行數(shù)量大于二進(jìn)行省略號(hào)處理
ctx.fillText(this.title.substring(_strLastIndex, i - 5) + '...', 10,
_strHeight);
_strlineW = 0;
_strLastIndex = i;
_num++;
break;
} else {
ctx.fillText(this.title.substring(_strLastIndex, i), 20, _strHeight);
_strlineW = 0;
_strHeight += 20;
_strLastIndex = i;
_num++;
}
} else if (i == this.title.length - 1) {
ctx.fillText(this.title.substring(_strLastIndex, i + 1), 20, _strHeight);
_strlineW = 0;
}
}
/* end */
ctx.draw(true, (ret) => { // draw方法 把以上內(nèi)容畫到 canvas 中。
uni.showToast({
icon: 'success',
mask: true,
title: '繪制完成',
});
uni.canvasToTempFilePath({ // 保存canvas為圖片
canvasId: 'myCanvas',
quality: 1,
complete: (res) => {
this.hbUrl.push(res.tempFilePath)
},
})
});
}, 1000)
} else {
console.log('err')
}
uni.showShareMenu({
withShareTicket: true,
menus: ["shareAppMessage", "shareTimeline"]
})
},
methods: {
saveHB() {
// console.log('點(diǎn)擊了圖片')
uni.previewImage({
urls: this.hbUrl
})
},
// 獲取圖片信息
getImageInfo(image) {
return new Promise((req, rej) => {
uni.getImageInfo({
src: image,
success: (res) => {
req(res)
},
});
})
},
// 獲取設(shè)備信息
getSystemInfo() {
return new Promise((req, rej) => {
uni.getSystemInfo({
success: function(res) {
req(res)
}
});
})
},
//保存相片到本地
savePicture() {
uni.saveImageToPhotosAlbum({
filePath: this.hbUrl,
})
},
//分享相片
sharePicture() {
uni.share({
provider: 'weixin',
imageUrl: this.hbUrl,
type: 2,
scene: 'WXSceneTimeline'
})
},
},
}
</script>
<style lang="scss">
.button-container {
display: flex;
justify-content: center;
position: fixed;
top: 80%;
height: 100rpx;
width: 100%;
button {
width: 40%;
background-color: #0055ff;
}
}
</style>