微信小程序分享海報開發總結
發表時(shí)間:2021-1-5
發布人(rén):融晨科技
浏覽次數:72
- 請求海報數據
- 加載圖片
- 初始化canvas
- 繪制文字
- 繪制區塊
- 微信小程序碼獲取
- 繪制圖片
- 導出(chū)圖片内容
- 小程序碼解析
正常情況下,1px在(zài)屏幕上(shàng)就(jiù)顯示1px,但是(shì)在(zài)高清屏幕上(shàng),這(zhè)個(gè)情況就(jiù)不(bù)一樣了(le/liǎo),以(yǐ)iPhone4S爲(wéi / wèi)例,它的(de)devicePixelRatio爲(wéi / wèi)2,将看到(dào)100px的(de)邏輯值等于(yú)200px的(de)設備值。在(zài)元素的(de)邏輯像素寬度上(shàng)創建圖像,當它們被繪制出(chū)來(lái)時(shí),它們會被設備像素按比例放大(dà),并且會變得模糊。解決這(zhè)個(gè)問題的(de)方法是(shì)創建按devicePixelRatio縮放的(de)圖像,然後使用CSS按相同的(de)比例縮小.
canvas 的(de)width 和(hé / huò) height控制元素位圖的(de)屬性,如不(bù)設置默認爲(wéi / wèi)width=300,height=150
// 獲取canvas
function getCanvas() {
return new Promise((resolve, reject) => {
Taro.createSelectorQuery()
.select('#myCanvas')
.fields({
node: true,
size: true
})
.exec(res => {
res ? resolve(res[0]) : reject(null);
});
});
}
/**
* 初始化canvas并返回 ctx
*
* @param {Object} res
* @returns {*}
*/
function setContext(res: Object): any {
let {canvas, width, height} = res;
const ctx = canvas.getContext('2d');
const dpr = Taro.getSystemInfoSync().pixelRatio;
// 防止重複放大(dà)處理
if (canvas.width !== width * dpr) {
// 設置畫布繪畫尺寸 = CSS尺寸 * 設備像素比率。
canvas.width = width * dpr;
canvas.height = height * dpr;
// 通過dpr縮放所有繪圖操作
ctx.scale(dpr, dpr);
}
return ctx;
}
let {node: canvas, width, height} = await getCanvas();
let ctx = setContext({canvas, width, height});
// 繪制前先清除,以(yǐ)防被原有圖案影響
ctx.clearRect(0, 0, width, height);
ctx.fillStyle = '#ffffff';
複制代碼
canvas标簽,微信對Canvas做了(le/liǎo)同層渲染的(de)優化,特别注意不(bù)要(yào / yāo)将canvas标簽放到(dào)子(zǐ)組件,必須放到(dào)page頁,否則無法獲取的(de)canvas對象
<Canvas type='2d' id='myCanvas' style='width: 414px; height: 736px;' />
複制代碼
微信小程序碼獲取
- 接口 A: 适用于(yú)需要(yào / yāo)的(de)碼數量較少的(de)業務場景
- 生成小程序碼,可接受 path 參數較長,生成個(gè)數受限,請謹慎使用。
- 接口 B:适用于(yú)需要(yào / yāo)的(de)碼數量極多的(de)業務場景
- 生成小程序碼,可接受頁面參數較短,生成個(gè)數不(bù)受限。
- 接口 C:适用于(yú)需要(yào / yāo)的(de)碼數量較少的(de)業務場景
- 生成二維碼,可接受 path 參數較長,生成個(gè)數受限。
我們使用的(de)是(shì)接口B,需要(yào / yāo)注意的(de)點,
- scene參數隻接收32的(de)長度
- 小程序未發布時(shí),page參數必須爲(wéi / wèi)空,scene=xxxx,才可以(yǐ)獲取到(dào)二維碼
- 接口返回的(de)數據需要(yào / yāo)爲(wéi / wèi)base64格式方便canvas繪制時(shí)使用
Taro.getImageInfo({
src: imgUrl
}).than(res => {
let { path } = res;
let imgtag = canvas.createImage();
imgtag.src = http://www.wxapp-union.com/path;
imgtag.onload = res => {
ctx.save();
ctx.drawImage(imgtag, 26, 615, 2 * 27, 2 * 27);
ctx.restore();
};
});
複制代碼
圖片裁剪
/**
* 畫圓
*
* @export
* @param {IDrawRound} options
* @example
* drawRound({
* ctx,
* r: 19,
* x: 22,
* y: 15,
* next() {
* ctx.fillStyle = '#F5F5F5';
* ctx.fill();
* }
* });
*/
export function drawRound(options: IDrawRound) {
let { ctx, r, x, y, next } = options;
ctx.save(); // 保存之(zhī)前的(de)
let cx = x + r; // 圓弧坐标x
let cy = y + r; // 圓弧坐标 y
ctx.arc(cx, cy, r, 0, 2 * Math.PI);
if (typeof next === 'function') {
next();
}
ctx.restore(); // 返回上(shàng)一狀态
}
// 繪制圓形頭像
drawRound({
ctx,
r: 27,
x: 26,
y: 615,
next() {
ctx.clip();
ctx.drawImage(imgtag, 26, 615, 2 * 27, 2 * 27); // 畫頭像
}
});
複制代碼
繪制圓角矩形
/**
* 繪制圓角矩形,需自行填充顔色
*
* @export
* @param {IDrawRound} options
*
* @example
* drawRoundRect({
* ctx,
* r: 13,
* x: 26,
* y: 80,
* w: 361,
* h: 361,
* next() {
* ctx.clip();
* ctx.drawImage(imgtag, 26, 80, 361, 361);
* }
* });
*/
export function drawRoundRect(options: IDrawRoundRect) {
let {ctx, x, y, w, h, r, next} = options;
ctx.save();
if (w < 2 * r) {
r = w / 2;
}
if (h < 2 * r) {
r = h / 2;
}
ctx.beginPath();
ctx.arc(x + r, y + r, r, Math.PI, Math.PI * 3 / 2);
ctx.lineTo(w - r + x, y);
ctx.arc(w - r + x, r + y, r, Math.PI * 3 / 2, Math.PI * 2);
ctx.lineTo(w + x, h + y - r);
ctx.arc(w - r + x, h - r + y, r, 0, Math.PI * 1 / 2);
ctx.lineTo(r + x, h + y);
ctx.arc(r + x, h - r + y, r, Math.PI * 1 / 2, Math.PI);
ctx.closePath();
if (typeof next === 'function') {
next();
}
ctx.restore(); // 返回上(shàng)一狀态
}
// 底部粉色圓角矩形
drawRoundRect({
ctx,
r: 15,
x: 26,
y: 687,
w: 147,
h: 23,
next() {
// 漸變填充
var grd = ctx.createLinearGradient(0, 0, 200, 0);
grd.addColorStop(0, '#E828FF');
grd.addColorStop(0.7, '#FF1D74');
grd.addColorStop(1, '#FFB050');
ctx.fillStyle = grd;
ctx.fill();
ctx.strokeStyle = grd;
ctx.stroke();
}
});
複制代碼
繪制文字
/**
* 繪制文字
*
* @export
* @param {*} ctx canvas的(de) 2d 對象
* @param {string} t 繪制的(de)文字
* @param {number} x
* @param {number} y
* @param {number} w 文字寬度
* @param {number} [l=30] 行高
* @param {number} [limit=2] 行數
*/
export function drawText(ctx: any, t: string, x: number, y: number, w: number, l:number = 30, limit:number = 2) {
// 參數說(shuō)明
// ctx:canvas的(de) 2d 對象,t:繪制的(de)文字,x,y:文字坐标,w:文字最大(dà)寬度
let chr = t.split('');
let temp = '';
let row = [];
let limitIndex = 0;
let wordsNum = 0;
for (let a = 0; a < chr.length; a++) {
wordsNum++;
if (ctx.measureText(temp).width < w && ctx.measureText(temp + (chr[a])).width <= w) {
temp += chr[a];
}
else {
// 行數+1
limitIndex++;
if (limitIndex < limit) {
row.push(temp);
temp = chr[a];
}
else {
break;
}
}
}
// 最後一行超出(chū)最大(dà)字數
if (limitIndex === limit && chr.length > wordsNum) {
temp = temp.substring(0, temp.length - 1) + '...';
}
row.push(temp);
for (let b = 0; b < row.length; b++) {
ctx.fillText(row[b], x, y + (b + 1) * l);// 每行字體y坐标間隔30
}
};
複制代碼
繪制完成導出(chū)圖片
export async function draw() {
let { node: canvas, width, height } = await getCanvas();
let ctx = setContext({ canvas, width, height });
// 繪制前先清除,以(yǐ)防原有圖案被影響
ctx.clearRect(0, 0, width, height);
ctx.fillStyle = '#ffffff';
ctx.fillRect(0, 0, 414, 736);
// 底部灰色塊
ctx.fillStyle = '#f5f5f5';
ctx.fillRect(0, 596, 414, 140);
// 底部粉色圓角矩形
drawRoundRect({
ctx,
r: 15,
x: 26,
y: 687,
w: 147,
h: 23,
next() {
// 漸變填充
var grd = ctx.createLinearGradient(0, 0, 200, 0);
grd.addColorStop(0, '#E828FF');
grd.addColorStop(0.7, '#FF1D74');
grd.addColorStop(1, '#FFB050');
ctx.fillStyle = grd;
ctx.fill();
ctx.strokeStyle = grd;
ctx.stroke();
}
});
// 推薦人(rén)
ctx.fillStyle = '#666666';
ctx.font = '13px/1.2 PingFangSC-Regular';
ctx.fillText(`${nickName}`, 93, 635);
// 推薦語
ctx.fillStyle = '#222222';
ctx.font = '15px/1.2 PingFangSC-Regular';
ctx.fillText(`${recommendation || '推薦你一個(gè)超值的(de)變美項目'}`, 93, 660);
// 底部文字
ctx.fillStyle = '#ffffff';
ctx.font = '13px/1.2 PingFangSC-Regular';
ctx.fillText('長按識别小程序購物', 43, 703);
// 省略部分圖片繪制代碼
// 獲取圖片臨時(shí)路徑
let { tempFilePath } = await Taro.canvasToTempFilePath({ canvas } as any);
return Promise.resolve(tempFilePath);
};
複制代碼
以(yǐ)上(shàng)導出(chū)的(de)圖片地(dì / de)址可以(yǐ)直接給img标簽賦值
const res = await Taro.saveImageToPhotosAlbum({
filePath: tempFilePath
});
if (res.errMsg === 'saveImageToPhotosAlbum:ok') {
Taro.showToast({
title: '保存圖片成功',
icon: 'success',
duration: 2000
});
}
複制代碼
小程序碼解析
小程序碼的(de)解析後,在(zài)page裏收到(dào)的(de)參數在(zài)scene裏
componentWillMount() {
let params = this.$router.params;
// 通過小程序碼進入,參數在(zài)scene裏
if (params.scene) {
let scene = decodeURIComponent(params.scene);
let {id} = query2parmas(scene);
this.setState({
goodsId: id
});
}
// 通過分享卡片進入參數直接在(zài)params裏
if (params.id) {
this.setState({
goodsId: params.id
});
}
}