- 微信小程序使用新版Canvas画布实现电子签名
布局文件: <view class="container column-me"> <view class="tips"> 请绘制清晰可见的签名并保存 </view> <canvas class="canvas" id="canvas" type="2d" disable-scroll="true" bindtouchstart="canvasStart" bindtouchmove="canvasMove" bindtouchend="canvasEnd" touchcancel="canvasEnd" binderror="canvasIdErrorCallback"></canvas> <view class='addBtn'> <button type="default" class='txt reset' bindtap="clickClear">重新签名</button> <button type="default" class='txt' bindtap="clickSave">保存签名</button> </view> </view> 样式文件: page { background: #fff; } .container { padding: 0 20rpx; height: 100vh; background: #fff; border-radius: 5px; } .canvas { width: 100%; flex: 1; box-sizing: border-box; margin-top: 10rpx; } .tips { display: flex; align-items: center; justify-content: center; text-align: center; color: #aaa; font-size: 20rpx; } .addBtn { display: flex; align-items: center; justify-content: center; width: 100%; background: #fff; z-index: 100; padding: 10rpx 0; } .addBtn .txt { text-align: center; width: 200rpx; font-size: 15rpx; border-radius: 100px; background: #0097fe; color: #fff; box-sizing: border-box; margin: 0 20rpx; padding: 10px; z-index: 100; } .reset { background: #fff !important; border: 1px solid #cccccc; color: #333333 !important; } 配置文件: { "usingComponents": {}, "pageOrientation": "landscape", "navigationBarTitleText": "医生签名" } JS文件: let http = require("../../utils/http.js"); // canvas 全局配置 var context = null; // 使用 wx.createContext 获取绘图上下文 context var mCanvas = null; //注册页面 Page({ /** * 页面的初始数据 */ data: { userId: '', //用户id hasDraw: false, //默认没有画 }, /** * 生命周期函数--监听页面加载 */ onLoad: function (options) { this.setData({ userId: options.scene, hasDraw: false, }) wx.createSelectorQuery() .select('#canvas') // 在 WXML 中填入的 id .fields({ node: true, size: true }) .exec((res) => { // Canvas 对象 const canvas = res[0].node mCanvas = res[0].node // 渲染上下文 context = canvas.getContext('2d') // Canvas 画布的实际绘制宽高 const width = res[0].width const height = res[0].height // 初始化画布大小 const dpr = wx.getWindowInfo().pixelRatio canvas.width = width * dpr canvas.height = height * dpr context.scale(dpr, dpr) //绘制背景 context.fillStyle = '#fff' context.clearRect(0, 0, canvas.width, canvas.height) context.fillRect(0, 0, canvas.width, canvas.height) }) }, onShow() { }, canvasIdErrorCallback: function (e) {}, //开始 canvasStart: function (event) { context.moveTo(event.touches[0].x, event.touches[0].y); }, //过程 canvasMove: function (event) { var x = event.touches[0].x; var y = event.touches[0].y; context.lineWidth = 4 context.lineCap = 'round' context.lineJoin = 'round' context.lineTo(x, y); context.stroke(); this.setData({ hasDraw: true, }); }, canvasEnd: function (event) { }, clickClear: function () { context.clearRect(0, 0, mCanvas.width, mCanvas.height); //清除画布 context.beginPath() //清空画笔 this.setData({ hasDraw: false, }); }, //保存签名 clickSave: function () { let that = this if (!this.data.hasDraw) { wx.showModal({ title: '提示', content: '签名内容不能为空!', showCancel: false }); return false; }; if (this.data.userId === undefined || this.data.userId === '') { wx.showModal({ title: '提示', content: '用户id为空,请重新扫码', showCancel: false }); return false; } //生成图片 wx.canvasToTempFilePath({ canvas: mCanvas, success: function (res) { http.upload({ url: '/admin/upload/upLoadFile', filePath: res.tempFilePath, name: 'file', formData: {}, callBack: function (uploadRes) { let res = JSON.parse(uploadRes) let url = res.data.url http.request({ url: "/doctor/home/updateSignature", method: "POST", noBody: true, data: { id: that.data.userId, image: url, }, callBack: (res) => { wx.showModal({ title: '温馨提示', content: '保存签名成功!', confirmText: '退出', showCancel: false, success(res) { if (res.confirm) { wx.exitMiniProgram({ success: function (res) { console.log(res) }, fail: function (err) { console.log(err) } }) } else if (res.cancel) { console.log('用户点击取消') } } }) } }) } }) } }) }, })
2023-08-04 - app.onLaunch与page.onLoad异步问题
问题:相信很多人都遇到过这个问题,通常我们会在应用启动app.onLaunch() 去发起静默登录,同时我们需要在加载页面的时候,去调用一个需要登录态的后端 API 。由于两者都是异步,往往page.onload()调用API的时候,app.onLaunch() 内调用的静态登录过程还没有完成,从而导致请求失败。 解决方案:1. 通过回调函数// on app.js App({ onLaunch() { login() // 把hasLogin设置为 true .then(() => { this.globalData.hasLogin = true; if (this.checkLoginReadyCallback) { this.checkLoginReadyCallback(); } }) // 把hasLogin设置为 false .catch(() => { this.globalData.hasLogin = false; }); }, }); // on page.js Page({ onLoad() { if (getApp().globalData.hasLogin) { // 登录已完成 fn() // do something } else { getApp().checkLoginReadyCallback = () => { fn() } } }, }); ⚠️注意:这个方法有一定的缺陷(如果启动页中有多个组件需要判断登录情况,就会产生多个异步回调,过程冗余),不建议采用。 2. 通过Object.defineProperty监听globalData中的hasLogin值 // on app.js App({ onLaunch() { login() // 把hasLogin设置为 true .then(() => { this.globalData.hasLogin = true; }) // 把hasLogin设置为 false .catch(() => { this.globalData.hasLogin = false; }); }, // 监听hasLogin属性 watch: function (fn) { var obj = this.globalData Object.defineProperty(obj, 'hasLogin', { configurable: true, enumerable: true, set: function (value) { this._hasLogin = value; fn(value); }, get: function () { return this._hasLogin } }) }, }); // on page.js Page({ onLoad() { if (getApp().globalData.hasLogin) { // 登录已完成 fn() // do something } else { getApp().watch(() => fn()) } }, }); 3. 通过beautywe的状态机插件(项目中使用该方法) // on app.js import { BtApp } from '@beautywe/core/index.js'; import status from '@beautywe/plugin-status/index.js'; import event from '@beautywe/plugin-event/index.js'; const app = new BtApp({ onLaunch() { // 发起静默登录调用 login() // 把状态机设置为 success .then(() => this.status.get('login').success()) // 把状态机设置为 fail .catch(() => this.status.get('login').fail()); }, }); // status 插件依赖于 beautywe-plugin-event app.use(event()); // 使用 status 插件 app.use(status({ statuses: [ 'login' ], })); // 使用原生的 App 方法 App(app); // on page.js Page({ onLoad() { // must 里面会进行状态的判断,例如登录中就等待,登录成功就直接返回,登录失败抛出等。 getApp().status.get('login').must().then(() => { // 进行一些需要登录态的操作... }) }, }); 具体实现 具体实现可以参考我的商城小程序项目 项目体验地址:体验 代码:代码
2021-05-20