【微信小程序

时间: 2023-07-11 admin IT培训

【微信小程序

【微信小程序

可在系列教程的基础上继续开发,也可以单独使用

【微信小程序-原生开发】系列教程

效果预览

代码实现

点击触发生成海报

在活动详情页,指定点击某图标/按钮,触发跳转到生成海报的页面

pages\components\party\detail\index.js

  getPoster() {let detail = this.data.detailwx.navigateTo({url: '/pages/components/poster/index',success: function (res) {// 跳转页面时,将活动详情传递过去res.eventChannel.emit('sendData', {data: detail})}})},

海报页加载时接收参数,开始生成海报

pages\components\poster\index.js

  onLoad() {let that = this// 接收列表页传入的复杂数据--对象(详情)const eventChannel = this.getOpenerEventChannel()eventChannel.on('sendData', function (res) {that.setData({detail: res.data})wx.showLoading({title: '生成中',})//开始生成海报that.getPoster()})},

海报的绘制过程

海报通过Canvas进行绘制

1. 获取系统屏幕宽高,确定画布的宽高

  // 生成海报getPoster() {let that = this// 获取屏幕宽高wx.getSystemInfo({success(res) {that.drawCanvas(res.windowWidth, res.windowHeight)}})},

2. 创建Canvas对象,进行绘制,并生成图片

详见代码中的注释

<canvas class="canvasClass" id="mycanvas" type="2d" />
Page {background-color: black;
}.canvasClass {width: 80%;height: 80vh;margin: 40rpx auto
}
  // 绘制海报drawCanvas(windowWidth, windowHeight) {let that = this;let detail = this.data.detail// 根据id查找到页面中的 canvas 标签,生成 Canvas 对象wx.createSelectorQuery()// # 后为页面中的 canvas 标签的 id 属性.select('#mycanvas').fields({node: true,size: true}).exec(async (res) => {// 获取到 Canvas 对象const canvas = res[0].node// 渲染上下文const ctx = canvas.getContext('2d')// Canvas 画布的实际绘制宽高const width = windowWidthconst height = windowHeight// 获取设备像素比const dpr = wx.getWindowInfo().pixelRatio// 清空画布ctx.clearRect(0, 0, width, height)// 初始化画布大小canvas.width = width * dprcanvas.height = height * dprctx.scale(dpr, dpr)// 绘制画布底色ctx.fillStyle = "white";ctx.fillRect(0, 0, canvas.width, canvas.height);// 解析活动封面图片let posterImgURL = detail.poster// 获取源图片宽高let posterImgInfo = await that.getImgInfo(posterImgURL)// 计算源图片的宽高比let posterImgRate = posterImgInfo.width / posterImgInfo.height// 计算出新的图片宽高(此处以宽为基准,按80%的宽等比例缩放图片)let newWidth = 0.8 * widthlet newHeight = 0.8 * width / posterImgRate// 绘制文本 -- 水平居中(活动名称)let text1 = detail.title;ctx.fillStyle = "black";ctx.font = "bold 25px 微软雅黑";let y1 = width / 10 + newHeight + 50ctx.fillText(text1, that.getTextxPosition(ctx, width, text1), y1);// 绘制文本 -- 水平居中(活动时间)let text2 = detail.date + ' ' + detail.week + ' ' + detail.timectx.font = "15px 微软雅黑";let y2 = y1 + 30ctx.fillText(text2, that.getTextxPosition(ctx, width, text2), y2);// 绘制文本 -- 水平居中(活动地点)let text3 = detail.placeMark || detail.placeInfo.titlectx.font = "15px 微软雅黑";let y3 = y2 + 30ctx.fillText(text3, that.getTextxPosition(ctx, width, text3), y3);// 绘制文本 -- 水平居中let codeTips = "长按识别二维码报名";ctx.font = "20px bold";ctx.fillStyle = "green";let y4 = y3 + 40ctx.fillText(codeTips, that.getTextxPosition(ctx, width, codeTips), y4);// 绘制图片--活动封面图片let posterImg = canvas.createImage()posterImg.src = posterImgURLposterImg.onload = async () => {ctx.drawImage(posterImg, (width - newWidth) / 2, width / 10, newWidth, newHeight)// 绘制图片--小程序二维码const codeImg = canvas.createImage()// 异步生成小程序二维码codeImg.src = await that.initCodeImg()codeImg.onload = () => {let y5 = y4 + 20ctx.drawImage(codeImg, width / 3, y5, width / 3, width / 3)// 生成图片wx.canvasToTempFilePath({canvas,success: res => {wx.hideLoading()that.setData({// 生成的图片临时文件路径tempFilePath: res.tempFilePath})},})}}})},

技术要点一:画布的最终大小需要按设备像素比进行转换

        // Canvas 画布的实际绘制宽高const width = windowWidthconst height = windowHeight// 获取设备像素比const dpr = wx.getWindowInfo().pixelRatio// 初始化画布大小canvas.width = width * dprcanvas.height = height * dprctx.scale(dpr, dpr)

技术要点二:需先清空画布后绘制底色

  • 为避免多次渲染导致累积层叠绘制,每次都需清空画布
  • 若未绘制底色,则最终保存的海报会是透明的(若想生成背景透明的海报,请删除绘制画布底色的代码)
        // 清空画布ctx.clearRect(0, 0, width, height)// 绘制画布底色ctx.fillStyle = "white";ctx.fillRect(0, 0, canvas.width, canvas.height);

技术要点三:保持图片的宽高比

        // 解析活动封面图片let posterImgURL = detail.poster// 获取源图片宽高let posterImgInfo = await that.getImgInfo(posterImgURL)// 计算源图片的宽高比let posterImgRate = posterImgInfo.width / posterImgInfo.height// 计算出新的图片宽高(此处以宽为基准,按80%的宽等比例缩放图片)let newWidth = 0.8 * widthlet newHeight = 0.8 * width / posterImgRate

此处需要异步解析图片,获取源图片的宽高,来计算宽高比

  // 获取图片信息getImgInfo(imgURL) {return new Promise((reslove) => {wx.getImageInfo({src: imgURL,success(res) {reslove(res);}})})},

通过返回 Promise ,方便借助 async和 await 将异步变同步,避免过多的代码嵌套。

技术要点四:绘制水平居中的文字

        // 绘制文本 -- 水平居中(活动名称)let text1 = detail.title;ctx.fillStyle = "black";ctx.font = "bold 25px 微软雅黑";let y1 = width / 10 + newHeight + 50ctx.fillText(text1, that.getTextxPosition(ctx, width, text1), y1);
  • fillText 的参数依次为:文本内容、文本的 x 坐标,文本的 y 坐标
  • 水平居中的 x 坐标计算方法为:(画布宽度-文本宽度)/ 2
  // 获取居中文本的x坐标getTextxPosition(ctx, ctxWidth, text) {let textWidth = ctx.measureText(text).width;let xPosition = ctxWidth / 2 - textWidth / 2;return xPosition},

技术要点五:绘制图片

        // 绘制图片--活动封面图片let posterImg = canvas.createImage()posterImg.src = posterImgURLposterImg.onload = () => {ctx.drawImage(posterImg, (width - newWidth) / 2, width / 10, newWidth, newHeight)}
  • 图片若是网络图片,则需等其异步加载完成后再绘制,所以后续生成图片等操作,都需在图片的 onload 函数中执行

技术要点六:生成小程序二维码

  // 生成小程序二维码async initCodeImg() {let {codeImgURL} = this.dataif (!codeImgURL) {// 生成小程序二维码let codeImgID = await this.getCodeImg()// 云存储的图片,需要通过云id去换取外网访问链接let result = await wx.cloud.getTempFileURL({fileList: [codeImgID]})codeImgURL = result.fileList[0].tempFileURL}return codeImgURL},
  // 通过云函数,生成小程序二维码getCodeImg() {let detail = this.data.detaillet params = detail._idreturn new Promise((resolve) => {wx.cloud.callFunction({name: 'get_codeImg', // 云函数的名称data: {// 页面路径path: 'pages/components/party/detail/index',// 参数params: params}}).then(res => {// 从云函数返回的结果中提取出目标数据resolve(res.result.fileID);})})},

云函数–生成小程序二维码

此处自定义的云函数名称为 get_codeImg

cloudfunctions\get_codeImg\index.js

// 云函数入口文件
const cloud = require('wx-server-sdk')cloud.init({env: cloud.DYNAMIC_CURRENT_ENV
}) // 使用当前云环境// 云函数入口函数
exports.main = async (event, context) => {const wxacodeResult = await cloud.openapi.wxacode.getUnlimited({// 页面路径page: event.path,// 参数scene: event.params,})let suffix = event.path.replace(/\//g, '_') + '_' + event.params// 在云存储生成图片const uploadResult = await cloud.uploadFile({// 图片存储路径cloudPath: `codeImgs/wxacode_${suffix}.jpg`,fileContent: wxacodeResult.buffer,})return uploadResult
}
  • 此处需注意,因参数通过 scene 场景值传递(且scene限定了长度,刚好和默认的 _id 值等长),在目标页面需添加代码识别该参数
  onLoad(options) {// 通过id获取详情(扫描小程序二维码时,id在scene里 )let id = options.id || options.sceneif (id) {this.setData({id: id})this.getDetail()return}
}

技术要点七:将canvas转换为图片

此步可在 canvas 绘制后执行,也可以在点击保存海报时在执行

            // 生成图片wx.canvasToTempFilePath({canvas,success: res => {that.setData({// 生成的图片临时文件路径tempFilePath: res.tempFilePath})},})

保存图片到手机

点击保存按钮时执行

<view class="btnBanner"><t-button style="margin-right: 40rpx;" block theme="light" capture-bind:tap="cancel" size="medium">取消</t-button><t-button block capture-bind:tap="saveImg" theme="primary" size="medium">保存</t-button>
</view>
.btnBanner {padding-top: 20rpx;display: flex;justify-content: space-evenly;margin: 0rpx 10%;
}
  //保存到手机相册saveImg() {wx.saveImageToPhotosAlbum({filePath: this.data.tempFilePath,success(res) {wx.showToast({title: '已保存到相册',icon: 'success',duration: 3000})}})},
  // 取消cancel() {wx.navigateBack()},

注意事项

  • 不同的海报,内容和布局不同,需要修改对应的 canvas 绘制代码

更多技巧

canvas 系列教程

微信小程序的 canvas 绘制已与 web 中 canvas 的绘制统一,相关的绘制技术,可参考

  • 01——直线、三角形、多边形、矩形、调色板_canvas调色板

  • 02——圆、弧线、圆角矩形、曲线(气泡、心形、N叶草)、扇形_canvas 半圆矩形

  • 03 —— 线的样式、绘制文本、操作图片(图片的渲染、缩放、裁剪、切割、平铺、特效)、变换元素(平移、缩放、旋转)_canvas putimagedata 缩放

  • 04 —— 渐变、阴影、路径、状态、Canvas对象、图形重叠模式_canvas strokerect

  • 05 ——交互、动画_canvas 交互

  • 06 ——边界检测、碰撞检测_canvas碰撞检测

  • 07 ——捕获、拖拽、抛掷、缓动动画、弹性动画_canvas管道动画