移動(dòng)端分享海報(bào)生成
最近做項(xiàng)目需求是生成商品分享海報(bào)舱权,并且保存到手機(jī)中要兼容H5和小程序
與后端同學(xué)溝通后,海報(bào)在前端生成最省性能和有較好的交互體驗(yàn)涯雅,先看做好的效果
前端框架使用的是uni-app方便打包成H5和小程序
實(shí)現(xiàn)方案是拿到后端返回的數(shù)據(jù)后夺欲,利用canvas畫(huà)布把各個(gè)數(shù)據(jù)拼在一起并且生成一張圖片
主要的參數(shù)有:背景圖媒至、商品圖、二維碼季二、價(jià)格檩咱、原價(jià)、標(biāo)題
首先拿到產(chǎn)品圖和二維碼之后需要把它們下載下來(lái)用uni-app的api就可以
先寫(xiě)一個(gè)下載方法并且在template
定義畫(huà)布組件
<template>
<canvas class="canvas" canvas-id="myCanvas" v-if="canvasStatus"></canvas>
</template>
onReady(){
this.downloadFileImg('','pic');
this.downloadFileImg('','code');
},
methods:{
downloadFileImg(url,name){
let self = this
uni.downloadFile({
url: url,
success: function(res) {
self[name] = res.tempFilePath;
},
fail: function(erros) {
console.log(error)
}
});
}
}
這樣圖片就暫時(shí)保存到本地臨時(shí)文件了
uni.downloadFile
需要注意的是
在各個(gè)小程序平臺(tái)運(yùn)行時(shí)胯舷,網(wǎng)絡(luò)相關(guān)的 API 在使用前需要配置域名白名單刻蚯。在h5上是跨域的,用戶需要處理好跨域問(wèn)題桑嘶。
下來(lái)編寫(xiě)canvas生成圖片的方法
/**
* 獲取分享海報(bào)
* @param array imgArr 海報(bào)素材 0 背景圖 1商品圖 2二維碼
* @param string store_name 素材文字
* @param string price 價(jià)格
* @param string ot_price 原始價(jià)格
* @param function successFn 回調(diào)函數(shù)
*/
PosterCanvas: function(imgArr, store_name, price, ot_price, successFn) {
let that = this;
uni.showLoading({
title: '海報(bào)生成中',
mask: true
});
const ctx = uni.createCanvasContext('myCanvas');
ctx.clearRect(0, 0, 0, 0);
/**
* 只能獲取合法域名下的圖片信息,本地調(diào)試無(wú)法獲取
*
*/
ctx.fillStyle = '#fff';
ctx.fillRect(0, 0, 750, 1150);
uni.getImageInfo({
src: imgArr[0],
success: function(res) {
const WIDTH = res.width;
const HEIGHT = res.height;
ctx.drawImage(imgArr[1], 0, 0, WIDTH, WIDTH);
ctx.save();
let r = 110;
let d = r * 2;
let cx = 480;
let cy = 790;
ctx.arc(cx + r, cy + r, r, 0, 2 * Math.PI);
// ctx.clip();
ctx.drawImage(imgArr[2], cx, cy, d, d);
ctx.restore();
const CONTENT_ROW_LENGTH = 20;
let [contentLeng, contentArray, contentRows] = that.textByteLength(store_name, CONTENT_ROW_LENGTH);
if (contentRows > 2) {
contentRows = 2;
let textArray = contentArray.slice(0, 2);
textArray[textArray.length - 1] += '……';
contentArray = textArray;
}
ctx.setTextAlign('left');
ctx.setFontSize(36);
ctx.setFillStyle('#000');
// let contentHh = 36 * 1.5;
let contentHh = 36;
for (let m = 0; m < contentArray.length; m++) {
if (m) {
ctx.fillText(contentArray[m], 50, 1000 + contentHh * m + 18, 1100);
} else {
ctx.fillText(contentArray[m], 50, 1000 + contentHh * m, 1100);
}
}
ctx.setTextAlign('left')
ctx.setFontSize(72);
ctx.setFillStyle('#DA4F2A');
ctx.fillText('¥' + price, 40, 820 + contentHh);
ctx.setTextAlign('left')
ctx.setFontSize(36);
ctx.setFillStyle('#999');
ctx.fillText('¥' + ot_price, 50, 876 + contentHh);
var underline = function(ctx, text, x, y, size, color, thickness, offset) {
var width = ctx.measureText(text).width;
switch (ctx.textAlign) {
case "center":
x -= (width / 2);
break;
case "right":
x -= width;
break;
}
y += size + offset;
ctx.beginPath();
ctx.strokeStyle = color;
ctx.lineWidth = thickness;
ctx.moveTo(x, y);
ctx.lineTo(x + width, y);
ctx.stroke();
}
underline(ctx, '¥' + ot_price, 55, 865, 36, '#999', 2, 0)
ctx.setTextAlign('left')
ctx.setFontSize(28);
ctx.setFillStyle('#999');
ctx.fillText('長(zhǎng)按或掃描查看', 490, 1030 + contentHh);
ctx.draw(true, function() {
uni.canvasToTempFilePath({
canvasId: 'myCanvas',
fileType: 'png',
destWidth: WIDTH,
destHeight: HEIGHT,
success: function(res) {
uni.hideLoading();
successFn && successFn(res.tempFilePath);
}
})
});
},
fail: function(err) {
uni.hideLoading();
that.Tips({
title: '無(wú)法獲取圖片信息'
});
}
})
},
首先創(chuàng)建一個(gè)canvas
畫(huà)布
獲取背景圖圖片信息拿到寬和高再繪制商品圖片并保存
接下來(lái)繪制二維碼并把坐標(biāo)放好并保存
在處理文字換行問(wèn)題并設(shè)置大小顏色和對(duì)其方式
在相對(duì)應(yīng)的設(shè)置價(jià)格和原價(jià)的顏色和大小還有坐標(biāo)
由于價(jià)格還有條橫線炊汹,我在網(wǎng)上又搜下了橫線的做法直接看方法就行
最后生成圖片信息并且使用uni.canvasToTempFilePath
方法把當(dāng)前畫(huà)布指定區(qū)域的內(nèi)容導(dǎo)出生成指定大小的圖片,并返回文件路徑逃顶。
這樣我們就得到一個(gè).png的臨時(shí)文件現(xiàn)在就剩最后一步把文件渲染到組件里了讨便,從回調(diào)函數(shù)就可以回去到
此方法用的比方比較多我把它寫(xiě)到公共方法里面并且綁定到vue原型上面方便我們后面使用
現(xiàn)在編寫(xiě)方法調(diào)用
handelCanvas(){
let imgArr = ['背景圖路徑',this.pic,this.code]
this.$util.PosterCanvas(imgArr,'標(biāo)題','價(jià)格','原價(jià)',function(tempFilePath){
console.log(tempFilePath)
})
}
這樣就拿到生成好的圖片的是一個(gè)臨時(shí)文件 現(xiàn)在把他放到頁(yè)面中展示就ok了
保存圖片功能H5可以長(zhǎng)按保存圖片這里只用處理小程序的就行
首先檢測(cè)授權(quán)拿到授權(quán)后調(diào)用uni-app的api就可以了
savePosterPath: function() {
let that = this;
uni.getSetting({
success(res) {
if (!res.authSetting['scope.writePhotosAlbum']) {
uni.authorize({
scope: 'scope.writePhotosAlbum',
success() {
uni.saveImageToPhotosAlbum({
filePath: 'canvas生成的臨時(shí)圖片',
success: function(res) {
....成功了
},
fail: function(res) {
....失敗了
}
});
}
});
} else {
uni.saveImageToPhotosAlbum({
filePath: 'canvas生成的臨時(shí)圖片',
success: function(res) {
....成功了
},
fail: function(res) {
....失敗了
}
});
}
}
});
},
這樣前端生成海報(bào)就大功告成了充甚,你學(xué)廢了嗎?
最后打一波廣告:
CRMEB商城一個(gè)免費(fèi)開(kāi)源項(xiàng)目
移動(dòng)端使用uni-app框架目前已經(jīng)適配公眾號(hào)霸褒、小程序伴找、app(暫未發(fā)布)
管理后臺(tái)使用vue+iview框架
開(kāi)源不易,希望各位關(guān)注下废菱,說(shuō)不定你會(huì)有意外收獲技矮!