- 新能力解读:小程序后台持续定位功能
在 2019 年 7 月 30 日发布的小程序基础库 v2.8.0 版本中,新增了小程序后台持续定位功能,在这篇文章中,将为你介绍这个能力的用途和用法。 新增的后台持续定位能力能干嘛? 在基础库版本 v2.8.0 版本以前,小程序提供的 API 仅有 [代码]wx.openLocation[代码]、[代码]wx.getLocation[代码] 和 [代码]wx.chooseLocation[代码],这些 API 都仅能用于某一时刻用户位置的获取和选择,这导致在进行产品设计上,是无法完成对于一些具有实时性要求的需求。所有的用户位置状态都是一个特定的时刻。 新增的后台持续定位能力可以让你在你的地图应用中实现实时的定位。在没有该能力之前,如果你需要实现类似的功能,需要每隔一秒获取一次地址,以获取当前用户的位置,并进行比较,但晒在新的后台持续定位能力中,你可以直接在 [代码]wx.onLocationChange[代码] 方法中获取到变动后的位置,相比于之前的轮询的方式,新的方式会更加的省电。 新增的后台持续定位能力应当如何使用? 在使用新的后台持续定位能力时,你需要根据实际的情况,选择使用 [代码]wx.startLocationUpdate[代码] 或 [代码]wx.startLocationUpdateBackground[代码] 从而开启自动获取数据。 随后,使用 [代码]wx.onLocationChange[代码] 来获取变动后的位置,从而对该位置进化后续的业务逻辑的判断。 当你使用完成后,需要使用 [代码]wx.stopLocationUpdate[代码] 来禁用后台持续定位能力,节省设备电量。 后台持续定位能力使用注意事项? 在使用后台持续定位能力时需要注意两点: 调用前需要 用户授权 scope.userLocationBackground 该功能从基础库 2.8.0 开始支持,低版本需做兼容处理。
2019-08-04 - 这些 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 - 小程序图片裁剪插件 image-cropper
之前的插件类目没有了导致搜不到了,重新发个文章。 image-cropper 一款高性能的小程序图片裁剪插件,支持旋转。 [图片] 优势 [代码]1.功能强大。[代码] [代码]2.性能超高超流畅,大图毫无卡顿感。[代码] [代码]3.组件化,使用简单。[代码] [代码]4.点击中间窗口实时查看裁剪结果。[代码] ㅤ 初始准备 1.json文件中添加image-cropper [代码] "usingComponents": { "image-cropper": "../image-cropper/image-cropper" }, "navigationBarTitleText": "裁剪图片", "disableScroll": true [代码] 2.wxml文件 [代码]<image-cropper id="image-cropper" limit_move="{{true}}" disable_rotate="{{true}}" width="{{width}}" height="{{height}}" imgSrc="{{src}}" bindload="cropperload" bindimageload="loadimage" bindtapcut="clickcut"></image-cropper> [代码] 3.简单示例 [代码] Page({ data: { src:'', width:250,//宽度 height: 250,//高度 }, onLoad: function (options) { //获取到image-cropper实例 this.cropper = this.selectComponent("#image-cropper"); //开始裁剪 this.setData({ src:"https://raw.githubusercontent.com/1977474741/image-cropper/dev/image/code.jpg", }); wx.showLoading({ title: '加载中' }) }, cropperload(e){ console.log("cropper初始化完成"); }, loadimage(e){ console.log("图片加载完成",e.detail); wx.hideLoading(); //重置图片角度、缩放、位置 this.cropper.imgReset(); }, clickcut(e) { console.log(e.detail); //点击裁剪框阅览图片 wx.previewImage({ current: e.detail.url, // 当前显示图片的http链接 urls: [e.detail.url] // 需要预览的图片http链接列表 }) }, }) [代码] 参数说明 属性 类型 缺省值 取值 描述 必填 imgSrc String 无 无限制 图片地址(如果是网络图片需配置安全域名) 否 disable_rotate Boolean false true/false 禁止用户旋转(为false时建议同时设置limit_move为false) 否 limit_move Boolean false true/false 限制图片移动范围(裁剪框始终在图片内)(为true时建议同时设置disable_rotate为true) 否 width Number 200 超过屏幕宽度自动转为屏幕宽度 裁剪框宽度 否 height Number 200 超过屏幕高度自动转为屏幕高度 裁剪框高度 否 max_width Number 300 裁剪框最大宽度 裁剪框最大宽度 否 max_height Number 300 裁剪框最大高度 裁剪框最大高度 否 min_width Number 100 裁剪框最小宽度 裁剪框最小宽度 否 min_height Number 100 裁剪框最小高度 裁剪框最小高度 否 disable_width Boolean false true/false 锁定裁剪框宽度 否 disable_height Boolean false true/false 锁定裁剪框高度 否 disable_ratio Boolean false true/false 锁定裁剪框比例 否 export_scale Number 3 无限制 输出图片的比例(相对于裁剪框尺寸) 否 quality Number 1 0-1 生成的图片质量 否 cut_top Number 居中 始终在屏幕内 裁剪框上边距 否 cut_left Number 居中 始终在屏幕内 裁剪框左边距 否 [代码]img_width[代码] Number 宽高都不设置,最小边填满裁剪框 支持%(不加单位为px)(只设置宽度,高度自适应) 图片宽度 否 [代码]img_height[代码] Number 宽高都不设置,最小边填满裁剪框 支持%(不加单位为px)(只设置高度,宽度自适应) 图片高度 否 scale Number 1 无限制 图片的缩放比 否 angle Number 0 (limit_move=true时angle=n*90) 图片的旋转角度 否 min_scale Number 0.5 无限制 图片的最小缩放比 否 max_scale Number 2 无限制 图片的最大缩放比 否 bindload Function null 函数名称 cropper初始化完成 否 bindimageload Function null 函数名称 图片加载完成,返回值Object{width,height,path,type等} 否 bindtapcut Function null 函数名称 点击中间裁剪框,返回值Object{src,width,height} 否 函数说明 函数名 参数 返回值 描述 参数必填 upload 无 无 调起wx上传图片接口并开始剪裁 否 pushImg src 无 放入图片开始裁剪 是 getImg Function(回调函数) [代码]Object{url,width,height}[代码] 裁剪并获取图片(图片尺寸 = 图片宽高 * export_scale) 是 setCutXY X、Y 无 设置裁剪框位置 是 setCutSize width、height 无 设置裁剪框大小 是 setCutCenter 无 无 设置裁剪框居中 否 setScale scale 无 设置图片缩放比例(不受min_scale、max_scale影响) 是 setAngle deg 无 设置图片旋转角度(带过渡效果) 是 setTransform {x,y,angle,scale,cutX,cutY} 无 图片在原有基础上的变化(scale受min_scale、max_scale影响) 根据需要传参 imgReset 无 无 重置图片的角度、缩放、位置(可以在onloadImage回调里使用) 否 GitHub https://github.com/wx-plugin/image-cropper/tree/master 如果有什么好的建议欢迎提issues或者提pr
2021-12-15 - 如何将数组中的相同二级分类再合并?
原始数据:[{ { "_id": "8d65afde5e4d2a79010af3ae173f3c31", "imageC": "http://tmp/wx9696d4e5754a92b0.o6zAJs_sYdqkP5OplRpgfPIz-Vis.9cIQCAVc8Pe4a3f590515973ba69b44c5f86b56dff81.png", "nameA": "A", "nameB": "A3", "nameC": "E3333" }, { "_id": "8d65afde5e4d2a79010af3b03f094576", "imageC": "http://tmp/wx9696d4e5754a92b0.o6zAJs_sYdqkP5OplRpgfPIz-Vis.9cIQCAVc8Pe4a3f590515973ba69b44c5f86b56dff81.png", "nameA": "A", "nameB": "A3", "nameC": "E3333" }, { "_id": "fb16f7905e4d2c00010c97000eab34a6", "imageC": "http://tmp/wx9696d4e5754a92b0.o6zAJs_sYdqkP5OplRpgfPIz-Vis.9cIQCAVc8Pe4a3f590515973ba69b44c5f86b56dff81.png", "nameA": "B", "nameB": "A5", "nameC": "E3333" }, { "_id": "4278fc3a5e4d2c080109a9095277efc3", "imageC": "http://tmp/wx9696d4e5754a92b0.o6zAJs_sYdqkP5OplRpgfPIz-Vis.9cIQCAVc8Pe4a3f590515973ba69b44c5f86b56dff81.png", "nameA": "B", "nameB": "A5", "nameC": "E222222" }, { "_id": "fb16f7905e4d2c0f010c9de51a769a17", "imageC": "http://tmp/wx9696d4e5754a92b0.o6zAJs_sYdqkP5OplRpgfPIz-Vis.9cIQCAVc8Pe4a3f590515973ba69b44c5f86b56dff81.png", "nameA": "B", "nameB": "A5", "nameC": "E11111" }, { "_id": "e30d61715e4d2c15010a550712a94455", "imageC": "http://tmp/wx9696d4e5754a92b0.o6zAJs_sYdqkP5OplRpgfPIz-Vis.9cIQCAVc8Pe4a3f590515973ba69b44c5f86b56dff81.png", "nameA": "B", "nameB": "A4", "nameC": "E11111" }, { "_id": "7d79e8035e4d2c1b010b25d421d9aabc", "imageC": "http://tmp/wx9696d4e5754a92b0.o6zAJs_sYdqkP5OplRpgfPIz-Vis.9cIQCAVc8Pe4a3f590515973ba69b44c5f86b56dff81.png", "nameA": "B", "nameB": "A4", "nameC": "E222222" }, { "_id": "7d79e8035e4d2c23010b29a05652d1ad", "imageC": "http://tmp/wx9696d4e5754a92b0.o6zAJs_sYdqkP5OplRpgfPIz-Vis.9cIQCAVc8Pe4a3f590515973ba69b44c5f86b56dff81.png", "nameA": "B", "nameB": "A4", "nameC": "E3333" }, { "_id": "8d65afde5e4d2c2b010bd06b467f35c3", "imageC": "http://tmp/wx9696d4e5754a92b0.o6zAJs_sYdqkP5OplRpgfPIz-Vis.9cIQCAVc8Pe4a3f590515973ba69b44c5f86b56dff81.png", "nameA": "B", "nameB": "A3", "nameC": "E3333" }, { "_id": "e30d61715e4d2c30010a62d60a9231b1", "imageC": "http://tmp/wx9696d4e5754a92b0.o6zAJs_sYdqkP5OplRpgfPIz-Vis.9cIQCAVc8Pe4a3f590515973ba69b44c5f86b56dff81.png", "nameA": "B", "nameB": "A3", "nameC": "E2222" } ] {[图片] 全成后希望变成相同的nameB的image子项并成一个数组,请指教!!! [ { "_id": "C", "child": [ { "nameB": "A3", "image": [{ "nameC": "E1111", "imageC": "http://tmp/wx9696d4e5754a92b0.o6zAJs_sYdqkP5OplRpgfPIz-Vis.9cIQCAVc8Pe4a3f590515973ba69b44c5f86b56dff81.png" }, { "nameC": "E22222", "imageC": "http://tmp/wx9696d4e5754a92b0.o6zAJs_sYdqkP5OplRpgfPIz-Vis.9cIQCAVc8Pe4a3f590515973ba69b44c5f86b56dff81.png" }, { "nameC": "E33333", "imageC": "http://tmp/wx9696d4e5754a92b0.o6zAJs_sYdqkP5OplRpgfPIz-Vis.9cIQCAVc8Pe4a3f590515973ba69b44c5f86b56dff81.png" } ]
2020-02-23 - 微信小程序UI组件库合集
UI组件库合集,大家有遇到好的组件库,欢迎留言评论然后加入到文档里。 第一款: 官方WeUI组件库,地址 https://developers.weixin.qq.com/miniprogram/dev/extended/weui/ 预览码: [图片] 第二款: ColorUI:地址 https://github.com/weilanwl/ColorUI 预览码: [图片] 第三款: vantUI(又名:ZanUI):地址 https://youzan.github.io/vant-weapp/#/intro 预览码: [图片] 第四款: MinUI: 地址 https://meili.github.io/min/docs/minui/index.html 预览码: [图片] 第五款: iview-weapp:地址 https://weapp.iviewui.com/docs/guide/start 预览码: [图片] 第六款: WXRUI:暂无地址 预览码: [图片] 第七款: WuxUI:地址https://www.wuxui.com/#/introduce 预览码: [图片] 第八款: WussUI:地址 https://phonycode.github.io/wuss-weapp/quickstart.html 预览码: [图片] 第九款: TouchUI:地址 https://github.com/uileader/touchwx 预览码: [图片] 第十款: Hello UniApp: 地址 https://m3w.cn/uniapp 预览码: [图片] 第十一款: TaroUI:地址 https://taro-ui.jd.com/#/docs/introduction 预览码: [图片] 第十二款: Thor UI: 地址 https://thorui.cn/doc/ 预览码: [图片] 第十三款: GUI:https://github.com/Gensp/GUI 预览码: [图片] 第十四款: QyUI:暂无地址 预览码: [图片] 第十五款: WxaUI:暂无地址 预览码: [图片] 第十六款: kaiUI: github地址 https://github.com/Chaunjie/kai-ui 组件库文档:https://chaunjie.github.io/kui/dist/#/start 预览码: [图片] 第十七款: YsUI:暂无地址 预览码: [图片] 第十八款: BeeUI:git地址 http://ued.local.17173.com/gitlab/wxc/beeui.git 预览码: [图片] 第十九款: AntUI: 暂无地址 预览码: [图片] 第二十款: BleuUI:暂无地址 预览码: [图片] 第二十一款: uniydUI:暂无地址 预览码: [图片] 第二十二款: RovingUI:暂无地址 预览码: [图片] 第二十三款: DojayUI:暂无地址 预览码: [图片] 第二十四款: SkyUI:暂无地址 预览码: [图片] 第二十五款: YuUI:暂无地址 预览码: [图片] 第二十六款: wePyUI:暂无地址 预览码: [图片] 第二十七款: WXDUI:暂无地址 预览码: [图片] 第二十八款: XviewUI:暂无地址 预览码: [图片] 第二十九款: MinaUI:暂无地址 预览码: [图片] 第三十款: InyUI:暂无地址 预览码: [图片] 第三十一款: easyUI:地址 https://github.com/qq865738120/easyUI 预览码: [图片] 第三十二款 Kbone-UI: 地址 https://wechat-miniprogram.github.io/kboneui/ui/#/ 暂无预览码 第三十三款 VtuUi: 地址 https://github.com/jisida/VtuWeapp 预览码: [图片] 第三十四款 Lin-UI 地址:http://doc.mini.talelin.com/ 预览码: [图片] 第三十五款 GraceUI 地址: http://grace.hcoder.net/ 这个是收费的哦~ 预览码: [图片] 第三十六款 anna-remax-ui npm:https://www.npmjs.com/package/anna-remax-ui/v/1.0.12 anna-remax-ui 地址: https://annasearl.github.io/anna-remax-ui/components/general/button 预览码 [图片] 第三十七款 Olympus UI 地址:暂无 网易严选出品。 预览码 [图片] 第三十八款 AiYunXiaoUI 地址暂无 预览码 [图片] 第三十九款 visionUI npm:https://www.npmjs.com/package/vision-ui 预览码: [图片] 第四十款 AnimaUI(灵动UI) 地址:https://github.com/AnimaUI/wechat-miniprogram 预览码: [图片] 第四十一款 uView 地址:http://uviewui.com/components/quickstart.html 预览码: [图片] 第四十二款 firstUI 地址:https://www.firstui.cn/ 预览码: [图片]
2023-01-10 - 小程序开发常用的几个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 - 请问要怎么写才能根据给定的keyword数组自动生成如下查询语句?
let keyword=['酒店','宾馆'] 假设要查询的关键词是一个数组(如:['酒店','宾馆']),请问要怎么写才能根据给定的keyword数组自动生成如下查询语句。而不是手动写死的。 目的是把集合中 company_name字段 包含 酒店 或 宾馆 的数据查询出来。 db.collection('point') .where({ company_name: _.or( new db.RegExp({ regexp: '酒店', options: 'i', }), new db.RegExp({ regexp: '宾馆', options: 'i', }) ) }) .get()
2021-10-08 - 小程序 大文件 多图 直接上传七牛云
思想:通过从后端获取的七牛云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 - [已解决]利用云开发解决小程序加载自定义字体无效果问题
终极效果 效果要求:使得最终页面效果在开发者模拟器、安卓、iOS都可正常显示字体效果。 [图片] 问题背景 如何在微信小程序上利用wx.loadFontFace方法加载自己要上传的字体的文件一直是困扰很多开发者的问题,很多人反映使用该方法在开发者工具模拟器上是可以显示的,有的开发者说在模拟器和iOS系统上可以正常显示自定义字体,但是安卓机上无法正常显示。 很多人为了能够实现加载自定义字体想了很多办法,比如 方法1: 字体文件转base64代码(如网站https://www.transfonter.org) ,然后存储在wxss文件中。这也是我曾使用的方法之一,确实很好用,安卓/苹果机上都可以正常实现效果。 缺点:但是面对字体文件包一般都很大的中文字体来说,转换成base64代码是极容易出现仅用来引用字体的wxss代码就超过2M的情况。 根据小程序的开发规矩,即使采用分包形式开发一个小程序,单个分包的代码量也是不能超过2M的,此时就是该方法的鸡肋。 方法2: 也有开发者利用iconfont上的在线引用方法,把自己会用到的字输进入然后点击生成字体,引用一个url即可实现。这种方法也是可行的。 缺点:但是对于部分字多或者可能出现用户输入文字的情况,这种方法就不太方便。 解决方案 今天,和大家讲的就是wx.loadFontFace方法,也是很多人一直在尝试,执着于想要利用这种方法实现。 在小程序开发者文档中,这一段文字是你不陌生的。 wx.loadFontFace(Object object) 基础库 2.1.0 开始支持,低版本需做兼容处理。 本接口从基础库版本 2.15.0 起支持在小程序插件中使用 动态加载网络字体,文件地址需为下载类型。‘2.10.0’起支持全局生效,需在 app.js 中调用。 注意: 字体文件返回的 contet-type 参考 font,格式不正确时会解析失败。 字体链接必须是https(ios不支持http) 字体链接必须是同源下的,或开启了cors支持,小程序的域名是servicewechat.com canvas等原生组件不支持使用接口添加的字体 工具里提示 Faild to load font可以忽略 ’2.10.0’ 以前仅在调用页面生效。 ——来源于《微信小程序开发者文档》 分析: 注意点1是大家一般无需担心的,我们下载到的字体文字很多是ttf文件,都是符合相关要求的。 注意点2也是一般不会犯错的,大家都会避开这个。 注意点3是非常重要的,也是很多开发者不明白的,下面,我会着重讲如何解决这个问题,解决了这个问题,无论调试还是真机都能正常显示。 在开发者文档中,有这样一段代码: [代码]wx.loadFontFace({ family: 'Bitstream Vera Serif Bold', source: 'url("https://sungd.github.io/Pacifico.ttf")', success: console.log }) [代码] 可以看出使用的是https链接,或许你曾经拿这段代码测试过。 或者你也曾经把字体文件上传到一些存储空间然后得到上传后文件的地址,或者说你曾经在开发者社区看到别人上传的字体文件的https链接然后来调用。 在开发者工具模拟器上正常显示,一到真机就显示不了。 当然为了想要使用我自己喜欢的字体,得到该字体的文件链接,我也突然脑洞大开,尝试把字体文件上传到云开发的云存储上,然后得到一个FileID链接和一个下载链接。 随后在页面或者全局的JS中加载该字体文件: 代码: [代码]loadFontFace() { wx.loadFontFace({ family: 'XXX', source: 'url("xxxxxx.ttf")', success(res){ console.log('res', res) }, fail(err){ console.log('err', err) } }) }, [代码] 遗憾的是,也是能在模拟器中显示出自定义字体效果,在安卓的真机上无法正常显示,iOS未知(我没有iOS系统故无法测试)。 到底是怎么回事,问题出在哪呢?于是一遍遍琢磨开发文档中的注意事项“字体链接必须是同源下的,或开启了cors支持,小程序的域名是servicewechat.com”。 后来琢磨后面一句话,我也是不明白啥是cors,于是就搜索了,记得其中一篇文章就讲到腾讯云的cors之类的。 操作步骤 1 进入腾讯云的官网(https://cloud.tencent.com),然后登录账号,选择公众号小程序登录,扫码并选择需要实现自定义字体的小程序进行登录 2 在顶部菜单栏找到“对象存储” [图片] 3 存储桶列表中新建一个存储桶 [图片] 4 给存储桶设置配置,跨域访问中设置好“cors规则”,把https://servicewechat.com域名填写到来源Origin的方框内。 [图片] 现在再来读读“字体链接必须是同源下的,或开启了cors支持,小程序的域名是servicewechat.com”是不是有了新的体会。 5 把需要使用的字体文件上传到该存储桶,然后点击文件右边的“详情”,你就会看到“对象地址”,这个地址就可以粘贴到JS文件的wx.loadFontFace使用。 [图片] 6 不要忘了设置刚刚上传的字体文件“访问权限”为“公有读私有写”,这样每个用户都可以读取这个字体文件了。 源码分享 JS文件 [代码]loadFontFace() { wx.loadFontFace({ family: 'XXX(你给字体拟的名称,最好英文如kaiti)', source: 'url("对象地址")', success(res){ console.log('res', res) }, fail(err){ console.log('err', err) } }) }, [代码] WXSS文件 [代码]page{ font-family: 'XXX'(你给字体拟的名称,最好英文如kaiti); } [代码] 或者 [代码].xxx(class名){ font-family:'xxx(你给字体拟的名称,最好英文如kaiti)' } [代码]
2021-04-13 - 微信小程序 -- 基于 movable-view 实现拖拽排序
微信小程序 – 基于 movable-view 实现拖拽排序 项目基于[代码]colorui[代码]样式组件 ColorUI组件库 (color-ui.com) 1.实现效果 [图片] 2. 设计思路 movable-view 绑定块移动事件的 块[代码]ID[代码] ,块移动的坐标 移动结束后触发[代码]moveEnd[代码]事件,根据[代码]Y[代码]坐标对对象数组进行排序 根据排序结果重置块位置 3.实现代码 代码已经进行了最简化处理 图中效果实现需引入[代码]colorui[代码]的[代码]main.wxss[代码]样式部分。 wxml [代码]<movable-area class="padding text-center bg-grey" style="width:100%;height:500px;" > <movable-view class="radius shadow bg-white" style="width:80%;height:80px;z-index:{{index==moveId?2:1}}" wx:for="{{tabList}}" wx:key="index" x="{{item.x}}" y="{{item.y}}" direction="all" bindchange="moveStatus" bindtouchend='moveEnd' data-moveid="{{index}}"> {{item.name}}</movable-view> </movable-area> [代码] js [代码]var compare = function (obj1, obj2) { var val1 = obj1.y; var val2 = obj2.y; if (val1 < val2) { return -1; } else if (val1 >= val2) { return 1; } else { return 0; } } Page({ /** * 页面的初始数据 */ data: { branchid:'', appdocid:'', tabList:[ { name:'十步杀一人' }, { name:'千里不留行' }, { name:'事了拂衣去' }, { name:'深藏身与名' } ], //移动的是哪个元素块 moveId:null, //最终停止的位置 endX:0, endY:0 }, initMove(){ let tabList = this.data.tabList; var tarr = [] tabList.forEach(function(ele,index){ let obj = ele obj.id = index obj.x = 30 obj.y = 100*index +20 tarr.push(obj) }) console.log(tarr) this.setData({ tabList:tarr }) }, moveEnd(e){ console.log(e) var that = this; that.setData({ ["tabList["+that.data.moveId+"].x"]:that.data.endX, ["tabList["+that.data.moveId+"].y"]:that.data.endY },()=>{ let tabList = this.data.tabList; tabList = tabList.sort(compare); that.setData({ tabList },()=>{ setTimeout(function(){ that.initMove(); },500) }) }) //计算位置 }, moveStatus(e){ // console.log(e) //移动的块ID var moveid = e.currentTarget.dataset.moveid; //最终坐标 let x = e.detail.x let y = e.detail.y this.setData({ moveId:moveid, endX:x, endY:y }) }, /** * 生命周期函数--监听页面加载 */ onLoad: function (options) { }, /** * 生命周期函数--监听页面初次渲染完成 */ onReady: function () { this.initMove(); } }) [代码] 4.参考文档 movable-view | 微信开放文档 (qq.com)
2021-06-17 - [拎包哥] 批量下载云开发云存储的文件到pc端
官方教程 注:第2步和第3部的代码是反过来的。 [图片] 我的步骤: 0.按照官方教程安装tcb脚手架。 1.在桌面新建文件夹hellowWorld,新建cloudbaserc.json,json里的内容为: [图片] { "envId":"你的云开发环境的id" } 2.在cmd输入命令行。 e.g. 下载云存储里的qrCode文件夹到PC端文件夹(桌面的helloWorld文件夹)。 注:--dir在这里的意思是声明前面的 . 是一个文件夹,不需要另外修改。 [图片] tcb storage download qrCode . --dir
2021-09-28 - 【笔记】JS——操作DOM结点的总结
HTML DOM(文档对象模型) 当网页被加载时,浏览器会创建页面的文档对象模型(Document Object Model)。 通过 HTML 对象选择器查找 HTML 对象 本例查找 id=“frm1” 的 form 元素,在 forms 集合中,然后显示所有元素值,包含在 document.elements中,通过x.elements[i].value来调用,比如一个个input输入的值 DOM动画 应该通过 style = “position: relative” 创建容器元素。 应该通过 style = “position: absolute” 创建动画元素。 上左偏移量在极短的时间内迅速增大而造成了视觉上的“动画” [图片] 当用户进入后及离开页面时,会触发 onload 和 onunload 事件。 onload 和 onunload 事件可用于处理 cookie。 例如:if (navigator.cookieEnabled == true) onchange 和onfocus事件经常与输入字段验证结合使用,鼠标移出输入框时触发,输入框获得焦点时触发 [代码]<input type="text" id="fname" onchange="myFunction()" onfocus="this.style.color="yellow""> function myFunction() { var x = document.getElementById("fname"); x.value = x.value.toUpperCase(); } [代码] onmouseover ,onmousemove和 onmouseout 事件 onmouseover为鼠标刚移入区域;onmousemove为鼠标在区域内移动;onmouseout为移出区域 [图片] onmousedown, onmouseup 以及 onclick 事件 onmousedown, onmouseup 以及 onclick 事件构成了完整的鼠标点击事件。 首先当鼠标按钮被点击时,onmousedown 事件被触发;然后当鼠标按钮被释放时,onmouseup 事件被触发;最后,当鼠标点击完成后,onclick 事件被触发。 JavaScript HTML DOM 事件监听器 addEventListener() 方法 [代码]element.addEventListener(event, function, useCapture); [代码] 第一个参数是事件的类型(比如 “click” 或 “mousedown”)。 第二个参数是当事件发生时我们需要调用的函数。 第三个参数是布尔值,指定使用事件冒泡还是事件捕获。此参数是可选的。 **注意:**请勿对事件使用 “on” 前缀;请使用 “click” 代替 “onclick”。 事件冒泡还是事件捕获? [图片] DOM操作 nodeName 属性 nodeName 属性规定节点的名称。 nodeName 是只读的 元素节点的 nodeName 等同于标签名 属性节点的 nodeName 是属性名称 文本节点的 nodeName 总是 #text 文档节点的 nodeName 总是 #document nodeValue 属性 nodeValue 属性规定节点的值。 元素节点的 nodeValue 是 undefined 文本节点的 nodeValue 是文本文本 属性节点的 nodeValue 是属性值 nodeType 属性 nodeType 属性返回节点的类型。nodeType 是只读的。 [图片] DOM节点 追加:appendChild() [图片] 标签之间插入某新结点insertBefore() [图片] 删除结点removeChild() [代码]var child = document.getElementById("p1"); child.parentNode.removeChild(child); [代码] 替换结点replaceChild() [代码]<div id="div1"> <p id="p1">这是一个段落。</p> <p id="p2">这是另一个段落。</p> </div> <script> var para = document.createElement("p"); var node = document.createTextNode("这是新文本。"); para.appendChild(node); var parent = document.getElementById("div1"); var child = document.getElementById("p1"); parent.replaceChild(para, child); </script> [代码] DOM集合 getElementsByTagName() 方法返回 HTMLCollection 对象。 HTMLCollection 对象是类数组的 HTML 元素列表(集合)。 下面的代码选取文档中的所有 <p> 元素: 实例 [代码]var x = document.getElementsByTagName("p"); [代码] 该集合中的元素可通过索引号进行访问。 如需访问第二个 <p> 元素,您可以这样写: [代码]y = x[1]; [代码] length 属性定义了 HTMLCollection 中元素的数量: length 属性在您需要遍历集合中元素时是有用的: 实例 改变所有 <p> 元素的背景色: [代码]var myCollection = document.getElementsByTagName("p"); var i; for (i = 0; i < myCollection.length; i++) { myCollection[i].style.backgroundColor = "red"; } [代码] 无法对 HTMLCollection 使用数组方法,比如 valueOf()、pop()、push() 或 join()。 节点列表,与DOM集合几乎一样 NodeList 对象是从文档中提取的节点列表(集合)。 使用 getElementsByClassName() 方法,某些(老的)浏览器会返回 NodeList 对象而不是 HTMLCollection。 所有浏览器都会为 childNodes 属性返回 NodeList 对象。 大多数浏览器会为 querySelectorAll() 方法返回 NodeList 对象。 总结: HTMLCollection(前一章)是 HTML 元素的集合;NodeList 是文档节点的集合。 访问 HTMLCollection 项目,可以通过它们的名称、id 或索引号。 访问 NodeList 项目,只能通过它们的索引号。 只有 NodeList 对象能包含属性节点和文本节点。 觉得该篇文章有帮助到你复习操作dom节点的知识,请不要忘记忘记点击左下角的大拇指~
2021-11-17 - 云开发该怎么更新数据库(嵌套数组)里匹配的一条数据?
[图片] 这是数据库中其中一个集合, 我想通过 _id 找到这个集合 然后更新 Course.title=="测试考试专用" 的这个数组的Status的值
2021-12-24 - getImageInfo一次获取多张图片信息,怎么处理?
getImageInfo一次获取多张图片信息,怎么处理?
2018-08-29 - 云函数接收公众号消息推送
公众号的消息推送和处理,也可以云开发来做了,不需要搭建自己的服务器了。 具体步骤如下: 1、将小程序的云环境共享给公众号的appid。 操作如下:小程序开发工具--云开发--更多--环境共享--添加共享--添加公众号appid。共享成功; 2、配置公众号云开发 打开开发工具--回到初始页项目管理页--左则项目类型栏选择“公众号网页”--云开发--弹出框里填入公众号appid--进入云控制台 3、配置公众号消息推送 以公众号appid进入云控制台后--更多--环境共享--消息推送--添加消息推送--选择event--选择subscribe_and_unsubscribe--选择接收消息推送的云环境--选择接收消息推送的云函数--结束 4、配置完成,接收消息推送 在云函数里写处理消息推送的代码。 在云函数里: console.log(event)//获取消息包JSON数据 console.log(wxContext)//获取公众号用户的openid和unionid。 5、结束。
2021-10-21 - 有什么办法快速生成或转换json文件为适合导入云数据库的格式吗?
云数据库导入时的json文件实际格式并不是传统的json,没有外部包裹的 [ ] 也没有每条之间的逗号,扔进vscode里都没法格式化 但是我实际从其他人那获得的数据是标准的json格式,可能有几千甚至上万条数据,要想把它转成符合导入要求的格式有什么方便的办法吗?比如有什么转换工具?脚本?
2021-08-10 - 小程序开发必备,这 5 款超实用开源插件!
Parser - 富文本解析 GitHub:https://github.com/jin-yufeng/Parser [图片] calendar - 预约日历 GitHub:https://github.com/jasondu/wxa-plugin-calendar [图片] cropper - 图片裁剪 GitHub:https://github.com/wx-plugin/image-cropper [图片] wxSearch - 搜索框 Github:https://github.com/icindy/wxSearch [图片] WxValidate - 表单验证 Github:https://github.com/wux-weapp/wx-extend/blob/master/docs/components/validate.md [图片] 如有收获,记得点赞、收藏 如有补充或者疑问,欢迎进行留言讨论
2020-08-14 - 云开发如何对外提供回调 URL
在云开发的使用过程中,有不少的用户会提出自己的疑问,我如何才能在自己的云开发应用中加入一个回调地址? 在实际开发过程中,不少涉及到支付、订单等异步操作的场景,会需要提供一个回调 URL,以确保在用户完成自己的支付过程后,由支付服务器对回调的 URL 发起请求,确认调用成功。因此,不少用户在使用时提出了自己的疑问,应该如何实现这样的功能? 由于目前云开发云函数尚未对外提供 HTTP 调用的能力,因此,我们可以借助腾讯云提供的云函数和 API 网关,来实现类似的功能。 架构介绍 [图片] 在进行调用时开发者所设置的回调地址是一个 API 网关的地址,微信支付等服务的服务器在完成操作以后,会执行 HTTP 请求,请求 API 网关。 API 网关对应的是腾讯云云函数,我们可以在腾讯云云函数中调用云开发的 SDK,或者是云开发的 HTTP API,完成对云开发数据库的操作。 具体实现 依赖 腾讯云账号 小程序的 AppID 和 App Secret 实现 1. 创建云函数 访问 https://console.cloud.tencent.com/ ,使用你的腾讯云账号登陆,在顶部菜单栏中找到「云产品」— 「Serverless」—「云函数」 [图片] 在云函数中,选择「函数服务」,并在函数服务页面点击「新建」 [图片] 创建一个新的云函数,其中函数名称根据你的需要填写 [图片] 运行环境选择 Node.js 8.9,创建方式选择空白函数,完成后点击下一步。 在下一页不需要做修改,直接点击完成。 2. 创建 API 网关调用 在创建完成云函数以后,我们会进入到这样的界面,在这个界面中选择「触发方式」 [图片] 在触发方式页面新增一个触发方式,使用 「API 网关触发」,请求方法选择「ANY」,发布环境选择「发布」,鉴权方法选择「免鉴权」。 [图片] 填写完成后,点击保存,然后你会获得一个地址,这个地址就是你后续的回调地址。 [图片] 你可以将这个地址在浏览器中打开,可以看到这样的数据 [图片] 则说明我们的云函数完成了配置。 3. 编写程序调用 当我们完成了云函数的配置以后,接下来我们可以修改云函数,使其完成我们自己想要的功能。 我们希望云函数可以从外部发来的请求中获取到参数,并借助云开发提供的 API,对云开发数据库中的数据进行更新。则我们可以这样操作 我们在本地新建一个目录,并在其中执行如下命令(需要你提前安装了 N ode.js 环境) [代码]cd 新建文件夹 npm init -y npm install -y got [代码] 然后创建一个文件[代码]index.js[代码],并在其中加入如下代码,并设置其中的 APPID、SECRET、ENV_ID 等字段。 [代码]'use strict'; const got = require('got'); const APPID = ''; // 小程序 APPID const SECRET = ''; // 小程序 Secret const ENV_ID = '' const TOKEN_URL = `https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=${APPID}&secret=${SECRET}` const QUERY_URL = 'https://api.weixin.qq.com/tcb/databasecollectionget?access_token=' exports.main_handler = async (event, context, callback) => { // 1. 提取参数 可以从 URL?id=1&status=2 中提取出 1 和 2 const id = event.queryString.id; const status = event.queryString.status; // 2. 获取 Token,用于后续的调用 let token_resp = await got(TOKEN_URL); let token = JSON.parse(token_resp.body).access_token const url = QUERY_URL + token; // 执行云开发命令 let result = await got(url, { method: 'POST', json: true, headers: { 'content-type': 'applicaiton/json', 'accept-encoding':'gzip' }, body: { "env": ENV_ID } }) return result.body }; [代码] 配置完成后,保存文件。 4. 上传代码 打包完成后,重新访问腾讯云控制台,找到刚刚创建的云函数,进入到「函数代码」中,选择其中的「本地上传文件夹」,选择你刚刚创建的文件夹,等待其自动压缩完成后,点击保存上传。 [图片] 上传完成后,会自动进行部署。 你可以重新访问刚刚生成的 API 网关地址,并在其后加入 id 和 status 参数,比如我的最后的地址是 [代码]https://service-51pn7koc-1251337088.gz.apigw.tencentcs.com/release/myFunction?id=1&status=2[代码] [图片] 访问以后,得到的结果是这样的 [图片] 这样就说明我成功的从云函数中访问了云数据库,并且通过一个无需鉴权的 API 地址获取到了信息。 总结 实际上,借助于 API 网关、云函数以及云开发的 API & SDK,你可以实现非常多的功能,能不能实现,就看你的想象力有没有达到啦。
2019-09-18 - 内容安全检测图片API:openapi.security.imgSecCheck完美解决方案。
背景需求: 我个人做了一款小程序的小游戏,本质是小程序。里面有个自定义图片的功能。用户从本地相册选一张图片进行裁剪,之后保存到缓存中或者上传到服务器。然后用户再用这张图片作为素材进行其它操作。这里就涉及到内容安全了,提交审核没有通过也是因为这个没有做内容安全。防止一些色情低俗的事情发生。 正文: 思路:相册选图片 --> 裁剪小的图片 --> 内容安全检测 --> 通过 --> 裁剪大的图片 --> 保存。 失败的原因:绝大多数是因为检测图片不能大于1M,而导致超时,或者是errCode:-1,又或者是其它问题。 [图片] [图片] 核心代码图片: [代码]默认裁剪小尺寸图片 (我的业务需求是正方形图片,也可动态计算宽高比例) [代码] [图片] 检测图片 部分iOS不兼容encoding: ‘ucs2’。注释掉就好了 [图片] [图片] 云函数 [图片] 测试情况: 正常图片不含违法违规,测试20次,全部通过。小程序上线后暂无发现检测失败情况。百度搜索的“人体油画”等等均可通过。 PS:第一次写经验分享哈,看不懂可以问我。体验一下我的小程序想问我这个小程序其它的功能点也可以喔! 技术会迭代更新,用到的技术会有时效性,看编辑时间,可能当时的技术现在不适用了
2020-10-22 - 诗词歌赋,样样精通!诗词古语小程序带你领略魅力古风丨实战
1. 小程序功能 古诗词大全 成语大全 成语接龙 诗词飞花令 诗词分享、收藏 诗词接龙 唐诗宋词起名字 百家姓 猜谜语 2. 小程序预览: [图片] 3. 部分截图 首页 [图片] 列表页 [图片] 详情页 分享页 [图片] 唐诗宋词 [图片] 成语接龙 [图片] 4. 项目结构 [代码]. ├── README.md ├── project.config.json // 项目配置文件 ├── cloudfunctions | 云环境 // 存放云函数的目录 │ ├── login // 用户登录云函数 │ │ ├── index.js │ │ └── package.json │ └── collection_get // 数据库查询云函数 │ │ ├── index.js │ │ └── package.json │ └── collection_update // 数据库更新云函数 │ ├── index.js │ └── package.json └── miniprogram ├── images // 存放小程序图片 ├── lib // 配置文件 ├── pages // 小程序各种页面 | ├── index // 首页 | └── menu // 分类页 | └── user // 用户中心 | └── search // 搜索页 | └── list // 列表页 搜索结果页 | └── detail // 详情页 | └── collection // 收藏页 | └── find // 发现页 | └── idiom-jielong // 成语接龙页 | └── poet // 作者页 | └── baijiaxing // 百家姓 | └── xiehouyu // 歇后语 | └── poet // 作者页 | └── suggest // 建议反馈 | └── ... // 其他 ├── style // 样式文件目录 ├── app.js // 小程序入口文件 ├── app.json // 全局配置 └── app.wxss // 全局样式 [代码] 5. 封装云函数操作数据库 本项目是使用的小程序云开发。云开发提供了一个 JSON 数据库,用户可以直接在云端进行数据库增删改查,但是,小程序对用户操作数据的权限进行了一定的限制(例如数据update、一次性get记录的条数限制等),所以,这里主要采用云函数来操作数据库。 查询数据、分页查询 函数根目录上右键,在右键菜单中,选择创建一个新的 Node.js 云函数,我们将该云函数命名为 collection_get。 编辑 index.js: [代码]// 云函数入口文件 const cloud = require('wx-server-sdk') cloud.init() const db = cloud.database() exports.main = async (event, context) => { /** * page: 第几页 * num: 每页几条数据 * condition: 查询条件,例如 { name: '李白' } */ const {database, page, num, condition} = event console.log(event) try { return await db.collection(database) .where(condition) .skip(num * (page - 1)) .limit(num) .get() } catch (err) { console.log(err) } } [代码] 使用 collection_get 云函数 例如,按照查询条件[代码]{tags: '唐诗三百首'}[代码]查询诗词列表,每页[代码]num = 10[代码]条数据: [代码]let {list, page, num} = this.data let that = this this.setData({ loading: true }) wx.cloud.callFunction({ name: 'collection_get', data: { database: 'gushici', page, num, condition: { tags: '唐诗三百首' } }, }).then(res => { if(!res.result.data.length) { // 没搜索到 that.setData({ loading: false, isOver: true }) } else { let res_data = res.result.data list.push(...res_data) that.setData({ list, page: page + 1, // 页面加1 loading: false }) } }) .catch(console.error) } [代码] 更新数据 注意,当我们向数据库中添加记录时,系统会自动帮我们为每条记录添加上用户的 [代码]openid[代码] 字段,但如果,数据表是自己用 json/csv 文件导入的,就不存在 [代码]openid[代码] 字段,此时,当更新这个数据表时,系统会认为你不是创建者,所以也就无法更新。 此时,就需要通过云函数更新数据库,新建云函数 collection_update, 编辑 index.js: [代码]// 更新数据 - 根据 _id 更新已打开人数 const cloud = require('wx-server-sdk') cloud.init() const db = cloud.database() const _ = db.command exports.main = async (event, context) => { const { id } = event console.log(event) try { return await db.collection('gushici').doc(id) .update({ data: { opened: _.inc(1) }, }) } catch (e) { console.error(e) } } [代码] 使用 collection_update 云函数 更新某_id数据的打开人数: [代码]let _id = e.currentTarget.dataset.id wx.cloud.callFunction({ name: 'collection_update', data: { id: _id }, }).then(res => { console.log(res.data) }) .catch(console.error) [代码] 6. 数据库模糊查询 小程序云开发可以使用正则表达式进行模糊查询。例如, 根据用户输入关键词,查询标题中存在改关键词的古诗词。 [代码]let database = 'gushici' let condition = { name: { $regex:'.*'+ inputValue, $options: 'i' } } let { list, page, num } = this.data let that = this this.setData({ loading: true }) // 模糊查询 wx.cloud.callFunction({ name: 'collection_get', data: { database, page, num, condition }, }).then(res => { if (!res.result.data.length) { // 没搜索到 that.setData({ loading: false, isOver: true }) } else { let res_data = res.result.data list.push(...res_data) that.setData({ list, loading: false }) } }) .catch(console.error) [代码] 7. 分享或转发功能 小程序中页面触发转发的方式有两种: 1.在小程序的右上角选择转发,需要定义函数 Page.onShareAppMessage,如果当前页面没有定义此事件,则点击后无效果。 2.通过给 [代码]button[代码] 组件设置属性 [代码]open-type="share"[代码],可以在用户点击按钮后触发 Page.onShareAppMessage 事件,如果当前页面没有定义此事件,则点击后无效果。 用户还可以在 Page.onShareAppMessage 事件中自定义转发后显示的标题、图片、路径: [代码]onShareAppMessage(res) { let id = wx.getStorageSync('shareId') if (res.from === 'button') { // 来自页面内转发按钮 console.log(res.target) } return { title: `跟我一起挑战最长的成语接龙吧!`, path: `pages/find/find`, imageUrl: '/images/img.jpg', } }, [代码] 注意:转发成功/失败的 callback 已经被官方废弃,所以理论上小程序是无法得知用户是否将页面分享成功的 8. 用户授权 详情请参考文章:微信小程序之授权 9. 需要注意的几个坑 查询不到数据 数据表中明明有数据,但是 collection.get 到的却为空。解决:可以在云开发控制台中打开数据库权限设置,设置权限。 更新数据失败 collection.update 函数调用成功单返回的却是0行记录被更新,因为小程序端不允许更新没有 openid 字段的数据。解决:可以通过云函数更新数据库。 background 图片 url 不能为本地图片 解决:1:将图片上传到服务器,填写服务器上的图片路径地址。2:将图片转为 base64 编码。 往云数据库中批量导入 json 数据失败 原因:请看文档:https://developers.weixin.qq.com/miniprogram/dev/wxcloud/guide/database/import.html 解决:去掉json数据 [代码]{}[代码]之间的逗号, 如果最外层为 [代码][][代码],也必须去掉, 最终形如: [代码]{ "index": "作者_1", "type": "作者", "poet": "李白", "abstract": "李白(701年-762年),字太白,号青莲居士,唐朝浪漫主义诗人,被后人誉为“诗仙”..." } { "index": "作者_2", "type": "作者", "poet": "白居易", "abstract": "白居易(772年-846年),字乐天,号香山居士..." } [代码] 源码链接 https://github.com/TencentCloudBase/Good-practice-tutorial-recommended 如果你有关于使用云开发CloudBase相关的技术故事/技术实战经验想要跟大家分享,欢迎留言联系我们哦~比心! [图片]
2019-08-09