个人案例
- 该帐号已注销
塞尔达攻略pro
《塞尔达攻略pro》扫码体验
- 《马保国语录》我大意了啊,传统功夫宜点到为止.mp3
欢迎体验 [图片] 小程序效果如图 [图片] 实现步骤 将mp3保存到云存储 [图片] 需要注意的是,此处贴了 ID 不可以直接用于backgroundAudioManager播放 需要将fileID转临时路径才可以播放 ``` wx.downloadFile({ url: fileID, //mp3的fileID success: res => { console.log('ok') resolve(res.tempFilePath) } }) ``` 最后使用 backgroundAudioManager 播放即可 ``` playVoice(title,src){ console.log('src:',title,src) backgroundAudioManager = wx.getBackgroundAudioManager() backgroundAudioManager.title = title backgroundAudioManager.epname = title backgroundAudioManager.singer = title backgroundAudioManager.coverImgUrl = 'cloud://yuludev-4e0n9.7975-yuludev-4e0n9-1301573818/mabaoguo/avatar.png' // 设置了 src 之后会自动播放 backgroundAudioManager.src = src backgroundAudioManager.onPause( ()=>{ this.playVoice(src) }) backgroundAudioManager.onStop( ()=>{ this.playVoice(src) }) backgroundAudioManager.onError( (e)=>{ console.error(e) }) backgroundAudioManager.onCanplay( (e)=>{ console.log('加载完毕') wx.hideLoading({ success: (res) => {}, }) }) }, ``` 感谢您的阅读~
2020-11-22 - 《讲笑话情话》用微信对话开放平台微信智言NLP开放平台搭建情话服务小程序
最终效果 欢迎扫码体验 [图片] [图片] 资源准备 在微信服务平台开通“讲笑话情话服务” 地址: https://developers.weixin.qq.com/community/servicemarket/detail/0008e2351088004e295aa03f95bc15 [图片] 调用方法 小程序调用示例wx.serviceMarket.invokeService({ service: 'wxcae50ba710ca29d3', // 'wx_mp_appid', api: 'jokebot', data: { "mode": 2, // 返回普通笑话 }, }).then(res => { console.log('invokeService success', res) wx.showModal({ title: 'cost', content: (Date.now()) + '', }) }).catch(err => { console.error('invokeService fail', err) wx.showModal({ title: 'fail', content: err + '', }) }) 总结 微信对话开放平台有: 讲笑话情话服务,情感分析服务,夸夸话术服务,智能对话服务,商品关键词抽取,商品二分类等多种服务. 开箱即用,极大简化了开发流程,欢迎大家尝试~ [图片]
2020-11-07 - 小程序AR识别,三行代码实现Camera数据毫秒级转base64图片
关键词:小程序AR 图片 base64 相机 Camera onCameraFrame Canvas ArrayBuffer Uint8Array Uint8ClampedArray upng-js 核心步骤: 1 相机原始图像数据frame.data,即ArrayBuffer数组,转成Uint8Array数组 2 Uint8Array数组转成Uint8ClampedArray数组 3 wx.canvasPutImageData(Uint8ClampedArray) 详细流程如下: 最近因为项目需求,需要上传base64去做AR识别功能,和大家一起分享讨论下具体的实现方式。 首先说下实现原理,通过Camera的onCameraFrame获取实时帧数据,将实时帧数据添加到Canvas上,然后将Canvas保存为临时图片,再将临时图片转换为base64。 贴上核心实现代码: wxml: js: var nCounter = 0; openCamera: function (res) { var that = this var camera_ctx = wx.createCameraContext() listener = camera_ctx.onCameraFrame((frame) => { // nCounter等于30 是因为一开始相机会有一个对焦的过程,如果一开始获取数据,就是模糊的图片 if (nCounter == 30) { console.log(frame.data instanceof ArrayBuffer, frame.width, frame.height) var data = new Uint8Array(frame.data); var clamped = new Uint8ClampedArray(data); // 实时帧数据添加到Canvas上 wx.canvasPutImageData({ canvasId: 'myCanvas', x: 0, y: 0, width: frame.width, height: frame.height, data: clamped, success(res) { // 转换临时文件 wx.canvasToTempFilePath({ x: 0, y: 0, width: frame.width, height: frame.height, canvasId: 'myCanvas', fileType: 'jpg', destWidth: frame.width, destHeight: frame.height, // 精度修改 quality: 0.8, success(res) { // 临时文件转base64 wx.getFileSystemManager().readFile({ filePath: res.tempFilePath, //选择图片返回的相对路径 encoding: 'base64', //编码格式 success: res => { // 保存base64 that.data.mybase64 = res.data; } }) }, fail(res) { console.log(res); } }, that) } }) } nCounter++ // console.log(nCounter); if (nCounter >= 100) { nCounter = 0 } }) listener.start() } 目前网上有两种转换方式并对比下: 1:upng-js等第三方转码js库,将相机流转换成base64,一般需要1-2s左右 [图片] 2.使用canvas将相机流转变base64,都是使用js或者小程序官方的api进行转换,一般转换时间在1秒以下: [图片] 重点说明下: 如何使用wx.canvasPutImageData()将相机流添加canvas,我们查看该官方api,添加的data类型为:Uint8ClampedArray [图片] 而我们通过onCameraFrame获取的data类型为:ArrayBuffer [图片] 所有两者类型不一致,就需要转换,将ArrayBuffer=>Uint8Array=>Uint8ClampedArray var data = new Uint8Array(frame.data); var clamped = new Uint8ClampedArray(data); 成功的把onCameraFrame获取实时帧数据转换并canvasPutImageData在canvas上,并通过canvasToTempFilePath获取临时文件,如何获取临时文件getFileSystemManager转换为base64,传入云端进行AR识别,就大功告成! 技术分享来自于:北京晞翼科技有限公司 技术作者:le3d618、xiaoz0816 微信商务联系:le3d618
2020-04-30 - 云开发实战-如何维护用户表?(优化版)
前言 之前写过一篇《云开发-如何维护用户表?》,这种方式是最简单的,经过阅读了一些开源项目的代码,我优化了部分写法。 对比 优化前实现思路: 通过 login 云函数获取 openid 存放到本地 在授权信息的时候去添加 userInfo 根据 openid 去查询是否已经存储 没有查到就是新用户进行添加并存放 id 到本地 查看后老用户就根据 id 进行更新信息 优化后实现思路: 在 app.js 通过 queryCurrentUser 云函数查询 openid 是否在用户表 得到状态后存放在 app.js 的全局变量 authorized 属性里面 当需要用户授权的时候判断状态,没有就跳转到授权页面 进行授权调用 authorize 云函数添加用户 代码 在 app.js 通过 queryCurrentUser 云函数查询 openid 是否在用户表 得到状态后存放在 app.js 的全局变量 authorized 属性里面。 [代码]wx.cloud.callFunction({ // 云函数名称 name: 'user', // 传给云函数的参数 data: { action: 'queryCurrentUser' } }).then(res => { if (res.result.errMsg === 'user.query.ok') { this.onAuthorized(res.result.data.userInfo); this.authorized = true; } wx.hideLoading(); }) onAuthorized(userInfo) { this.authorized = true; this.globalData.userInfo = userInfo; }, [代码] queryCurrentUser 云函数 [代码]async queryCurrentUser(context, params) { const { OPENID } = context; let res = await db.collection('users').where({ openid: OPENID }).get(); if (res.data.length === 0) { return { errMsg: 'user.query.none' }; } return { errMsg: 'user.query.ok', data: { userInfo: res.data[0].userInfo } }; }, [代码] 当需要用户授权的时候判断状态,没有就跳转到授权页面 index.js [代码]toInfo(res) { if (app.authorized !== true) { wx.navigateTo({ url: '/pages/authorize/authorize' }); return; } // 省略业务代码.... } [代码] 进行授权调用 authorize 云函数添加用户 authorize.js [代码]wx.cloud.callFunction({ // 云函数名称 name: 'user', // 传给云函数的参数 data: { action: 'authorize', userInfo: userInfo } }).then(res => { if (res.result.errMsg === 'user.authorize.ok' || res.result.errMsg === 'user.authorize:authorized') { app.onAuthorized(res.result.data.userInfo); wx.showLoading({ title: '授权成功' }); setTimeout(() => { wx.hideLoading(); app.navigateBack(); }, 1000); return; } wx.nextTick(() => { wx.showToast({ title: '授权失败', icon: 'none', duration: 1000 }); }); }); [代码] authorize 云函数 [代码]const authorizedRes = { env: cloud.DYNAMIC_CURRENT_ENV, errMsg: 'user.authorize:authorized' }; async authorize(context, params) { const { OPENID } = context; let getRes = await db.collection('users').where({ openid: OPENID }).get(); if (getRes.errMsg !== 'collection.get:ok') { return errorAuthorizeRes; } if (getRes.data.length > 0) { return authorizedRes; } let addRes = await db.collection('users').add({ data: { openid: OPENID, userInfo: params.userInfo, authorizedTime: new Date(), } }); return { errMsg: 'user.authorize.ok', data: { userInfo: params.userInfo } }; } [代码] 总结 这种方式优点如下: 用云函数来验证,云函数可以直接获取 openid 通用统一的授权页面进行授权,这样就不需要在不同的地方写同样的授权代码 添加逻辑在云函数中实现,改小程序前端代码需要重新发版,云函数部署就行 代码需要不断优化才能更好。
2020-09-16 - 【圣诞节】给你的头像加个圣诞帽吧
圣诞节快来了,来给你的头像加个帽子吧 看着大伙都在弄这个,我自己也来试一哈,我分别用了两种方式来实现,一种是普通的方式,一种是wxs方式 普通方式 效果图如下: [图片] 思路 获取头像 选择素材 缩放,移动,旋转素材 生成canvas 生成图片,保存图片 实现方式 [图片] 首先是获取头像,这个不用说,大家应该都会的。 选择素材这里我准备了三张圣诞帽的素材,这个网上有很多,可以自己找下,然后我还做了一个选择手机相册的功能,如果你自己有素材的话也可以直接选择这个功能。 缩放,移动,旋转素材都是通过触摸函数去实现的,这里是先将布局做好,然后在标签上面绑定各个触摸事件,通过返回的值在标签的style里设置实现各个效果。 调整好了之后点击保存头像会获取所有参数并将头像画出来,再通过 [代码]wx.canvasToTempFilePath()[代码] 将canvas生成图片最后通过 [代码]wx.saveImageToPhotosAlbum()[代码] 保存图片。 主要代码 主要的函数就是下面这几个,代码片段我会放在文末,没有什么比较难的地方,就是要注意下计算的时候不要算错就行。 [图片] 需要注意的点 由于素材的大小可能会有不同,所以在重新选择素材的时候高度要重新设置一下,这里我用了一个方法来重置高度,主要是每次重新选择素材的时候就用 [代码]wx.getImageInfo()[代码] 这个api去获取图片素材的宽高,再计算出宽高比。 [图片] wxs实现方式 实现方式 思路跟普通方式是一样的,不同的是这里将绑定事件通过 [代码]wxs[代码] 去实现,直接设置标签的参数而不通过逻辑层去处理,在性能上会比较好一点,不过这种实现方式在进行旋转的时候最后生成的图片会有不准,后面会说到。 参数的获取是通过在标签上设置style,然后点击保存的时候用 [代码]wx.createSelectorQuery()[代码] 获取各个参数的 [图片] [图片] 获取旋转的值 由于 [代码]wx.createSelectorQuery()[代码] 并不能获取到 [代码]rotate[代码] 这个参数,所以我是通过下面这种方式来拿到旋转的值的,将旋转值以宽度的形式赋值给 [代码].vo-ro[代码] [图片] [图片] 但是我发现旋转之后生成的图片不是正确的,原因是旋转之后通过 [代码]wx.createSelectorQuery()[代码] 拿到的宽高并不是图片大小的宽高,而是旋转之后的宽高,按理来说不应该是这样的,即使通过样式旋转,它的宽高应该保持不变才对,这样就造成了参数上的错误,所以画出来的图片是不准确的。 因为加了旋转之后画出来的图片会不准确,暂时想不出别的方法,我把旋转的按钮先注释掉了,只支持缩放跟拖拽。 总结 两种方式,wxs性能要更好,但是效果没第一种的好,看你要哪种了,最后祝大家圣诞节快乐,祝你生活愉快 https://developers.weixin.qq.com/s/Cizd1RmY7qdg
2019-12-25 - 云开发数据库命令之地理位置查询
在进行云开发的数据更新的时候,我们可以进行不同条件的查询,从而提升我们更新的效率。在云开发支持了 Geo Point 等相关数据类型以后,我们可以用云开发实现多种不同地理位置的数据存储和利用,对于我们开发基于地理位置的小程序来说,有很大的帮助。 今天的课程中,我们来介绍小程序数据更新命令中的「地理位置查询」命令。 geoNear:查询特定点附近的数据 查询特定点附近的数据可以说是我们在进行应用开发时,最为常用的功能,它可以应用于诸如查询当前用户坐标周围的店铺、查询距离我最近的公交站等场景,这个时候,我们需要使用 [代码]geoNear[代码] 来进行数据库查询。 数据结构需求 如果你想要使用 [代码]geoNear[代码],则要求你在数据库中存储的数据对于地理位置的存储是基于 [代码]db.Geo.Point[代码] 进行的,这样你就可以完成使用 [代码]geoNear[代码] 进行查询。 数据查询实例 假设我们当前数据库内数据的结构是这样的,每一个数据下有一个 point 属性,这个属性是通过 [代码]db.Geo.Point[代码] 添加的。 [图片] 如果我们希望查询距离当前位置,1000米 ~ 2000米的数据,则可以执行这样的命令 [代码]const db = wx.cloud.database() const _ = db.command db.collection('items').where({ location: _.geoNear({ geometry: db.Geo.Point(113.323809, 23.097732), minDistance: 1000, maxDistance: 2000, }) }).get() [代码] 这里的 [代码]geometry[代码] 中的 [代码]Point[代码] 的数据是获取到的当前地理位置信息,它必须是 [代码]db.Geo.Point[代码] 类型的,你可以通过小程序的 [代码]wx.getLocation[代码] 方法获取当前的地址信息,并将其作为参数设置在这里。 [代码]minDistance[代码] 则是距离中心点的最小距离,单位是米,所以这里将其设置为 1000。类似的,[代码]maxDistance[代码] 则是距离中心点的最大距离,单位也是米,所以这里将其设置为 2000。 通过这样的方法,我们就可以查询出数据了。 在我们去做一些基于地理位置的应用的时候,[代码]db.command.geoNear[代码] 可以很大程度上简化我们的开发。 geoWithin:查询特定区域内的数据 在开发地理位置应用时,除了基于某一个点的位置进行查询以外,我们还会查询某一个区域内的数据,比如查询北京市昌平区内的所有的酒吧、查询深圳南山区内所有的博物馆,这样的需求也是切实存在,并且十分常见的需求。这个时候,我们可以考虑,使用 [代码]geoWithin[代码] 来进行数据查询。 数据结构需求 如果你想要使用 [代码]geoWithin[代码],则要求你在数据库中存储的数据对于地理位置的存储是基于 [代码]db.Geo.Point[代码] 进行的,这样你就可以完成使用 [代码]geoWithin[代码] 进行查询。 数据查询实例 假设我们当前数据库内数据的结构是这样的,每一个数据下有一个 point 属性,这个属性是通过 [代码]db.Geo.Point[代码] 添加的。 [图片] 如果我们希望查询在东经 112 度 ~ 东经 114 度,北纬 22 度 到 北纬 24 度范围内的数据,则可以执行这样的数据查询 [代码]const db = wx.cloud.database() const _ = db.command const { Point, LineString, Polygon } = db.Geo db.collection('items').where({ location: _.geoWithin({ geometry: Polygon([ LineString([ Point(112, 22), Point(112, 24), Point(114, 24), Point(114, 22), Point(112,22) ]) ]), }) }).get() [代码] 我们通过 [代码]Polygon[代码] 和 [代码]LineString[代码] 构建出了一个正方形,从而实现了查询一个特定的正方形区域内的数据。 如果你希望查询一个其他形状的范围内的数据,只需要传入多个 [代码]Point[代码] 的数据就可以完成,比如如果你要查询一个五边形内部的数据,也只需在构建 LineString 时,传入 6 个 Point的数据即可。 为什么五边形却是 6 个 Point 呢? 因为在云开发中,如果你要构建一个多边形,则需要使得整个多边形是闭合的,也就是说,你的起始点是一样的,因此,如果你想要构建一个四边形,则需要五个点。如果是构建五边形,则是六个点。 geoIntersects:查询与特定区域相交的数据 [代码]geoIntersects[代码] 是用于查询所有数据中和给定数据相交的数据,我们可以将其用作判断某一些特定的点、线、面是否在一个特定区域内。举个例子,假设你已经有了用户当前的活动范围,比如某一条街道,那么你可以基于 [代码]geoIntersects[代码] 来构建一条线,并基于这条线查询,所有数据中,是否有数据与这个线相交,如果相交,则说明对应的数据点是在用户所在的街道上。你就可以将这个数据告诉用户,让用户去找这些点。 相比于 [代码]geoWithin[代码],[代码]geoIntersects[代码] 对于当前用户的位置数据更为随意,支持 [代码]Point[代码]、[代码]LineString[代码]、[代码]MultiPoint[代码]、 [代码]MultiLineString[代码]、 [代码]Polygon[代码]、 [代码]MultiPolygon[代码] 等多种不同的数据结构,在进行查询的时候,更加的方便。 数据结构需求 如果你想要使用 [代码]geoIntersects[代码],则要求你在数据库中存储的数据对于地理位置的存储是基于 [代码]db.Geo.Point[代码] 进行的,这样你就可以完成使用 [代码]geoIntersects[代码] 进行查询。 数据查询实例 假设我们当前数据库内数据的结构是这样的,每一个数据下有一个 point 属性,这个属性是通过 [代码]db.Geo.Point[代码] 添加的。 [图片] 如果我们希望查询在东经 112 度 ~ 东经 114 度,北纬 22 度 到 北纬 24 度范围内的数据,则可以执行这样的数据查询 [代码]const db = wx.cloud.database() const _ = db.command const { Point, LineString, Polygon } = db.Geo db.collection('items').where({ location: _.geoIntersects({ geometry: Polygon([ LineString([ Point(112, 22), Point(112, 24), Point(114, 24), Point(114, 22), Point(112,22) ]) ]), }) }).get() [代码] 我们通过 [代码]Polygon[代码] 和 [代码]LineString[代码] 构建出了一个正方形,从而实现了查询一个特定的正方形区域内的数据。 如果你希望查询一个其他形状的范围内的数据,只需要传入多个 [代码]Point[代码] 的数据就可以完成,比如如果你要查询一个五边形内部的数据,也只需在构建 LineString 时,传入 6 个 Point的数据即可。 当然,在使用时,你可以根据自己的实际情况,设定不同的图形类型,作为数据库查询的对象,完成自己的数据查询需求。 总结 这节课,我们介绍了 [代码]geoNear[代码]、[代码]geoWithin[代码]、[代码]geoIntersects[代码] 三个 API,帮助大家理解其各自在什么样的场景下使用,下一节课,我们将介绍 eq、neq、lt、lte、gt、gte 几个命令。
2019-09-23