微信小程序 — 总结
第一次接触微信小程序项目 – 小记
- 环境: 小程序基于mpvue - 电商类 H5公众号
- 授权(注册/自动登录)
- 生成分享海报图片(本章使用基础的canvas绘制)
(小程序和公众号通过 unionid 关联,公众号通过 userinfo 进行授权,拿到用户信息及 unionid 在H5上授权之后,小程序可直接静默登录,反之亦然。)
<!-- 事件授权 -->
<button class="get_user_info_button"
open-type="getUserInfo"
lang="zh_CN"
@getuserinfo="bingGetUserInfo"
>授权</button>
bingGetUserInfo(e){
let params = e.target;
let userInfo = params.userInfo;
let getUnion = {
encryptedData: params.encryptedData,
iv: params.iv,
session_key: getStorage('session_key')
}
// 通过session_key 、iv 和 encryptedData 调用微信接口解密,获取用户的unionid (session_key 通过 code 获取)
getUnionid(getUnion).then(res => {
// 获取 unionid -> 请求用户信息
// if 存在用户
// if 是否绑定手机号(通过用户id获取是否绑定手机号)
// 登录
// else 注册
// 填写手机号进行注册
// else 不存在
// 通过 userInfo 注册用户
}).cath(err => {...});
}
// 绘制海报图片(生成的海报不同,可改动参数)
function createLocalImg(canvas, url) {
return new Promise((resolve, reject) => {
const img = canvas.createImage()
img.onload = function () {
resolve(img)
}
img.onerror = err => {
reject({
message: `图片加载失败${url}`
})
console.error('图片加载失败', url)
}
img.src = url
})
}
// 绘制圆角矩形
function drawRoundedRect(ctx, x, y, width, height, r, fill, stroke) {
ctx.save();
ctx.beginPath(); // 绘制顶部和右上角
ctx.moveTo(x + r, y);
ctx.arcTo(x + width, y, x + width, y + r, r); // 绘制右侧和右下角
ctx.arcTo(x + width, y + height, x + width - r, y + height, r); // 绘制底部和左下角
ctx.arcTo(x, y + height, x, y + height - r, r); // 绘制左上角
ctx.arcTo(x, y, x + r, y, r);
if (fill) ctx.fill();
if (stroke) ctx.stroke();
ctx.restore();
}
function drawShareImg(info) {
console.log("draw canvas", info)
return new Promise(async (resolve, reject) => {
const query = wx.createSelectorQuery()
query.select('#canvasPoster').fields({
node: true,
size: true
}).exec(async res => {
try {
const canvas = res[0].node;
const ctx = canvas.getContext('2d');
const sysInfo = wx.getSystemInfoSync();
let dpr = sysInfo.pixelRatio;
const width = res[0].width;
const height = res[0].height;
// 避免安卓机挂掉 安卓机dpr最大为2
if (sysInfo.platform === 'android' && dpr > 2) {
dpr = 2;
}
// 产品图偏移量
let offset = info.offset
// y轴坐标
let canvasY = 0
function scaleSize(size) {
return width / 750 * size
}
canvas.width = width * dpr;
canvas.height = height * dpr;
// 避免安卓机挂掉 安卓机dpr最大为2
if (sysInfo.platform === 'android' && dpr > 2) {
canvas.height = canvas.height - scaleSize(200);
}
ctx.scale(dpr, dpr)
// 绘制背景色
ctx.fillStyle = '#FFFFFF'
ctx.fillRect(0, canvasY, width, height)
// 绘制背景图
let prodHeight = width
const prodImg = await createLocalImg(canvas, info.cover)
ctx.drawImage(prodImg, 0, canvasY, width, prodHeight)
// 绘制圆角矩形中的内容
let qsChildBoxY = prodHeight + scaleSize(10);
ctx.fillStyle = '#FF6300';
drawRoundedRect(ctx, scaleSize(36), qsChildBoxY, scaleSize(150), scaleSize(52), scaleSize(6), true, false);
ctx.strokeStyle = "#FF6300";
let qsChildTextY = prodHeight + scaleSize(44);
ctx.font = `${scaleSize(28)}px sans-serif`;
ctx.fillStyle = '#FFFFFF';
ctx.fillText('项目名称', scaleSize(54), qsChildTextY);
canvasY += width + (scaleSize(50))
ctx.font = `${scaleSize(30)}px sans-serif`
ctx.fillStyle = '#1a1a1a'
ctx.textAlign = 'left'
// 字符分隔为数组 字符长度大于13留两个缩进字符
if (info.name.length > 13) {
let textArr = info.name.split('');
let textHeight = 0; // 文本最终占据高度
let textOfLine = ''; // 每行显示的文字
let indentText = scaleSize(36); // 首行缩进
let textWidth = scaleSize(596); // 标题宽度
let limitRow = 2; // 控制行数
let rowCount = 0;
// 循环分割的文字数组
for (let i = 0; i < textArr.length; i++) {
let singleWord = textArr[i]; // 获取单个文字或字符
let connectText = textOfLine + singleWord; // 连接文字
let isLimitRow = limitRow ? rowCount === (limitRow - 1) : false; // 计算接下来要写的是否是最后一行
let measureText = isLimitRow ? (connectText + '...') : connectText; // 最后一行则显示省略符,否则显示连接文字
ctx.font = `${scaleSize(40)}px sans-serif`; // 设置字体并计算宽度,判断是否存在首行缩进
let width = ctx.measureText(measureText).width + scaleSize(36);
let conditionIndent = (indentText && rowCount === 0); // 首行需要缩进满足条件
let measureWidth = conditionIndent ? (width + indentText) : width;
let xPos = scaleSize(192);
// 大于限制宽度且已绘行数不是最后一行,则写文字
if (measureWidth > textWidth && i > 0 && rowCount !== limitRow) {
let canvasText = isLimitRow ? measureText : textOfLine;
let xPos = conditionIndent ? scaleSize(192) : scaleSize(36); // 如果是最后一行,显示计算文本
ctx.fillStyle = '#000'; // 写文字
ctx.fillText(canvasText, xPos, canvasY);
textOfLine = singleWord; // 下一行文字
canvasY += scaleSize(60); // 记录下一行位置
textHeight += scaleSize(36); // 计算文本高度
rowCount++;
if (isLimitRow) break;
} else {
textOfLine = connectText; // 不大于最大宽度
}
}
} else {
ctx.font = `${scaleSize(40)}px sans-serif`;
ctx.fillStyle = '#1a1a1a'
ctx.textAlign = 'left'
let ptext = info.name
let arrText = ptext.split('')
let prox = scaleSize(192)
let proy = canvasY
let lineHeight = scaleSize(44)
let maxWidth = scaleSize(700)
let line = ''
for (var n = 0; n < arrText.length; n++) {
let testLine = line + arrText[n]
let metrics = ctx.measureText(testLine)
let testWidth = metrics.width
if (testWidth > maxWidth && n > 0) {
ctx.fillText(line, prox, proy)
line = arrText[n]
proy += lineHeight
} else {
line = testLine
}
}
ctx.fillText(line, prox, proy)
}
let codeImgY = canvasY - scaleSize(150);
const miniCodeImg = await createLocalImg(canvas, info.codeImg)
ctx.drawImage(miniCodeImg, scaleSize(536), codeImgY, scaleSize(180), scaleSize(180))
canvasY += scaleSize(60)
ctx.font = `${scaleSize(24)}px sans-serif`
ctx.textAlign = 'center'
ctx.fillStyle = '#999'
ctx.fillText(`长按识别`, (width - scaleSize(130)), canvasY)
resolve(canvas)
} catch (error) {
reject(error)
wx.showToast({
title: error.message,
icon: 'none'
})
}
})
})
}
export default drawShareImg;