- Canvas绘制圆角矩形
Chrome 99 之前常规操作: [代码] ctx.beginPath(); ctx.moveTo(x+topLeft, y); ctx.arcTo(x+width, y, x+width, y+height, topRight); ctx.arcTo(x+width, y+height, x, y+height, bottomRight); ctx.arcTo(x, y+height, x, y, bottomLeft); ctx.arcTo(x, y, x+width, y, topLeft); ctx.closePath(); ctx.fill(); [代码] 其中[代码]x[代码]、[代码]y[代码]表示矩形的起点坐标,[代码]width[代码]、[代码]height[代码]分别表示矩形的宽、高,[代码]topLeft[代码]、[代码]topRight[代码]、[代码]bottomRight[代码]、[代码]bottomLeft[代码]分别表示矩形左上、右上、右下和左下圆角半径大小。 下面来解读一下[代码]arcTo()[代码]的语法,以[代码]ctx.arcTo(x1,y1,x2,y2,radi)[代码]为例。 第一部分 [代码](x1,y1)[代码]坐标点,可以分成两块来理解,第一块我们可以理解为当前画笔停留的坐标点到[代码](x1,y1)[代码]的一条直线,第二块我们可以理解为即将要绘制圆角的部位,也就是所在矩形的四个顶角的位置。 第二部分 [代码](x2,y2)[代码]坐标点,也可以分成两块来理解,第一块我们可以理解为[代码](x1,y1)[代码]坐标点到[代码](x2,y2)[代码]的一条直线,这条直线与第一部分理解的直线形成一个夹角,那么圆角就绘制在这个夹角(内角)之间,并与夹角两边相切,第二块可以理解为画笔画完当前圆角最后所停留的坐标点。 第三部分 [代码]radi[代码]那就很容易理解了,也就是当前即将绘制的圆角的半径。 [图片] 然后以此类推,完成圆角矩形的绘制。 Chrome 99之后一行代码: [代码]ctx.roundRect(x, y, width, height, [topLeft, topRight, bottomRight, bottomLeft]); ctx.fill(); [代码] 最后可以做下兼容: [代码]if (ctx.roundRect) { ctx.roundRect(x, y, width, height, [topLeft, topRight, bottomRight, bottomLeft]); }else { ctx.beginPath(); ctx.moveTo(x+topLeft, y); ctx.arcTo(x+width, y, x+width, y+height, topRight); ctx.arcTo(x+width, y+height, x, y+height, bottomRight); ctx.arcTo(x, y+height, x, y, bottomLeft); ctx.arcTo(x, y, x+width, y, topLeft); ctx.closePath(); } [代码] 结尾祝君开发顺利,生活愉快!
05-08 - 省钱有道之 云开发环境共享小结
#前言 最近为了节省一点小程序的运营成本,一些没啥流量的小程序如果每个月也要19块略微有些肉疼(主要还是穷),研究了一下云环境共享,在这里简单做一下总结。 [图片] 这里有官方的小程序环境共享文档需提前了解一下,具体共享步骤按官方文档操作即可。 https://developers.weixin.qq.com/miniprogram/dev/wxcloud/guide/resource-sharing/introduce.html #注意点 共享环境有几个注意点大致如下: 1、必须是相同主体 2、开通了云开发环境的小程序可以共享给同主体的小程序、公众号,被共享方无需开通云开发环境 3、一个云开发环境最多可以共享给10个小程序/公众号 4、共享后双发均可主动解除 5、按官方文档要求,资源方需有云函数cloudbase_auth,测试时发现没有这个云函数其实也能正常运行,可能我验证的场景还不够多 6、云能力初始化的方式不同,资源方按传统的云环境初始化方式即可,也就是 wx.cloud.init({ env: env.activeEnv, traceUser: true }); 而调用方的初始化方式有所不同 const cloud = new wx.cloud.Cloud({ //资源方AppID resourceAppid, //资源方环境ID resourceEnv, }) // 跨账号调用,必须等待 init 完成 // init 过程中,资源方小程序对应环境下的 cloudbase_auth 函数会被调用,并需返回协议字段(见下)来确认允许访问、并可自定义安全规则 const initRes = await cloud.init(); 后续调用资源方的云函数就用这个cloud就行了:cloud.callFunction({...}); 7、调用方有操作到云存储文件的api也需要用6步骤中的cloud 8、云存储fileId需要用cloud.getTempFileURL转换成临时/永久链接,否则在调用方无法展示 9、一些api的云调用方式也有变化,需指明具体的appid。比如A小程序授权给了B小程序,想给B小程序推送客服消息需要写成 await cloud.openapi({appid:B小程序appid}).customerServiceMessage.send({...}); 10、获取调用方的appid/openid/unionid也有所不同 // 跨账号调用时,由此拿到来源方小程序/公众号 AppID console.log(wxContext.FROM_APPID) // 跨账号调用时,由此拿到来源方小程序/公众号的用户 OpenID console.log(wxContext.FROM_OPENID) // 跨账号调用、且满足 unionid 获取条件时,由此拿到同主体下的用户 UnionID console.log(wxContext.FROM_UNIONID) #适配 基于以上注意点,开始进行适配,由于我是一套代码部署N个小程序,然后一个云环境共享给其他小程序,希望通过配置决定哪个小程序作为资源方,哪些作为调用方 首先是云开发环境的初始化: 1、env.js 环境配置: //云开发环境 const cloudBase = { //使用共享云环境资源,资源方=false,调用方=true useShareResource: false, //资源方AppID resourceAppid: "wx9d2xxxxxxxx0088", //资源方环境ID resourceEnv: "prod-9gxqvi3qb3c257ef", //云环境ID prod: "prod-9gxqvi3qb3c257ef" } 2、api.js 操作模块 const env = require('../env.js'); let cloud; /** * 初始化云能力 * @returns {Promise} */ const wxCloudInit = async function () { const {cloudBase} = env; if (!wx.cloud) { console.error('请使用 2.2.3 或以上的基础库以使用云能力') } else if (cloudBase.useShareResource) { const {resourceAppid, resourceEnv} = cloudBase; // 声明新的 cloud 实例 cloud = new wx.cloud.Cloud({ //资源方AppID resourceAppid, //资源方环境ID resourceEnv, }) // 跨账号调用,必须等待 init 完成 // init 过程中,资源方小程序对应环境下的 cloudbase_auth 函数会被调用,并需返回协议字段(见下)来确认允许访问、并可自定义安全规则 const initRes = await cloud.init(); console.log("初始化云能力完毕:", initRes, "资源方appid:", resourceAppid, "资源方环境ID:", resourceEnv); } else { wx.cloud.init({ env: env.activeEnv, traceUser: true }); console.log("初始化云能力完毕,当前环境:", env.activeEnv); cloud = wx.cloud; } this.cloud = cloud; } /** * 云函数调用 * @param name * @param data * @param success * @param fail * @param complete */ const callCloudFunction = function (name, data, success, fail, complete) { //执行云函数 cloud.callFunction({ // 云函数名称 name: name, // 传给云函数的参数 data: Object.assign({}, data, {env: env.activeEnv}) }).then(res => { typeof success == 'function' && success(res); }).catch(res => { typeof fail == 'function' && fail(res); }).then(res => { typeof complete == 'function' && complete(res); }); }; 3、在app.js中初始化云环境,后续有用到wx.cloud的都需要改成api.cloud const api = require('utils/api.js'); App({ onLaunch: async function (options) { await api.wxCloudInit(); } }); 其次是资源方的获取用户信息调整 每次都要判断wxContext.FROM_OPENID是否为空,不为空则是调用方的用户信息,为空则是资源方的用户信息,略微繁琐,干脆封装了一个npm包wx-server-inherit-sdk,改造了一下getWxContext函数,源码如下,引入这个包后也就可以不用引入官方的wx-server-sdk const cloud = require('wx-server-sdk'); // 保存原始getWXContext方法到另一个变量 const originalGetWXContext = cloud.getWXContext; cloud.getWXContext = function () { //调用原始getWXContext方法 const wxContext = originalGetWXContext.call(this); const {FROM_APPID, FROM_OPENID} = wxContext; //云开发环境共享时获取到的APPID会替换成源方APPID if (FROM_APPID) { Object.assign(wxContext, {APPID: FROM_APPID}); } //云开发环境共享时获取到的OPENID会替换成源方OPENID if (FROM_OPENID) { Object.assign(wxContext, {OPENID: FROM_OPENID}); } return wxContext; } module.exports = cloud; 到此也就大功告成。为了省钱也是够折腾的[哭笑]
2023-08-28 - 【小程序技巧】如何让长文本超过限定行数自动折叠,并且可以展开收起
这是去年在校做项目遇到的一个需求,文章沉在草稿箱里一直没写完,主要分享一下如何实现长文本的折叠展开。 长文本超过限定行数自动折叠,点击长文本或者按钮,实现展开收起效果。这类效果其实在平时的app中或者网站中很常见,举几个栗子: 微信朋友圈: [图片] 新浪微博: [图片] 分析需求 1、文本超长省略,主要是通过 line-clamp 实现: [代码].text-clamp2 { overflow: hidden; display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; } [代码] 文本效果: [图片] 2、如何判断文本是否超出两行,显示「全文」「收起」按钮呢? [图片] 通过上图我们可以发现,当文本区域省略时,它的高度会相对变小,那么我们只需要获取到不省略和省略时的文本区域高度,进行比较就能知道是否超出了两行。 [图片] 思路解决了,怀着喜悦的心情翻看了一下文档:咦?为什么小程序没有像 js 那样操作 dom 节点的接口?那还怎么获取元素的尺寸高度!好在功夫不负有心人,终于在文档找到类 DOM 操作的 API「SelectQuery」。 实现需求 3、什么是 SelectQuery?如何去使用它? 从文档(传送门)描述来看 SelectQuery 是一个查询节点信息的对象,它可以选择匹配选择器的所有节点以及显示区域内的节点信息。既然它可以类似 jQuery 那样去匹配选择器,那么我们可以获取到需要的高度信息了。 [代码]// wxml <view class="contentInner1 text-clamp2">小程序是一种新的开放能力,开发者可以快速地开发一个小程序。小程序可以在微信内被便捷地获取和传播,同时具有出色的使用体验。</view> <view class="contentInner2">小程序是一种新的开放能力,开发者可以快速地开发一个小程序。小程序可以在微信内被便捷地获取和传播,同时具有出色的使用体验。</view> [代码] [代码]// js wx.createSelectorQuery().selectAll(".contentInner1, .contentInner2").boundingClientRect(res => { console.log(res) }).exec() [代码] 查询结果(文本区域省略时高度为 52px、不省略时为 104px,只要 res[0].height < res[1].height,此时就应该显示展开收起按钮 ) [图片] 4、逻辑设计上的优化 由于论坛帖子不只一个,我们得匹配对应的两个长文本节点,如果都给一个唯一的选择器,那么在页面中一次性查询这么多节点,很明显这不是最优的。 实际上我们可以将这封装成一个自定义组件,可供每个页面循环复用,在组件内我们只需要关注 单个 长文本的节点信息,不需要一次性获取当前页面的所有长文本节点,更重要的是:在组件内每个长文本的展开与收起状态都是独立的,也省去了在页面内定义字段去标识每个帖子的展开状态。 5、实现效果 [图片] [图片] 6、参数说明 属性 类型 默认值 说明 content String “示例文本” 长文本内容 maxline Number 1 最多展示行数[只允许 1-5 的正整数] position String “left” 展开收起按钮位置[可选值为 left right] foldable Boolean true 点击长文本是否展开收起 最后附上代码片段,有疑问欢迎在下方留言或者发社区私信(三连暗示) [图片]
2021-12-30