- 小程序官方地区选择器数据
用途: 后端需要完全匹配小程序的地址做一些操作时,可用到。产品有时需要自定义交互时,可重写地区选择器。来源: 数据来源于微信开发者工具Stable 1.03.2010240。 地址链接: https://static.bazhuay.com/region.zip 地址树形结构: https://static.yipintemian.com/json/region.json(json格式,解决乱码) https://img.bazhuay.com/region.js(可能乱码) 最新的开发工具最新稳定版(1.05.2112141):https://static.yipintemian.com/json/region2.json 温馨提示: 公司在自行开发、运营电商小程序,欢迎交流共同进步。不定期分享文章,欢迎关注哈。
2022-01-20 - 使用对象存储,优雅的管理前端切图
背景: 众所周知,小程序主包&分包大小限制(2M),目前很多开发者都为之抓耳挠腮。 然而目前在现在的开发过程中,一张前端切图动辄十多 K, 几十 K,甚至有些 gif 图片上百 K 都是很常见的。 我们从包大小的角度思考,如果空间都拿来存储图片资源,属实是非常浪费的。 相信每个小程序开发者都对此有过思考,也看到有的项目已经用网络图片来解决此问题。但如果只是拷贝 url 拿过来用,使用起来会比较繁琐,维护起来比较麻烦,怎么更优雅的用,更优雅的维护是我们所追求的。 这里利用腾讯云提供的 对象存储服务(COS)& cos-node-js-sdk,讲一下我对于前端切图素材的解决方案。 思路: 在对象存储上,其实是跟电脑的硬盘一样,是有目录结构的。 如图我们以 ui-material 文件夹为根目录,所有的图片资源都放在这个目录里。 [图片] [图片] 而文件目录本身就是树形结构的,放一张图可能更容易理解 [图片] 所以就有了思考,想能不能把所有图片以 JSON 的方式存储 ? key=>文件名,value=>图片地址 比如我想访问 HP.jpg 这张图片,我希望是这样的访问 Images.HP 而访问 active/IBM.jpg 我希望是 Images.active.IBM 也就是说把文件的 path 以 json 的方式输出就是不是就 ok 了 ~ 下面使用 node 实现 ~ 安装依赖: npm install cos-nodejs-sdk-v5 path-parse ramda lodash --save -dev 1.连接 cos : // index.js const COS = require("cos-nodejs-sdk-v5"); const PathParse = require("path-parse"); const Ramda = require("ramda"); const Lodash = require("lodash"); const fs = require("fs"); /** 这里是一些对象存储的配置信息,可以在腾讯云控制台中查看 */ const COS_SECRETID = "xxxx填你自己的"; const COS_SECRETKEY = "xxxx填你自己的"; const COS_BUCKET = "log-1255751956"; const COS_REGION = "ap-hongkong"; const COS_ENCODING_TYPE = "url"; /** 访问地址 */ const COS_ACCESS_DOMAIN = "https://log-1255751956.cos.ap-hongkong.myqcloud.com"; /** UI素材资源目录 */ const UI_MATERIAL_PATH = "ui-material"; /** 获取 cos 实例 */ const cos = new COS({ SecretId: COS_SECRETID, SecretKey: COS_SECRETKEY, }); 2.获取 ui-material 文件夹下的文件列表,通过官方提供的 cos-node-js-sdk /** 获取 Bucket 信息 * 我们只获取 ui-material 这个文件夹下的文件 * 所以后面后面统一上传素材文件到这个目录下*/ cos.getBucket( { /**指定存储桶 */ Bucket: COS_BUCKET, /**指定地区 */ Region: COS_REGION, /**指定文件夹 */ Prefix: `${UI_MATERIAL_PATH}/`, /**指定编码方式 */ EncodingType: COS_ENCODING_TYPE, }, (err, data) => { if (!err) { const { Contents } = data; console.log('Contents==>',Contents); } else { console.log("getBuket err", err); } } ); // 输出结果: [ { Key: 'ui-material/', LastModified: '2021-09-09T06:32:28.000Z', ETag: '"d41d8cd98f00b204e9800998ecf8427e"', Size: '0', Owner: { ID: '1255751956', DisplayName: '1255751956' }, StorageClass: 'STANDARD' }, { Key: 'ui-material/HP.png', LastModified: '2021-09-09T15:43:49.000Z', ETag: '"4363f2b9df8a0ec2de805ae2938571fb"', Size: '10990', Owner: { ID: '1255751956', DisplayName: '1255751956' }, StorageClass: 'STANDARD' }, { Key: 'ui-material/active/', LastModified: '2021-09-09T09:24:18.000Z', ETag: '"d41d8cd98f00b204e9800998ecf8427e"', Size: '0', Owner: { ID: '1255751956', DisplayName: '1255751956' }, StorageClass: 'STANDARD' }, { Key: 'ui-material/active/IBM.png', LastModified: '2021-09-09T15:44:31.000Z', ETag: '"b5974b615efa779c702a15316490f464"', Size: '3202', Owner: { ID: '1255751956', DisplayName: '1255751956' }, StorageClass: 'STANDARD' }, { Key: 'ui-material/active/moduleA/', LastModified: '2021-09-09T15:46:04.000Z', ETag: '"d41d8cd98f00b204e9800998ecf8427e"', Size: '0', Owner: { ID: '1255751956', DisplayName: '1255751956' }, StorageClass: 'STANDARD' }, { Key: 'ui-material/active/moduleA/intel.png', LastModified: '2021-09-09T15:46:12.000Z', ETag: '"edec5fe92c7d52cb69a40890eaa6a113"', Size: '5316', Owner: { ID: '1255751956', DisplayName: '1255751956' }, StorageClass: 'STANDARD' }, { Key: 'ui-material/order.png', LastModified: '2021-09-09T07:18:30.000Z', ETag: '"dd5cee76f07bc77f1e3b8b5e65e130da"', Size: '3106', Owner: { ID: '1255751956', DisplayName: '1255751956' }, StorageClass: 'STANDARD' } ] 我们可以清晰的看见 List 中的 Key 其实就是我们所需要的文件路径,我们只需要去解析这个 Key 就 ok 了 3.获取图片路径: const Key = 'ui-material/active/moduleA/intel.png'; const url = `${COS_ACCESS_DOMAIN}/${Key}` // 结果=====> https://log-1255751956.cos.ap-hongkong.myqcloud.com/ui-material/active/moduleA/intel.png 4.获取图片对应的 objcet props: const parsePath = PathParse(Key); /** 解析 path 信息 **/ const props = [ ...parsePath.dir.split('/') ,parsePath.name ]; props.shift(); /** 删除数组第一个元素,因为 ui-material 是根目录 ===> [ 'active', 'moduleA', 'intel' ] **/ 5.利用 loadsh.set 为对象属性赋值 let obj = {}; Lodash.set(obj, props, url); console.log(obj) /** =====> { active: { moduleA: { intel: 'https://log-1255751956.cos.ap-hongkong.myqcloud.com/ui-material/active/moduleA/intel.png' } } } **/ 好了到这里我们以经可以顺利的生成文件的 JSON 了,没错它是一个树形结构。 我们只需要把这个 JSON 给写入 js 文件,后续直接从这个文件拿数据就行了。 6.写入数据到 js 文件 const outputStr = `export default ${Ramda.toString(json)};`; fs.writeFileSync("./assets/data.js", outputStr); 7.运行脚本 node index.js 会得到这样一个 js 文件 export default { HP: "https://log-1255751956.cos.ap-hongkong.myqcloud.com/ui-material/HP.png", active: { IBM: "https://log-1255751956.cos.ap-hongkong.myqcloud.com/ui-material/active/IBM.png", moduleA: { intel: "https://log-1255751956.cos.ap-hongkong.myqcloud.com/ui-material/active/moduleA/intel.png", }, }, order: "https://log-1255751956.cos.ap-hongkong.myqcloud.com/ui-material/order.png", }; 8.在小程序里使用 [图片] // assets/index.js import Images from './data'; export { Images } 在 Page 里使用 import { Images } from '../../assets/index'; Page({ data: { // 这种方式引用有没有很爽 intelIcon: Images.active.moduleA.intel } }); 可以结合 npm script 或者 gulp 每次在图片更新时,重新执行脚本,更新 data.js 9.完整代码 const COS = require("cos-nodejs-sdk-v5"); const PathParse = require("path-parse"); const Ramda = require("ramda"); const Lodash = require("lodash"); const fs = require("fs"); /** 这里是一些对象存储的配置信息,可以在腾讯云控制台中查看到 */ const COS_SECRETID = "xxxx填你自己的"; const COS_SECRETKEY = "xxxx填你自己的"; const COS_BUCKET = "log-1255751956"; const COS_REGION = "ap-hongkong"; const COS_ENCODING_TYPE = "url"; /** 访问地址 */ const COS_ACCESS_DOMAIN = "https://log-1255751956.cos.ap-hongkong.myqcloud.com"; /** UI素材资源目录 */ const UI_MATERIAL_PATH = "ui-material"; /** 获取 cos 实例 */ const cos = new COS({ SecretId: COS_SECRETID, SecretKey: COS_SECRETKEY, }); /** 获取 Bucket 信息 * 我们只获取 ui-material 这个文件夹下的文件 * 所以后面后面统一上传素材文件到这个目录下*/ cos.getBucket( { /**指定存储桶 */ Bucket: COS_BUCKET, /**指定地区 */ Region: COS_REGION, /**指定文件夹 */ Prefix: `${UI_MATERIAL_PATH}/`, /**指定编码方式 */ EncodingType: COS_ENCODING_TYPE, }, (err, data) => { if (!err) { const { Contents } = data; const effectiveFile = filterEffectiveFile(Contents); const jsonObj = genJson(effectiveFile); outJsFile(jsonObj); } else { console.log("getBuket err", err); } } ); /** 输出 js 文件*/ function outJsFile(json) { try { const outputStr = `export default ${Ramda.toString(json)};`; fs.writeFileSync("./assets/data.js", outputStr); } catch (e) { console.log("writeFile file fail", e); } } /** 过滤出有效的 file */ function filterEffectiveFile(Contents) { /**过滤出 size > 0 的file */ return Contents.filter((_) => _.Size > 0).map((_) => { const url = `${COS_ACCESS_DOMAIN}/${_.Key}`; const parsePath = PathParse(_.Key); const props = [...parsePath.dir.split("/"), parsePath.name]; props.shift(); return { url, props, }; }); } /** 生成 json 对象 */ function genJson(fileList) { let objects = {}; fileList.forEach((file) => { /** 核心方法 可以看 loadsh 文档中的 set 方法 */ Lodash.set(objects, file.props, file.url); }); return objects; }
2021-09-10 - 小程序纯前端将数据导出为excel表格
网上有许多文章是讲述小程序将数据导出为excel表格的,但大多需要经过请求服务端,再加上云存储。那一套逻辑之前做后端的时候就玩过了。很多时候,我们浏览页面时数据已经从服务端获取到本地了,直接将之导出即可,再走服务端,实为多此一举。为了减轻服务端压力,于是便有了这篇文章。本文章介绍如何在小程序使用纯前端技术将以获取到的数据导出为excel表格。文末有代码片段 xlsx插件文档 sheetjs插件文档 [代码]const XLSX = require('../utils/excel.js') Page({ data: { }, onLoad() { }, exportData() { // 数据源 const data = [{ code: 1, name: 'A', }, { code: 2, name: 'B', }, { code: 3, name: 'C', }, { code: 4, name: 'D', }] // 构建一个表的数据 let sheet = [] let title = ['序号', '姓名'] sheet.push(title) data.forEach(item => { let rowcontent = [] rowcontent.push(item.code) rowcontent.push(item.name) sheet.push(rowcontent) }) // XLSX插件使用 var ws = XLSX.utils.aoa_to_sheet(sheet); var wb = XLSX.utils.book_new(); XLSX.utils.book_append_sheet(wb, ws, "用户表"); var fileData = XLSX.write(wb, { bookType: "xlsx", type: 'base64' }); let filePath = `${wx.env.USER_DATA_PATH}/用户表.xlsx` // 写文件 const fs = wx.getFileSystemManager() fs.writeFile({ filePath: filePath, data: fileData, encoding: 'base64', bookSST: true, success(res) { console.log(res) const sysInfo = wx.getSystemInfoSync() // 导出 if (sysInfo.platform.toLowerCase().indexOf('windows') >= 0) { // 电脑PC端导出 wx.saveFileToDisk({ filePath: filePath, success(res) { console.log(res) }, fail(res) { console.error(res) util.tips("导出失败") } }) } else { // 手机端导出 // 打开文档 wx.openDocument({ filePath: filePath, showMenu: true, success: function (res) { console.log('打开文档成功') }, fail: console.error }) } }, fail(res) { console.error(res) if (res.errMsg.indexOf('locked')) { wx.showModal({ title: '提示', content: '文档已打开,请先关闭', }) } } }) } }) [代码] 导出效果如下,如需高级设置请参照xlsx插件文档 sheetjs插件文档 [图片] 代码片段 -完-
2023-08-11 - 使用腾讯位置服务API完成车辆轨迹回放(模拟真实的速度和方向)
产品需求: 根据能够回放出来车辆的运行轨迹路线、运行方向和速度。 需求分析: 1、首先因为是Web网页端的功能,所以需要用到的是地图模块的API,可以选择百度地图或者腾讯地图。 2、由于需要位置信息,所以地图需要支持点到点的路线绘制功能。 3、关键点:需要一个小车,并且小车是可以根据不同的方向而改变车头朝向。 因为前两点功能百度地图API可以满足,但是第三点,腾讯地图LBS,更新了新版本的接口,图标可以自动根据方向改变朝向。所以选择腾讯地址,减少开放量,另外就是直接API支持,减少了很多的BUG。 开发前的准备: 1、在腾讯位置服务中注册为开发者: [图片] 2、在控制台配置Key 配置完成之后,就可以通过开发文档-web前端-JavaScript-API来获取腾讯位置服务的LBS组件 [图片] 开始开发: 第一步:画页面,初始化地图: 把key中的XXXXXXXXXXX更换为我们刚才在腾讯地图LBS后台获取的key。 [代码]<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>marker轨迹回放-全局模式</title> </head> <script charset="utf-8" src="https://map.qq.com/api/gljs?v=1.exp&key=XXXXXXXXXXX"></script> <style type="text/css"> html, body { height: 100%; margin: 0px; padding: 0px; } #container { width: 100%; height: 100%; } </style> <body> <div id="container"></div> <script type="text/javascript"> var center = new TMap.LatLng(30.465512, 114.402740); //初始化地图 var map = new TMap.Map("container", { zoom: 15, center: center }); </script> </body> </html> [代码] 效果如图所示: [图片] 第二步:画路线,并根据路线模拟运行 这里需要注意的是,如果路线比较复杂,尽可能的使用分钟级,甚至秒级的坐标,这样绘制的轨迹也会更精准。速度的展示,需要后台在记录坐标的时候计算好,并实时反馈。 [代码] <script type="text/javascript"> var center = new TMap.LatLng(39.984104, 116.307503); //初始化地图 var map = new TMap.Map("container", { zoom: 15, center: center }); var path = [ new TMap.LatLng(39.98481500648338, 116.30571126937866), new TMap.LatLng(39.982266575222155, 116.30596876144409), new TMap.LatLng(39.982348784165886, 116.3111400604248), new TMap.LatLng(39.978813710266024, 116.3111400604248), new TMap.LatLng(39.978813710266024, 116.31699800491333) ]; var polylineLayer = new TMap.MultiPolyline({ map, // 绘制到目标地图 // 折线样式定义 styles: { 'style_blue': new TMap.PolylineStyle({ 'color': '#3777FF', //线填充色 'width': 4, //折线宽度 'borderWidth': 2, //边线宽度 'borderColor': '#FFF', //边线颜色 'lineCap': 'round' //线端头方式 }) }, geometries: [{ styleId: 'style_blue', paths: path }], }); var marker = new TMap.MultiMarker({ map, styles: { 'car-down': new TMap.MarkerStyle({ 'width': 40, 'height': 40, 'anchor': { x: 20, y: 20, }, 'faceTo': 'map', 'rotate': 180, 'src': 'https://mapapi.qq.com/web/lbs/javascriptGL/demo/img/car.png', }), "start": new TMap.MarkerStyle({ "width": 25, "height": 35, "anchor": { x: 16, y: 32 }, "src": 'https://mapapi.qq.com/web/lbs/javascriptGL/demo/img/start.png' }), "end": new TMap.MarkerStyle({ "width": 25, "height": 35, "anchor": { x: 16, y: 32 }, "src": 'https://mapapi.qq.com/web/lbs/javascriptGL/demo/img/end.png' }) }, geometries: [{ id: 'car', styleId: 'car-down', position: new TMap.LatLng(39.98481500648338, 116.30571126937866), },{ "id": 'start', "styleId": 'start', "position": new TMap.LatLng(39.98481500648338, 116.30571126937866) }, { "id": 'end', "styleId": 'end', "position": new TMap.LatLng(39.978813710266024, 116.31699800491333) }] }); marker.moveAlong({ 'car': { path, speed: 250 } }, { autoRotation:true }) </script> [代码] [图片]另外需要后台配合的是: 1、把汽车的轨迹坐标,按照秒级/分钟级记录,同时记录下轨迹记录的时间。 2、把坐标绘制成轨迹,而不是仅仅设置起点和终点。 3、轨迹与轨迹之间用地图计算出来距离,然后除以时间计算出来速度。前端地图实时更新 marker.moveAlong中的car的速度。来达到轨迹回放跟实际车辆运行速度一致的目的。 总结: 使用腾讯位置服务API,是目前最简单的可以花轨迹+Mark图标跟随轨迹移动+Mark图标可以自适应转向的实现。
2021-08-19 - 【交互方案】针对不小心触发返回按钮的交互
17年 因为业务上有该类需求,所以提过一个问题。 想实现监听左上角按钮来做其他操作,比如跳转其他页面。 https://developers.weixin.qq.com/community/develop/doc/92f7cdbacdf724cba640955423a8444f 此交互优化只应对表单等内容提交回填的处理 应用场景: 某个表单提交页,页面挺多内容,用户填写完后,不小心触发了左上角返回,或者不小心点了物理按键,那么用户填写的内容就丢失了,那么最开始大家的想法都是,点击左上角返回按钮或者物理按键返回能触发监听我们再弹窗提醒用户是否退出当前页面。这个交互应该是再正常不过的了,在各种app都有见过这种交互。 网页里: h5里都是通过监听popstate,以及设置pushstate实现。 小程序里: 然而小程序的翻遍官方文档,没找到该方法,发帖询问后也是得到官方童鞋回复,不会提供该方法。 官方童鞋给的原因是:会有某些开发者,阻止用户退出某些页面,以达到一些xxx目的,所以官方为了防止开发者滥用,并不打算开放该功能。 翻了社区大家实现方式,有大佬给了一个方案: https://developers.weixin.qq.com/community/develop/article/doc/000844b537c230b04b999a54f56013 该监听方法的缺点: [图片] 最后确实没发现有什么好的监听方案了,那既然代码无法实现,那么我们可以优化用户体验来达到该效果。 实现操作方案如下: [代码]// app.js下跟onLaunch同级新增个globalData字段。 globalData: { formData: {} // 这里需要默认填写该字段,不然其他地方使用了会报错。 } // 首先用户填写任意字段都存储一个对象到globalData下。 <input type="text" placeholder="请输入用户昵称" bindinput="handleUserName" /> handleUserName(e) { getApp().globalData.formData.userName = e.detail.value } [代码] 这样将用户填写的内容都存到globalData下,而我们最初的交互是,存储后用户点击返回下次进来自动回填。 [代码]onShow() { this.setData({ userName: getApp().globalData.formData.userName || '' }) } [代码] 而最终的交互是这样操作: 如果用户填写完一整页内容,而内容我们都存到了globalData下,用户不小心返回了上一页,那么我们在用户重新进入该页面时,判断globalData的formData下是否存在内容,如果存在,弹窗提醒用户是否回填上次填写的内容,如果用户确认回填那么我们给用户自动回填上次填写的信息,如果用户取消回填,那么我们将globalData下的formData设置为空对象即可。 如此 我们即从交互上规避了不小心点击返回导致需要重新输入的问题,并且交互体验得到极大提升。。
2020-06-04 - 小程序瀑布流组件实例
微信小程序瀑布流组件 实现效果图 [图片] 源代码链接:https://git.weixin.qq.com/xieyefeng888/waterfall 用法 下载组件源码到项目目录下 微信开发者工具导入项目即可查看效果 主要使用到的API有 this.getRelationNodes() ,案例结合文档更容易理解 [图片] [图片] [图片] [图片] 注意:必须在两个组件定义中都加入relations定义,否则不会生效。 知识扩展 this.selectComponent()可以获取到子组件的里面的数据和方法,还可以获取到properties里面的值 [图片] [图片] [图片] [图片] [图片] [图片]
2020-11-25 - Painter 一款轻量级的小程序海报生成组件
生成海报相信大家有的人都做过,但是canvas绘图的坑太多。大家可以试试这个组件。然后附上楼下大哥做的可视化拖拽生成painter代码的工具:链接地址https://developers.weixin.qq.com/community/develop/article/doc/000e222d9bcc305c5739c718d56813
2019-09-27