- 这些 Canvas 小技巧,保证你新年用得上
来自「微信开发者」公众号,作者为微信小程序技术研发工程师binnie。 本文主要介绍了3个隐藏的 Canvas 小技巧: - 绘制并生成图片 - Video 绘制 Canvas / webgl - 视频解码并绘制到 webgl - 录制并导出 webgl 视频 一键加滤镜 快速合成音视频 轻松挑选视频封面 …… Canvas 能够做这些? 作为资深的开发者,相信大家对 Canvas 都不陌生。这项能力在绘制图形方面发挥着极大的作用,高效支持图片编辑、数据可视化等应用场景。但是只局限于一般能力应用,那格局就小了。 Canvas 的应用场景非常丰富!赶紧往下看看这些隐藏的 Canvas 小技巧,保证你新年用得上!还有手把手教程以及文末彩蛋哟。 -- • 绘制并生成图片 • -- [图片] 示例:新年模板长按保存祝福 适用场景:图片分享海报 相关 API:RenderingContext/Canvas/wx.canvasToTempFilePath Step 1: 创建实例获取对象 创建 Canvas 实例,获取 CanvasRenderingContext2D 对象(Canvas 绘图上下文)来绘制形状、文本、图像等。 const query = wx.createSelectorQuery() let canvas = null query.select('#myCanvas') .fields({ node: true, size: true }) .exec((res) => { // 通过 wx.createSelectorQuery 获取到 canvas 实例 canvas = res[0].node // 通过 canvas.getContext('2d') 获取 CanvasRenderingContext2D 对象 const ctx = canvas.getContext('2d') }) Step 2: 设置宽高调整图片 获取 Canvas 绘图上下文后,将 Canvas 的宽高设置为节点宽高 * 设备像素比,绘制出来的图片更清晰 // 获取设备像素比 const dpr = wx.getSystemInfoSync().pixelRatio // 将 canvas 宽高设置为 canvas.width = res[0].width * dpr canvas.height = res[0].height * dpr Step 3: 绘制内容 使用 CanvasRenderingContext2D 绘制,根据业务需要在画布中绘制头像、文字、背景等 // 矩形 ctx.fillStyle = '#FFFFFF' ctx.fillRect(0, 0, canvas.width , canvas.height ) // 图片 var image = canvas.createImage() himage.src = 'https://example.com/example.jpg' headImage.onload = (res) => { ctx.drawImage(himage 0, 0, 32, 32; } // 文本 ctx.font = "18px SimHei"; ctx.textAlgin = "left" ctx.fillStyle = "#07c160"; ctx.fillText("这是我的名字", 0, 0); Step 4: 生成并保存本地 使用 wx.canvasToTempFilePath 将画布生成图片,wx.saveImageToPhotosAlbum 将图片保存到本地。 wx.canvasToTempFilePath({ canvas: canvas, // canvas 实例 success(res) { // canvas 生成图片成功 wx.saveImageToPhotosAlbum({ filePath: res.tempFilePath, success(res) { // 保存成功 } }) } }) -- • Video 绘制 Canvas / webgl • -- [图片] 示例:视频文件绘制 Canvas 适用场景:制作 Video 滤镜、挑选 Video 封面等 相关 API:RenderingContext/Canvas Step 1: 获取实例 通过 wx.createSelectorQuery 获取 VideoContext 实例 let video = null wx.createSelectorQuery().select('#video').context(res => { // 通过 wx.createSelectorQuery 获取 VideoContext 实例 video = res.context; }) Step 2: 绘制内容 获取 VideoContext 实例后,将 VideoContext 传递给 Canvas 进行绘制。开发者根据业务需求选择绘制类型: Canvas 2d 写法:canvas.drawImage(video, ...)webgl 写法:gl.texImage2D(..., video) wx.createSelectorQuery().selectAll('#myCanvas,#webglCanvas').node(res => { const ctx = res[0].node.getContext('2d') const gl = res[1].node.getContext('webgl') setInterval(() => { // canvas 2d // 将 video 纹理对象传入 drawImage 进行绘制 ctx1.drawImage(video, 0, 0, w * dpr, h * dpr); // 添加一个蒙层 ctx1.fillStyle = 'rgba(0, 0, 0, 0.3)' ctx1.fillRect(0, 0, w * dpr, h * dpr); // webgl const render = createRenderer(res[1].node, w, h) render(new Uint8Array(ctx1.getImageData(0, 0, w * dpr, h * dpr).data), w * dpr, h * dpr) }, 1000 / 24) }).exec() function createRenderer(canvas, width, height) { const gl = canvas.getContext("webgl") ... return (arrayBuffer, width, height) => { ... // 指定二维纹理图像 gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, arrayBuffer) gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0) } } -- • 视频解码并绘制到 webgl • -- [图片] 示例:视频一键解码并绘制到 webgl 适用场景:添加特效、贴图等视频编辑场景 相关 API:wx.createVideoDecoder/VideoDecoder/RenderingContext/Canvas.requestAnimationFrame/wx.createMediaAudioPlayer/MediaAudioPlayer Step 1: 创建视频解码器进行解码 1. 调用 createVideoDecoder 对视频进行解码 2. 使用 videodecoder.start 启动解码,视频源文件不限制本地或远程路径 3. 通过 videodecoder.on('start', res => {}) 监听解码,通过 videodecoder.getFrameData() 获取到解码数据 // 获取视频解码器 getVideoDecoder(source, abortAudio) { return new Promise((resolve, reject) => { // 创建视频解码器 videodecoder = wx.createVideoDecoder() // 开始解码 videodecoder.start({ abortAudio: abortAudio, source: source, // 视频源文件,支持本地路径&远程路径 mode: 0 // 按pts解码,保证音画同步 }) // 监听解码 开始 videodecoder.on('start', res => { console.log('videodecoder start', res) // 状态初始化 isStop = false resolve(videodecoder) }) // 监听解码 结束 videodecoder.on('ended', res => { // 状态设置为结束,停止画面录制器 isStop = true }) }) }, Step 2: 解码数据绘制到 webgl 1. 通过 gl.texImage2D(..., image) 将解码数据绘制到 webgl 2. 使用 webgl.requestAnimationFrame 继续绘制,效果更加流畅 // 将解码数据绘制到 webgl 中 const query = wx.createSelectorQuery() query.select('#webglCanvas').node().exec((res) => { const webgl = res[0].node const requestAnimationFrame = webgl.requestAnimationFrame; // 初始化webgl let render = null if (!render) { render = createRenderer(webgl, 600, 400) } /** * 绘制视频帧到 canvas */ let i = 1 let loop = () => { // 解码结束,停止循环 if (isStop) { return } // 获取解码数据,绘制到 webgl 中 const imageData = videodecoder.getFrameData() if (imageData) { // render 的高宽需要设置为图片的宽高才可以绘制出来 render(new Uint8Array(imageData.data), imageData.width, imageData.height) } // 继续绘制 console.log('绘制帧数:', i++) requestAnimationFrame(loop) } // 启动录制循环 requestAnimationFrame(loop) }) Step 3: 添加音频播放器同步播放音频 完成 Step2 后,webgl 只有视频播放,缺少音频。因此使用 wx.createMediaAudioPlayer(),支持 addAudioSource 传入 videodecoder,保证视频帧渲染音画同步 /** * 创建媒体音频播放器 */ let mediaAudioPlayer = null let addAudio = () => { if (mediaAudioPlayer) return mediaAudioPlayer = wx.createMediaAudioPlayer() mediaAudioPlayer.start().then(() => { // 添加播放器音频来源 mediaAudioPlayer.addAudioSource(videodecoder).then(res => { console.log('add mediaAudioPlayer: ',) }) }) } // render 绘制视频同时添加音频 render(new Uint8Array(imageData.data), imageData.width, imageData.height) addAudio() -- • 录制并导出 webgl 视频 • -- [图片] 示例:录制并一键导出 webgl 视频 适用场景:将动画、编辑过的视频导出视频文件保存 相关 API:wx.createMediaRecorder/MediaRecorder/wx.createMediaContainer/MediaContainer/MediaTrack Step 1: 创建 webgl 画面录制器进行录制 通过 createMediaRecorder 创建页面录制器,并且绑定 webgl(建议离屏状态,效果更好)进行录制 /** * 获取画面录制器 */ getRecorder() { let canvas = this.getMainCanvasNode() let recorder = wx.createMediaRecorder(canvas, { fps: choosedVideoInfo.fps, // 实际视频的 fps videoBitsPerSecond: choosedVideoInfo.bitrate, // 实际视频的 bitrate gop: 12 }) // 监听录制事件 recorder.on("timeupdate", (res) => { console.log('recorder 录制中,当前时间:', res.currentTime) }) recorder.on("stop", (res) => { console.log('recorder停止') this.saveMedia(res.tempFilePath) }) // 开始录制 recorder.start() this.recorder = recorder return recorder }, // 初始化 画面录制器 并进行录制 await this.initRenderer() this.getDecoder().then((decoder) => { let recorder = this.getRecorder() var self = this function loop() { if (self.stopped) { return } let frameData = decoder.getFrameData() if (!frameData) { console.log('没取到帧') setTimeout(() => { loop() }, 1000/60) } else { self.renderFrame(frameData) recorder.requestFrame(() => { console.log('录制帧数:', i++) loop() }) } } loop() }) Step 2: 添加音频合成音视频 1. 通过 createMediaContainer 创建音视频处理容器来合成音视频 2. 通过 MediaContainer.extractDataSource 将视频源分离出视频轨道和音频轨道,将需要的轨道通过 MediaContainer.addTrack 添加到容器中 3. 通过 MediaContainer.export 导出即可获得合成后的视频文件 /** * 将视频和音频合到一起并保存到本地 * @param {*} videoTempFilePath */ saveMedia(videoTempFilePath) { const self = this let choosedFile = this.choosedFile const MediaContainer = wx.createMediaContainer() // webgl的取视频 MediaContainer.extractDataSource({ source: videoTempFilePath, success(res) { MediaContainer.addTrack(res.tracks[0]) // 源视频取音频 MediaContainer.extractDataSource({ source: choosedFile, success(res) { // 拿到音频轨道并加入到容器 res.tracks[0].kind == 'audio' && MediaContainer.addTrack(res.tracks[0]) res.tracks[1].kind == 'audio' && MediaContainer.addTrack(res.tracks[1]) // 合成视频并导出视频文件 MediaContainer.export({ success(res) { // 保存视频到本地 wx.saveVideoToPhotosAlbum({ filePath: res.tempFilePath, success() { wx.showToast({ title: '导出成功', icon: 'success', duration: 2000 }) self.destroy() } }) } }) } }) } }) }, -- •高效图像处理彩蛋 • -- 学会以上这些 Canvas 小技巧,还担心新年的美图美照美视频处理不过来?赶紧码下这个 Canvas 代码包,保证你就是家里最闪耀的靓女靓仔。 预祝大家新的一年 Canvas 在手,红包一直有!
2022-03-24 - 小程序开发常用的几个NPM包
小程序开发常用的几个NPM包 ~ 1)js-base64 场景 平时在做支付的时候会做一下加解密的工作,用于数据的验证用 [图片] ~ 示例 [图片] ~ 2)jsencrypt 场景 目前的应用场景是在用户注册或登录的时候,用公钥对密码进行加密,再去传给后台,后台用私钥对加密的内容进行解密,然后进行密码校验或者保存到数据库。 [图片] ~ 示例 [图片] ~ 3)urijs 场景 日常操作组装链接使用 [图片] ~ ~ 示例 buildQuery [图片] ~
2022-09-28 - css 多行文字两端对齐
[图片] 需要的地方补充这个样式,文字就可以实现两端对齐了. .text-justify{ text-align: justify; text-justify:distribute-all-lines; /*这行必加,兼容ie浏览器*/ text-align-last: left!important; } [图片]
2022-12-20 - 微信云开发成长笔记
33.如何批量下载云储存文件 参考地址 32.联表查询生成消费记录统计,用户数量和消费次数 联表生成的数据 [图片] 想要的数据统计(统计当天总消费次数,和当天访问总人数) [图片] [代码] const storeId = '0ab5303b62cc290d0e0799a838ca9158' const createTimeStart = '2022-06-15' const createTimeEnd = '2022-09-01' db.collection('deal').aggregate().match({ _storeId: storeId }).sort({ createTime: -1 }).lookup({ from: 'users', localField: '_userId', foreignField: '_id', as: 'userList', }) .replaceRoot({ newRoot: $.mergeObjects([ $.arrayElemAt(['$userList', 0]), { count: '$count', createTime: $.dateToString({ //将日期对象格式化为 年月日 date: $.dateFromString({ //将日期字符串转化为日期对象 dateString: '$createTime' }), format: '%Y-%m-%d' }), type: '$type', mobile: "$mobile" } ]) }) .match({ type: _.or([_.eq(2), _.eq(4)]), //筛选只要扣除的类型 createTime: _.and(_.gte(createTimeStart), _.lte(createTimeEnd)) //查询的时间区间 }) .group({ _id: '$createTime', //根据日期进行分组 consumeCount: $.sum('$count'), userCount: $.addToSet('$mobile'), //向数组中添加值,如果数组中已存在该值 }).project({ _id: 0, createTime: '$_id', consumeCount: 1, userCount: $.size('$userCount'), }) .sort({ createTime: -1 }) .end() [代码] 31.web本地上传文件 cloud.uploadFile cloud.uploadFile ,建议配合 Cloud.getTempFileURL 转换为 [代码]https://[代码]进行访问 [图片] 你可能会遇到跨域问题,则需要再腾讯云中配置本地测试的安全域名,一般默认的 [代码]localhost:8080端口[代码]时可用的,其它要自行配置 [图片] [图片] [图片] [图片] [图片] [图片] 30.查询条件根据时间区间筛选 createTimeStart <= createTime <= createTimeEnd [代码]createTimeStart 可以为字符串格式 "2022-08-17 9:50:00"[代码] [代码]createTime:_.and(_.get(createTimeStart),_.lte(createTimeEnd)) [代码] 29.静态网站访问云储存图片路径 小程序端一般上传图片后,会生成一条云路径地址 [代码]cloud://xxxxxx[代码],但事实上外部(web)访问这个路径是行不通的。 但是可以通过 Cloud.getTempFileURL 转换为 [代码]https://[代码]进行访问 28.web端如何实现连接云数据库 参考文章 27.联表查询 Aggregate().lookup() Aggregate().lookup() .project() $.arrayElemAt [代码]//联和 store_users和users,将store_users中的_userId与users中的_id匹配,把匹配符合条件的存放到userInfo数组中 db.collection('store_users').aggregate() .lookup({ from: 'users', localField: '_userId', foreignField: '_id', as: 'userInfo', //聚合匹配出的数组 }).project({ // 取匹配数组的第0个对象 userInfo:$.arrayElemAt(['$userInfo', 0]) }) .end() [代码] 26.如何实现扫码处理成功后,实时监听获取结果 .watch() Collection.watch 如果非本人操作处理扫码结果,需要对应开启表权限 [图片] 25.如何删除所有记录 [代码]_.neq(0) 不等于0的值 [代码] [图片] 24.云开发发送手机验证码 cloudbase.sendSms 微信小程序cloudbase.sendSms中template_id怎么获取? [代码]你可能会遇到发送成功但是一直收不到验证码的问题[代码] [图片] 或者在云开发控制台,查看 [代码]短信监控[代码] 日志 [图片] 23.云开发获取当前微信用户绑定的手机号 1.用户点击授权按钮获取code 2.接口通过code获取手机号 [代码]<button open-type="getPhoneNumber" bindgetphonenumber="getPhoneNumber"></button> ... Page({ getPhoneNumber (e) { console.log(e.detail.code) } }) [代码] [代码]//云函数 config.json 中配置 { "permissions": { "openapi": [ "phonenumber.getPhoneNumber" ] } } ... //云函数调用 const result = await cloud.openapi.phonenumber.getPhoneNumber({ code: '获取的code' }); return { success: true, data: result, message: '获取成功' }; [代码] [代码]你可能会遇到 获取不到code的问题,建议更新手机微信版本或者开发工具版本。[代码] 确认当前基础库版本是否为 2.21.2 及以上。 正常情况下,基础库 2.21.2 对应的是客户端版本号为 8.0.16,如果你的版本低于 8.0.16 并且是通过开发者工具向客户端推送的 2.21.2 基础库,大几率不会返回 code 参数,请手动更新微信版本。 22.如何实现刷新当前页面 网上有很多方法,我这边采用的是 [代码]this.onLoad()[代码],不过这种方式需要你重新初始化一遍[代码]data[代码]里面的数据 简单实现一个倒计时,结束时刷新页面 [代码]let timer = null const time = 120 //有效时间120,超时自动刷新 Page({ data: { validTime: time }, onLoad: function (options) { this.validTimeInit() }, onUnload: function () { clearInterval(timer) }, validTimeInit() { let _this = this timer = setInterval(() => { let validTime = this.data.validTime validTime-- if (validTime < 0) { clearInterval(timer) _this.setData({ validTime: time }) _this.onLoad() } else { this.setData({ validTime: validTime }) } }, 1000); } }) [代码] 21._.or()查询多个符合条件的数据 云开发查询or的用法 _.eq() 查询筛选条件,表示字段等于某个值 [代码] const stores = data.map(s => { return _.eq(s._storeId) }) || [] const { data: storesInfo } = await db.collection('hyc_stores').where({_id: _.or(stores) }).get() [代码] 20…add()数据时返回值是res .add() 类似[代码].where,.update[代码]返回的都是 [代码]res.data[代码],而[代码].add[代码]返回的是[代码]res[代码],且只有添加成功的[代码]_id[代码] [图片] 19.moment 格式化时间不匹配 不清楚具体原因,但是网上说是 时区问题 加上 [代码].utcOffset(480)[代码] 就可以了 [代码] moment().utcOffset(480).format('YYYY-MM-DD HH:mm:ss') [代码] 18.onShow(options) 参数拿不到 onShow和onLoad的区别 Page页面级 [代码]onShow[代码] 没有 [代码]options[代码],全局的App里面的 [代码]onShow[代码]才有,想获取到参数尽量用 [代码]onLoad[代码] https://developers.weixin.qq.com/community/develop/doc/0008005f68c300c912070098b56800 17.常用云函数指令 [代码]const _ = db.command[代码] _.or :满足其中一个条件即可 跨字段的或操作; 查询更新数组/对象 [代码]//数组 arr 第 index 下标对象的 id [`arr.${index}.id`]: id [代码] 联表查询 16.父子组件通信 使用微信小程序全局变量 小程序的全局变量存储在文件 app.js 的globalData对象中 [代码]// app.js 中定义 App({ globalData: { hasLogin: false, openid: null }, }) // 其他文件中读取使用 const app = getApp(); console.log(app.globalData.hasLogin) [代码] 使用微信小程序的 数据缓存 [代码] wx.setStorageSync('userInfo', data) wx.getStorageSync('userInfo') [代码] 使用父子组件间的属性 父组件传值给子组件,子组件可以在 properties 中接收并使用 子组件传值给父组件,可以通过方法触发 triggerEvent [代码]//子组件 this.triggerEvent('myEvent',传参) //父组件 <子组件 bind:myEvent="传递方法" /> [代码] 15.input修改数组对象的某一个值 解决方案 14.微信map地图组件闪退问题 安卓手机在切换tab的时候,频繁切换时,小程序会闪退 解决方案 13.日期格式化: [代码]<wxs src="../../utils/filter.wxs" module="filter"></wxs> <text class="time"> {{filter.format(newsMsg.createAt,'YYYY-MM-DD')}}</text> [代码] 利用[代码].wxs[代码]方法,在页面上引入 utils/filter.wxs [代码]function format(date, fmt) { let ret; const opt = { "Y+": date.getFullYear().toString(), // 年 "m+": (date.getMonth() + 1).toString(), // 月 "d+": date.getDate().toString(), // 日 "H+": date.getHours().toString(), // 时 "M+": date.getMinutes().toString(), // 分 "S+": date.getSeconds().toString() // 秒 // 有其他格式化字符需求可以继续添加,必须转化成字符串 }; for (let k in opt) { ret = new RegExp("(" + k + ")").exec(fmt); if (ret) { fmt = fmt.replace(ret[1], (ret[1].length == 1) ? (opt[k]) : (opt[k].padStart(ret[1].length, "0"))) }; }; return fmt; } function formatNumber(n) { n = n.toString() return n[1] ? n : '0' + n } function getWeek(n) { switch (n) { case 1: return '星期一' case 2: return '星期二' case 3: return '星期三' case 4: return '星期四' case 5: return '星期五' case 6: return '星期六' case 7: return '星期日' } } module.exports.format = format [代码] 12.下拉加载更多: .json [代码]"enablePullDownRefresh":true, [代码] .wxml [代码] /** * 页面相关事件处理函数--监听用户下拉动作 */ onPullDownRefresh: function () { this.setData({ pages: 0, list: [], 'queryModel.current': 1, 'queryModel.size': 10, 'queryModel.totalCount': 0 }) // 接口加载数据 this.getNewsPage() // 停止加载,下拉返回 wx.stopPullDownRefresh(); }, [代码] 11.空格,’ ’问题 👉 复 制 吧 👈 微信小程序中的多个空格怎么打? | 微信开放社区 10.text文本属性 如果属于纯文本,切属于 <textarea>输出的内容,尽量用 <text>组件标签 [代码]<text>{{details.intro}}</text> [代码] 9.rich-text 富文本图片和换行问题 rich-text [代码] <rich-text nodes="{{details.content}}"></rich-text> [代码] 图片百分比 [代码]//rich-text富文本图片自适应处理 // content 为富文本内容 content.replace(/\<img/gi, '<img style="max-width:100%;height:auto" ') [代码] 不换行问题/app.wxss [代码]/* 富文本样式,解决富文本不换行问题 */ rich-text { word-break: break-all; white-space: pre-wrap; } [代码] 8.对象深拷贝 [代码]//对象深拷贝 const deepClone = function (obj, newObj) { var newObj = newObj || {}; for (let key in obj) { if (typeof obj[key] == 'object') { let isArray = Array.isArray(obj[key]); //判断是否数组 newObj[key] = (isArray == true ? [] : {}) deepClone(obj[key], newObj[key]); } else { newObj[key] = obj[key] } } return newObj; } module.exports = { deepClone } [代码] 7.request请求封装 utils/request.js [代码]const baseURL = '服务器地址'; function request(method, url, data) { return new Promise(function (resolve, reject) { let header = { 'content-type': 'application/json', }; wx.request({ url: baseURL + url, method: method, data: method === 'POST' ? JSON.stringify(data) : data, header: header, success(res) { //请求成功 //判断状态码---errCode状态根据后端定义来判断 if (res.data.code == 200) { resolve(res.data); } else { //其他异常 reject('运行时错误,请稍后再试'); } }, fail(err) { //请求失败 reject(err) } }) }) } export default request [代码] api/index.js使用 [代码]// 小程序接口 import request from '../utils/request' const API = { getFlightPage: (data) => request('POST', '/flight/page', data), // 查询各模块的在线生活服务 lifeServeList: (moduleId) => request('GET', `/module/life/service/list?module=${moduleId}`), } module.exports = { API: API } [代码] app.js入口引入 [代码]//引入api接口 const $api = require('./api/index').API ... App({ $api, ... }) [代码] .wxml [代码]const app = getApp() ... //接口调用 app.$api.newsDetails().then(res => {}) [代码] 6.修改页面背景色 .wxss [代码]page{ background-color:'每个页面都可以自定义背景色' } [代码] 5.<image>mode属性 <image>组件 4.IOS<video>黑屏问题 [代码]custom-cache="{{false}}"[代码] [代码]一般缓存为一级缓存、二级缓存、和自定义缓存,换而言之custom-cache="{{false}}"就是不使用自定义缓存的意思。为什么用了视频组件会卡loading加载不出来呢,可能是因为微信设置的自定义缓存的位置有容量限制,因此将它关闭了以后会自动使用系统缓存,可能就没有这个问题了。[代码] [代码]<video autoplay="true" src="{{url}}" controls custom-cache="{{false}}"></video> [代码] 3.微信小程序暗黑模式 暗黑模式详解文章 2.微信小程序escape转码 例如你要传递一个URL链接参数到下一个页面,你可能发现微信会自动给你截取一部分参数,因此可以通过此种方式避免。 [代码]//转码 escape() //解码 unescape() [代码] 1.微信小程序生成二维码 weapp-qrcode.js [代码]//引入 const QRCode = require('../../utils/weapp-qrcode.js') [代码] [代码]//使用 new QRCode('myQrcode', { text: `xxx链接`, width: 200, height: 200, padding: 12, // 生成二维码四周自动留边宽度,不传入默认为0 correctLevel: QRCode.CorrectLevel.L, // 二维码可辨识度 callback: (res) => { console.log(res.path) // 接下来就可以直接调用微信小程序的api } }) [代码] [代码]<canvas class="canvas-code"canvas-id="myQrcode" style="background:#fff;width: 200px;height: 200px;"/> [代码] [代码]//调用扫一扫获取结果 // 只允许从相机扫码 wx.scanCode({ onlyFromCamera: true, success(res) { wx.navigateTo({ url: res.result //xxx链接 }) } }) [代码]
2022-09-21 - 小程序 大文件 多图 直接上传七牛云
思想:通过从后端获取的七牛云SDK,完成大文件在小程序直接上传七牛云 本文章实现多图直接上传功能,且能实现预览功能,效果图如下(有需要的朋友们可以看一下): [图片] 使用七牛云官方的SDK 下载:qiniu-wxapp-sdk composer安装七牛云sdk composer require itbdw/laravel-storage-qiniu 从后端获取upToken的方法 public function uploadToken() { $auth = new Auth('AK', 'SK'); $bucket = 'bucket'; $token = $auth->uploadToken($bucket); return json_encode(['uptoken'=>$token]); } 小程序拿到token,用来上传图片 const qiniuUploader = require('../../qiniuUploader.js') data: { imageURL:'', uptoken:'' }, //大文件上传 big(){ wx.request({ url: 'http://www.xxxxx.com/index.php/api/uploadToken',//拿到token header: { 'Content-Type': 'application/json;charset=UTF-8' }, method: 'get', success: (response) => { this.setData({ uptoken: response.data.uptoken }) console.log(response) console.log(this.data.uptoken) } }) wx.chooseImage({ //可上传多图 count: 3, sizeType: ['original', 'compressed'], sourceType: ['album', 'camera'], success:(res) => { //微信小程序的API,获取临时图片地址 console.log(res) // tempFilePath可以作为img标签的src属性显示图片 const img = res.tempFilePaths let arr=[] img.map(url=>{ // 交给七牛上传 qiniuUploader.upload(url, (res) => { //数组追加 arr.push('http://'+res.imageURL) this.setData({ 'imageURL': arr, }); console.log(this.data.imageURL) }, (error) => { console.log('error: ' + error); }, { region: 'NCN', //华北 domain: 'xxx.xxxxx.xxx', // 自己的域名 key: Math.floor(Math.random()*50 + 50)+'.jpg', // 上传到七牛云图片就是这名字了 // 以下方法三选一即可,优先级为:uptoken > uptokenURL > uptokenFunc uptoken: this.data.uptoken, // 由其他程序生成七牛 uptoken uptokenURL: 'http://www.xxxxx.com/index.php/api/uploadToken', // uptokenFunc: function() { // return '[yourTokenString]'; // } }, (res) => { console.log('上传进度', res.progress) console.log('已经上传的数据长度', res.totalBytesSent) console.log('预期需要上传的数据总长度', res.totalBytesExpectedToSend) }, () => { // 取消上传 }, () => { // `before` 上传前执行的操作 }, (err) => { // `complete` 上传接受后执行的操作(无论成功还是失败都执行) }); }) } }) },
2022-03-16 - 小程序实现Icon组件的四种方式
介绍小程序中自定义icon组件的四种方式 小程序官方提供了一个icon组件,但是改组件只有10种类型。在日常开发中,难免会需要自定义一些icon组件。本文带你实现四种自定义icon组件的方式,并且概述每种方式的优劣点 一、image图片 利用小程序原生image标签就能实现一个自定义的icon组件。这种方式通过切图,然后对其设置宽高便能实现想要的icon组件。但这种图片方式无法方便的修改icon颜色,并且图片缩放过大后容易失真,并且图片会占用一次http请求 实现代码如下 [代码]<image src="./close.png" style="display: inline-block;" class="image"/> [代码] [代码].image { width: 25rpx; height: 25rpx; } [代码] 二、css3方式 小程序wxss原生支持css3。所以可以通过css3方式实现自己想要的自定义icon。但这种方式实现一些比较复杂的icon比较困难,且用css的方式实现需要耗费一定的工作量 实现代码如下 [代码]<view class="icon-close"></view> [代码] [代码].icon-close { display: inline-block; width: 17rpx; height: 2rpx; background-color: red; transform: rotate(45deg); } .icon-close::after { content: ""; display: block; width: 17rpx; height: 2rpx; background-color: red; transform: rotate(-90deg); } [代码] 三、字体文件方式 小程序wxss支持加载远程字体文件。阿里巴巴矢量图标库(iconfont.cn)可以在线选择想要的icon图片,并且可以修改样式,然后生成cdn的字体文件。然后再通过css加载改字体文件便能实现自定义icon。这种方式比较便捷,图标种类繁多,而且修改样式也比较灵活 实现代码如下 [代码]<icon class="iconfont icon_01" /> [代码] [代码]@font-face { font-family: 'iconfont'; /* Project id 2361238 */ src: url('//at.alicdn.com/t/font_2361238_cxshqh1m3m7.woff2?t=1642320886173') format('woff2'), url('//at.alicdn.com/t/font_2361238_cxshqh1m3m7.woff?t=1642320886173') format('woff'), url('//at.alicdn.com/t/font_2361238_cxshqh1m3m7.ttf?t=1642320886173') format('truetype'); } .iconfont { font-family: "iconfont" !important; font-size: 16px; font-style: normal; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } .icon_01::before { content: "\e6f1"; } [代码] 四、svg方式 通过svg图片的方式也能实现自定义icon。但是相比第一种方式,svg图片可以修改颜色,并且缩放的失真率也比较低。不过小程序wxss并不支持加载本地的svg图片。我们可以通过在线(https://www.sojson.com/image2base64.html)svg转base64的方式在wxss中加载svg图片。 实现代码如下 [代码]<icon class="close-icon"></icon> [代码] [代码].close-icon { width: 20rpx; height: 20rpx; background: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBzdGFuZGFsb25lPSJubyI/PjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+PHN2ZyB0PSIxNjU2MjM5NjM4MDE2IiBjbGFzcz0iaWNvbiIgdmlld0JveD0iMCAwIDEwNDUgMTAyNCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHAtaWQ9IjE4NDIiIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iMjA0LjEwMTU2MjUiIGhlaWdodD0iMjAwIj48ZGVmcz48c3R5bGUgdHlwZT0idGV4dC9jc3MiPkBmb250LWZhY2UgeyBmb250LWZhbWlseTogZmVlZGJhY2staWNvbmZvbnQ7IHNyYzogdXJsKCIvL2F0LmFsaWNkbi5jb20vdC9mb250XzEwMzExNThfdTY5dzh5aHhkdS53b2ZmMj90PTE2MzAwMzM3NTk5NDQiKSBmb3JtYXQoIndvZmYyIiksIHVybCgiLy9hdC5hbGljZG4uY29tL3QvZm9udF8xMDMxMTU4X3U2OXc4eWh4ZHUud29mZj90PTE2MzAwMzM3NTk5NDQiKSBmb3JtYXQoIndvZmYiKSwgdXJsKCIvL2F0LmFsaWNkbi5jb20vdC9mb250XzEwMzExNThfdTY5dzh5aHhkdS50dGY/dD0xNjMwMDMzNzU5OTQ0IikgZm9ybWF0KCJ0cnVldHlwZSIpOyB9Cjwvc3R5bGU+PC9kZWZzPjxwYXRoIGQ9Ik0yODIuNTE3MzMzIDIxMy4zNzZsLTQ1LjM1NDY2NiA0NS4xNjI2NjdMNDg5LjQ3MiA1MTIgMjM3LjE2MjY2NyA3NjUuNDYxMzMzbDQ1LjM1NDY2NiA0NS4xNjI2NjdMNTM0LjYxMzMzMyA1NTcuMzU0NjY3bDI1Mi4wOTYgMjUzLjI2OTMzMyA0NS4zNTQ2NjctNDUuMTYyNjY3LTI1Mi4yODgtMjUzLjQ0IDI1Mi4yODgtMjUzLjQ4MjY2Ni00NS4zNTQ2NjctNDUuMTYyNjY3TDUzNC42MTMzMzMgNDY2LjYyNGwtMjUyLjA5Ni0yNTMuMjI2NjY3eiIgcC1pZD0iMTg0MyI+PC9wYXRoPjwvc3ZnPg=='); background-position: unset; background-repeat: no-repeat; background-position: center; background-size: contain; } [代码] 五、总结 上述四种方式都能实现自定义icon。每种都有其利弊。如果四种方式都可以满足需求,建议优先使用第三种方式。
2022-06-27