- 解锁小程序中使用SVG新姿势
SVG 的优势 清晰度: 可以进行放大,而不失真 更小的文件体积 可扩展性,可以动态颜色 动效 可以添加动效 在小程序中使用 目前小程序 的image标签已经支持了 svg 的显示 [代码] <image src="./xx.svg"/> [代码] 如何动态的改变 svg 属性呢? 大体思路:把svg转成 base64 然后通过 image标签 src设置图片,再动态赋值svg颜色 把svg转成base64 如下一个svg 代码文件 [代码]<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%" viewBox="24 24 48 48"><animateTransform attributeName="transform" type="rotate" repeatCount="indefinite" from="0" to="360" dur="1400ms"></animateTransform><circle cx="48" cy="48" r="20" fill="none" stroke="#eeeeee" stroke-width="2" transform="translate\(0,0\)"><animate attributeName="stroke-dasharray" values="1px, 200px;100px, 200px;100px, 200px" dur="1400ms" repeatCount="indefinite"></animate><animate attributeName="stroke-dashoffset" values="0px;-15px;-125px" dur="1400ms" repeatCount="indefinite"></animate></circle></svg> [代码] 转成base64,其实就是 对这个svg进行 encodeURIComponent 得到 如下代码 [代码]%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%22100%25%22%20height%3D%22100%25%22%20viewBox%3D%2224%2024%2048%2048%22%3E%3CanimateTransform%20attributeName%3D%22transform%22%20type%3D%22rotate%22%20repeatCount%3D%22indefinite%22%20from%3D%220%22%20to%3D%22360%22%20dur%3D%221400ms%22%3E%3C%2FanimateTransform%3E%3Ccircle%20cx%3D%2248%22%20cy%3D%2248%22%20r%3D%2220%22%20fill%3D%22none%22%20stroke%3D%22%23eeeeee%22%20stroke-width%3D%222%22%20transform%3D%22translate%5C(0%2C0%5C)%22%3E%3Canimate%20attributeName%3D%22stroke-dasharray%22%20values%3D%221px%2C%20200px%3B100px%2C%20200px%3B100px%2C%20200px%22%20dur%3D%221400ms%22%20repeatCount%3D%22indefinite%22%3E%3C%2Fanimate%3E%3Canimate%20attributeName%3D%22stroke-dashoffset%22%20values%3D%220px%3B-15px%3B-125px%22%20dur%3D%221400ms%22%20repeatCount%3D%22indefinite%22%3E%3C%2Fanimate%3E%3C%2Fcircle%3E%3C%2Fsvg%3E [代码] 拼接base64 [代码] data:image/svg+xml;charset=utf-8,encodeURIComponent后的代码 [代码] 在对应svg属性上动态设置颜色,比如这里用到的是填充颜色 在js文件 data中定义 color 状态 在wxml中动态渲染 [代码] <image src="data:image/svg+xml;charset=utf-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%22100%25%22%20height%3D%22100%25%22%20viewBox%3D%2224%2024%2048%2048%22%3E%3CanimateTransform%20attributeName%3D%22transform%22%20type%3D%22rotate%22%20repeatCount%3D%22indefinite%22%20from%3D%220%22%20to%3D%22360%22%20dur%3D%221400ms%22%3E%3C%2FanimateTransform%3E%3Ccircle%20cx%3D%2248%22%20cy%3D%2248%22%20r%3D%2220%22%20fill%3D%22none%22%20stroke%3D%22%23{{color}}%22%20stroke-width%3D%222%22%20transform%3D%22translate%5C(0%2C0%5C)%22%3E%3Canimate%20attributeName%3D%22stroke-dasharray%22%20values%3D%221px%2C%20200px%3B100px%2C%20200px%3B100px%2C%20200px%22%20dur%3D%221400ms%22%20repeatCount%3D%22indefinite%22%3E%3C%2Fanimate%3E%3Canimate%20attributeName%3D%22stroke-dashoffset%22%20values%3D%220px%3B-15px%3B-125px%22%20dur%3D%221400ms%22%20repeatCount%3D%22indefinite%22%3E%3C%2Fanimate%3E%3C%2Fcircle%3E%3C%2Fsvg%3E" /> [代码] [代码]注意:这里的颜色 由于是已经被编码了,所以# 已经被转义了 %23, 直接写颜色数字即可[代码] 当然你也可以 去掉%23 自己实现一个内部方法 [代码] if (color && color.startsWith('#')) { return `%23${color.slice(1)}`; } [代码] 这样其实就实现了 svg的动态渲染,可是这种写法,写在wxml中 不是特别的优雅,那么如何重构下让我们的代码看起来更优雅呢? 把 svg 单独存放 支持动态返回 动态赋值 image src 属性 svg 动态函数 loading.svg.js 文件 [代码]export const loadingSvg = (color='#ddd') =>{ const svgXml = `<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%" viewBox="24 24 48 48"><animateTransform attributeName="transform" type="rotate" repeatCount="indefinite" from="0" to="360" dur="1400ms"></animateTransform><circle cx="48" cy="48" r="20" fill="none" stroke="${color}" stroke-width="2" transform="translate\(0,0\)"><animate attributeName="stroke-dasharray" values="1px, 200px;100px, 200px;100px, 200px" dur="1400ms" repeatCount="indefinite"></animate><animate attributeName="stroke-dashoffset" values="0px;-15px;-125px" dur="1400ms" repeatCount="indefinite"></animate></circle></svg>` return `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svgXml)}` } [代码] 逻辑层引入,setData [代码] onLoad(){ const { loadingSvg } = require('./loading.svg.js') const svgImg = loadingSvg('#eee') this.setData({svgImg}) }, [代码] 渲染层使用 [代码] <image src="{{svgImg}}"/> [代码] github 使用案例 demoFormpSvg
2022-04-30 - 微信小程序 scroll-view 橡皮筋回弹效果解决方案
花了 3 个小时,才找到 [代码]scroll-view[代码] 禁用回弹效果失效的问题 一般情况下,我们只需要根据官方案例设置就没有问题,如下: [代码]<scroll-view class="scroll-view" scroll-y enhanced="{{ true }}" bounces="{{ false }}"> <view wx:for="{{arr}}" wx:key="*this">{{item}}</view> </scroll-view> page({ data: { arr: [1,2,3,4] } }) [代码] 但是,正经人谁是一般人 我们使用 [代码]scroll-view[代码] 大部分都是动态数据,都是由接口动态返回,这时候如果像下面这样写,禁用回弹效果就会失效 [代码]<scroll-view class="scroll-view" scroll-y enhanced="{{ true }}" bounces="{{ false }}" > <view wx:for="{{arr}}" wx:key="*this">{{item}}</view> </scroll-view> [代码] [代码]page({ data: { arr: [] } }, onLoad(){ setTimeout(()=>{ this.setData({ arr: [1,2,3,4,5] }) },3000) }) [代码] 猜测这个可能是 scroll-view 初始化的原因,判断 [代码]arr[代码] 里面有值,来设置初始化,而当数据动态回填的时候没有做监听来重新初始化, 所以需要改成下面这两种写法即可 第一种:arr 有值并动态返回重新覆盖 [代码]<scroll-view class="scroll-view" scroll-y enhanced="{{ true }}" bounces="{{ false }}" > <view wx:for="{{arr}}" wx:key="*this">{{item}}</view> </scroll-view> [代码] [代码]page({ data: { arr: [1,2,3] } }, onLoad(){ setTimeout(()=>{ this.setData({ arr: [1,2,3,4,5] }) },3000) }) [代码] 第二种:arr 没有值 添加 if 判断显示隐藏 第一种判断:[代码]wx:if="arr.length"[代码] [代码]<scroll-view class="scroll-view" scroll-y enhanced="{{ true }}" bounces="{{ false }}" wx:if="arr.length" > <view wx:for="{{arr}}" wx:key="*this">{{item}}</view> </scroll-view> [代码] [代码]page({ data: { arr: [] } }, onLoad(){ setTimeout(()=>{ this.setData({ arr: [1,2,3,4,5] }) },3000) }) [代码] 第二种判断:[代码]wx:if="isShowScrollView"[代码] [代码]<scroll-view class="scroll-view" scroll-y enhanced="{{ true }}" bounces="{{ false }}" wx:if="isShowScrollView" > <view wx:for="{{arr}}" wx:key="*this">{{item}}</view> </scroll-view> [代码] [代码]page({ data: { arr: [], isShowScrollView: false } }, onLoad(){ setTimeout(()=>{ this.setData({ arr: [1,2,3,4,5], isShowScrollView: false }) /** * 也可以这样防止万一 * setTimeout(()=>{ * this.setData({ * isShowScrollView: true * }) * },50) */ },3000) }) [代码]
2024-01-18 - textarea使用出现的问题说明和解决办法
textarea是官方的原生组件,该组件的使用存在着非常多的问题,详细说明请看下图。 [图片] 就我目前使用该组件一共出现过三个问题。 第一,textarea嵌套在scroll-view中进行滚动时会出现非常严重的问题,首先是滚动的效果非常差,需要小程序很长的时间才会有反应。该问题在低端机或者老旧机型上展现尤其明显,推测是因为小程序渲染负担或计算量过大导致的;然后这个问题就会导致另外一个问题:小程序渲染负担或计算量过大导致小程序使用的手机资源超过了限制,这个时候就会触发小程序的回收机制。这个时候具体的表现就是小程序“闪退”。针对这个问题,本人通过一系列的测试最后确定了textarea与scroll-view这两个组件产生了冲突导致的,所以本人建议textarea与scroll-view最好不要嵌套使用。同时推荐wxml的自动滚动功能,该功能不需要在wxss或wxml特别说明某个组件或者页面滚动,只要所有组件加起来的高度超过页面高度即可触发滚动。 第二,在使用textarea的页面,各组件的总高度超过页面高度100%的时候,最好不要在wxss写“height: 100%;overflow: hidden;”。[图片] 这是因为textarea的展示层级是所有组件中最高的,同时如果组件总高度超过页面高度时可触发滚动,这时就会出现文字漂移的现象了。目前我查看了网上关于这个问题的很多解决办法,基本原理大致都是view和textarea的相互切换,或者在使用textarea时跳转到其他页面进行输入。而前者我尝试了两位大佬的解决方案,就我个人使用而言我觉得这种思路下的解决方案使用体验度非常差,编辑富文本是需要双击view切换textarea,而textarea切换view的速度也不稳定,有可能在切换的途中页面滚动了,这个时候还是会出现文字漂移的问题。至于后者也不用试了,一看也是体验度比较差的解决方案。本人对于该问题也没有完美的解决方案,不过确实有方法可以避开文字漂移问题。具体方式就是一开始说的,不要在wxss的pages中加入height: 100%;overflow: hidden; 第三,textarea的展示层级是所有组件中最高的,这个现象还有引发另外一个问题,那就是页面中同时存在textarea和弹窗。那么出现弹窗时textarea的内容就会透过弹窗展示出来。而这个问题只需要在出现弹窗时使用hidden隐藏textarea组件即可。
2020-05-28 - textarea焦点一闪,键盘弹不出来?
[图片]
2023-09-28 - 🎆我们开源啦 | 基于Skyline开发的组件库🚀
我们开源啦,希望可以给大家的开发之旅带来一些灵感。我后溪的小程序也都会基于这个组件库开发,并且会保持组件库的更新与维护。 我是第一次进行开源,肯定会有错漏,欢迎大家指正,我会以最快的时间响应修改。 Skyline UI 组件库 前言 Skyline 是微信小程序推出的一个类原生的渲染引擎,其使用更精简高效的渲染管线,性能比 WebView 更优异,并且带来诸多增强特性,如 Worklet 动画、手势系统、自定义路由、共享元素等。 使用这个组件库的前提是:通过微信小程序原生+skyline框架开发,所以目前我们不保证兼容webview框架(也就是电脑端与低版本的微信),但后续会进行系统性的兼容。 使用 Skyline UI前,请确保你已经学习过微信官方的 微信小程序开发文档 和 Skyline 渲染引擎文档 。 背景 随着Skyline 渲染引擎 1.1.0 版本发布,我们所运营的小程序也平稳的渡过了阵痛期,团队使用Skyline也越来得心应手,所以接下来,团队的开发重心全面偏向Skyline渲染框架,考虑有大量的UI交互重复,我们决定基于Skyline开发了这个UI组件库。 但团队力量有限,这个新生的组件可能有很多的不尽如人意,所以希望能以开源的方式吸引更多开发者使用Skyline框架,如果这个框架不适合你,也可以借鉴其思路。 Gitee Gitee仓库 在线预览 以下是目前两个使用该框架的小程序 SkylineUI组件库 [图片] NONZERO COFFEE [图片] 开始使用 UI库结构 Skyline UI组件库 依赖于以下四部分,具体使用参考以下的具体说明 utils工具库: 其中包含了UI库自定义的一个工具类SkyUtils,它包含了组件中所含的各种函数,非常重要。 各组件元素:sky-*(组件名) skywxss样式库:其中包含深浅色色彩、文字字体、布局等样式wxss 在小程序中引入 UI库 一、直接下载引入 点击下载组件包 将src下所有文件复制到您项目根目录下的components文件夹中,没有的话请自行新建。 二、npm引入 1.在小程序项目中,可以通过 npm 的方式引入 SkylineUI组件库 。如果你还没有在小程序中使用过 npm ,那先在小程序目录中执行命令: [代码]npm init -y [代码] 2.安装组件库 [代码]npm install jieyue-ui-com [代码] 3.npm 命令执行完后,需要在开发者工具的项目中点菜单栏中的 工具 - 构建 npm 两种引入方式的不同可能导致后续使用时,引用组件的路径不同,请注意区别 1.直接引入components文件夹内,引用地址通常是 ‘./components/‘ 2.npm引入,组件引用地址通常是’./miniprogram_npm/jieyue-ui-com/’ 如何使用 1.在app.js文件中初始化工具类,并且添加两个全局变量 [代码]// app.js App({ onLaunch() { ;(async ()=>{ // 全局注册工具类SkyUtils // 这里默认npm引用,地址为'./components/utils/skyUtils',如果是直接引用组件,地址可能是'./components/utils/skyUtils',后面不再说明 const SkyUtils = await import('./components/utils/skyUtils'); wx.SkyUtils = SkyUtils.default; // 初始化设备与系统数据 wx.SkyUtils.skyInit() // 小程序自动更新方法 wx.SkyUtils.versionUpdate() })() }, globalData: { sky_system:{}, sky_menu:{} }, }) [代码] 2.在app.wxss文件中引入样式文件 [代码]//wxss * _dark.wxss 是适配深色模式的色彩变量 @import '/miniprogram_npm/jieyue-ui-com/skywxss/skycolor.wxss'; @import '/miniprogram_npm/jieyue-ui-com/skywxss/skycolor_dark.wxss'; @import '/miniprogram_npm/jieyue-ui-com/skywxss/skyfontline.wxss'; @import '/miniprogram_npm/jieyue-ui-com/skywxss/skyfont.wxss'; @import '/miniprogram_npm/jieyue-ui-com/skywxss/skyother.wxss'; [代码] 3.page.json中引用组件 [代码]//page.json { "usingComponents": { "sky-text":"/miniprogram_npm/jieyue-ui-com/sky-text/sky-text" } } [代码] 4.页面中使用 [代码] // wxml <sky-text content="文本内容" max-lines="2" fade></sky-text> [代码] 5.其他组件具体使用请参考组件包中的redeme.md 适配深色模式 如果您在开发时,全部使用我们预设好的颜色变量,那么可以自动适配深色模式。 [代码].page{ background-color: var(--bg-l0); } [代码] [代码] <view style="background-color: var(--bg-l0)"></view> <view style="background-color: {{color}}"></view> [代码] [代码] Page({ data: { color: "var(--bg-l0)" } }) [代码]
2024-01-09 - [新手向]用scroll-view来实现横向选项栏(可点击定位居中)
预览 [图片] 这是一个用于切换房间的横向选择器 代码预览&详解 <scroll-view scroll-x="{{true}}" scroll-y="{{false}}" scroll-left="{{roomLeft}}" scroll-with-animation="{{true}}" class="top"> <view class="roomPiker {{currentRoom == roomIndex && 'active'}} {{roomIndex === 0 ? 'firstPick' : ''}} {{roomIndex === roomList.length - 1 ? 'lastPick' : ''}}" data-current="{{roomIndex}}" wx:for="{{roomList}}" wx:key="roomIndex" wx:for-index="roomIndex" wx:for-item="Room" catchtap="handleRoomChange"> <view class="pickerContainer"> <view class="round">{{Room.roundYear}}年{{Room.roundMonth}}月</view> <view class="roomText {{currentRoom == roomIndex && 'active'}}">{{Room.room}}房</view> </view> </view> </scroll-view> 使用官方组件scroll-view,首先启用横向滚动,禁用纵向滚动:scroll-x="{{true}}" scroll-y="{{false}}"(tip:参数要带{{}}),启用动画过渡scroll-with-animation,最后绑定滚动条位置scroll-Left; 接着遍历打印房间数组roomList,被选中的房间采用.roomPicker.active,第一个索引值为0房间元素margin-left设置为calc(50% - 32px),最后一个元素的margin-right则设置为calc(50% - 32px),这样就可以实现首尾元素都可以居中显示了;/*每个房间元素的宽度width是64px,所以要减去一半宽度*/ wxml的逻辑差不多就是这样。 .top { height: 64px; background-color: #FAFAFA; margin-top: 100px; padding-top: 9px; padding-bottom: 9px; /*这里避个坑,scroll-view的样式中尽量不要用左右的padding, 否则列表元素较少的时候会出现后面的元素溢出却不能滚动显示的问题,详情见 本文末问答参考*/ white-space: nowrap;/*必要属性*/ transition: all 0.5s ease; position: relative;/*必要属性*/ } .roomPiker { display: inline-block;/*必要属性*/ width: 64px; height: 64px; background-color: #E1F5B5; margin-right: 9px; /* 防止高度塌陷 */ vertical-align: top;/*必要属性*/ } .pickerContainer { display: grid; place-items: center; } .round { font-size: 12px; color: #383838; } .roomText { margin-top: 4px; font-size: 18px; color: #383838; } .roomPiker.firstPick { margin-left: calc(50% - 32px); } .roomPiker.lastPick { margin-right: calc(50% - 41px); } .roomPiker.active { background-color: #A5D63F; } .roomText.active { font-weight: 550; color: #000000; } wxss需要注意的地方不多,代码中注释的地方必要。 Page({ data: { roomList: [{ room: 501, roundYear: 2023, roundMonth: 11 }, { room: 502, roundYear: 2023, roundMonth: 11 }, { room: 503, roundYear: 2023, roundMonth: 11 }, { room: 504, roundYear: 2023, roundMonth: 11 }, { room: 505, roundYear: 2023, roundMonth: 11 }, { room: 506, roundYear: 2023, roundMonth: 11 }], currentRoom: 0, //当前显示房间索引 roomLeft: 0, //scroll-view滚动条位置,即距离左边的距离 }, handleRoomChange(e) {//处理点击事件 let { current } = e.currentTarget.dataset; if (this.data.currentRoom == current || current == undefined) return; this.setData({ currentRoom: current }) this.getScrollLeft(); //点击后调用改变滚动条位置的函数 }, getScrollLeft() {//改变滚动条位置,使选中元素在屏幕中央显示 const query = wx.createSelectorQuery();//创建查询对象 query.selectAll(".roomPiker").boundingClientRect(); //调用selectAll方法选择所有class为.roomPiker的元素,并调用boundingClientRect方法获取它们的边界信息。 query.exec(res => { console.log(res) let num = 0; for (let i = 0; i < this.data.currentRoom; i++) { //num += res[0][i].width; num += (res[0][i].width + 9)/*记得加上元素间的间隔*/ }//根据当前选中元素前面所有元素的宽度和水平间距计算出滚动条左边距离 const scrollLeft = Math.ceil(num);//取整 this.setData({ roomLeft: scrollLeft, }); }) } }) 本人是一个学生新手,这只是一篇学习笔记,仅供参考,欢迎指正~ 文档参考:https://developers.weixin.qq.com/miniprogram/dev/component/scroll-view.html问答参考:https://developers.weixin.qq.com/community/develop/doc/00002843c78850e695a0f3dcc6b800
2023-11-21 - Skyline模式下,全局样式在组件内部无效呢?
[图片] 如上图, Skyline模式下, 在组件内的 flex 和 items-center 样式,不起作用。 直接把组件内的代码放到页面就没问题。 ------------------------------------------------------------------------------------ 我是用的Uniapp开发的,附上解决方案: 修改 \node_modules\@dcloudio\uni-cli-shared\dist\json\mp\jsonFile.js 文件 function addMiniProgramComponentJson(filename, json) { // 方法中增加下面这一行,编译后会自动给组件里面加上 optons.styleIsolation 配置 json = Object.assign(json,{"styleIsolation": "apply-shared" }); jsonComponentsCache.set(filename, json); }
2023-11-03 - 微搭中如何使用小程序插件
概述小程序插件是对一组 js 接口、自定义组件或页面的封装(插件介绍),必须嵌入在小程序中才能被用户使用,不能独立运行。因此,插件适合用来封装自己的功能或服务,提供给第三方小程序进行展示和使用,本篇实践主要讲解在微搭中的小程序应用如何使用小程序官方插件的指引示例。 实践方案本篇实践以小程序“评价组件”为引用示例。 小程序管理后台安装插件1. 前往小程序官方管理后台安装插件,如下图所示,查看“体验评价”组件接口文档。 [图片]2. 在评价组件接口文档中获取组件appid,文档中示例部分即为评价组件真实appid。 [图片] 3. 在小程序管理后台的“设置-第三方服务-插件管理”中添加插件。 [图片]4. 添加插件:通过 appid 查找插件并添加。 [图片]5. 如果插件无需申请,添加后可直接使用;否则需要申请并等待插件开发者通过后,方可在小程序中使用相应的插件。 [图片]微搭中引入插件 1. 微搭中构建应用场景,按钮组件绑定跳转事件,前往评价页面。 [图片]2. 评价页面需修改全局配置文件以及绑定页面加载自定义方法[代码]function1[代码]。 [图片] 3. 全局配置文件引入“评价组件”。 [图片] "plugins": { "wxacommentplugin": { "version": "latest", "provider": "wx82e6ae1175f264fa" // 评价组件appid } } 4. 页面加载自定义方法[代码]function1,[代码]调用评价组件接口。 [图片] export default function({event, data}) { var plugin = requirePlugin("wxacommentplugin"); plugin.openComment({ success: (res) => { console.log('plugin.openComment success', res) }, fail: (res) => { console.log('plugin.openComment fail', res) } }) } 真机效果演示1.发布应用,真机效果下评价交互过程展示。 [图片] [图片] [图片] 2.小程序管理后台评价记录展示。 [图片]
2024-03-27 - 关于《微信小程序设计指南》中的字体颜色示例是什么意思?
微信小程序设计指南BW_0_Alpha_0.9 黑 BW_0_Alpha_0.5 黑 Black_Opacity_30 黑 Black_Opacity_15 黑 Black_Opacity_10 黑 上述 BW 0 是什么意思,还希望大佬们解释下。
2023-05-06 - 暗黑模式:当前暗黑模式的取舍?
1、目前官方已经支持到暗黑模式,全局配置也支持了如图 [图片] 2、大部分时候,通过全局配置能够完成暗黑模式 3、目前用的暗黑模式,可以参考之前的文章: 喂饭系列:暗黑模式(主题切换同理、taro同理)设置 4、探讨部分,目前暗黑模式的局限性: 4.1 使用小程序提供的暗黑模式: 优点:省心 缺点: 如果需要自定义颜色,仍旧需要自己写 无法手动切换暗黑模式,只能跟随系统 4.2 自定义暗黑模式 优点:自定义程度高,可以手动切换 缺点: 需要用自定义导航栏,否则使用 wx.setNavigationBarColor 切换过程中会闪一下 综上: 1、如果需要更好的体验,还是得自定义暗黑模式,只是需要更多精力去完善 2、如果不需要手动切换暗黑模式,则用小程序自带的,体验最佳
2024-04-30 - 小程序使用page-container时,低下的页面还要能上下滚动,怎么处理
https://developers.weixin.qq.com/miniprogram/dev/component/page-container.html 小程序使用page-container时,低下的页面无法上下滚动,如果需要让底下的云还能继续上下滚动,需要怎么处理
2023-03-01 - Skyline渲染引擎
在 WebView 渲染之外新增了一个渲染引擎 Skyline,其使用更精简高效的渲染管线,并带来诸多增强特性,让 Skyline 拥有更接近原生渲染的性能体验。
2023-11-02 - Skyline | 它超赞耶!Skyline 商用初体验 -- NONZERO COFFEE
抛开软件功能不谈,在交互体验上,打造一款能和原生APP相媲美的微信小程序,是我这样的微信开发者的梦想。想在技术上想实现原生的功能,开发成本是非常巨大的。但感谢微信小程序推出的渲染引擎Skyline,让我可以轻松实现原生app的交互体验。 目前,小程序已上线运行半月,虽有bug,但瑕不掩瑜。如果您对于使用webView开发还是Skyline开发还有疑虑的话,希望以下介绍对于有选择困难症的同学们有所帮助。接下来,我想借助自己开发的小程序,向大家推荐与介绍Skyline渲染引擎(当然也希望向大家推荐我所开发的小程序 -- 「 NONZERO COFFEE 」)。 如有错误或遗漏,欢迎在评论区批评指正,不胜感激。 小程序效果演示 [图片] 对小程序感兴趣的也可以加我好友讨论技术: [图片] 开发文档中对于Skyline的介绍,有以下几个重要的增强特性:worklet 动画、手势系统、自定义路由(预设路由)、共享元素动画。对于他们的使用和说明,我就不过多说明了,具体可以参照:https://mp.weixin.qq.com/s/dRz2PnkwHxYVL2kCexQ7WQ。 接下来我会介绍这些特性在小程序中,是怎么变成我的奇淫巧技,让小程序也可以更加优美。 worklet 动画:丝滑与复杂的页面元素展示 当页面滚动时,我们通常会希望根据页面不同的滚动状态或位置,灵活的展示或改变某些元素的状态,那么借助scroll-view的worklet:onscrollupdate时间和worklet动画可以完美实现效果~ [图片] 1、展示中可以看到背景图片会随着下拉放大,但是上滑时却是跟随上滑,完美实现不同状态 2、为了优化顾客点单体验,主要菜单栏(点单、外卖、商城)需要在上滑至离开屏幕时,顶部需要弹出备用菜单栏. 以上效果如果通过简单的wx:if判断,那么动画效果势必会很突兀,如果使用worklet 动画的timing()与spring()函数,可以轻松实现动画的流畅效果。 附上简单代码: 这里是置顶的点单菜单栏 这里是页面内容和 onLoad(options) { // 背景图片的高度和缩放 this.bgimgTop = shared(0) this.bgimgSca = shared(1) // 顶部点单栏透明度 this.topfunOp = shared(0) }, onReady() { // 背景图片动画监听 this.applyAnimatedStyle('.bgimg', () => { 'worklet' return {top: `${this.bgimgTop.value}px`,transform:`scale(${this.bgimgSca.value},${this.bgimgSca.value})`} }) // 顶部点单栏动画监听 this.applyAnimatedStyle('.fixtopview', () => { 'worklet' return {opacity: `${this.topfunOp.value}`} }) }, scrolling(e){ "worklet" let scrollTop = e.detail.scrollTop if(scrollTop <= 0){ //向下滚动 this.bgimgTop.value = 0 this.bgimgSca.value = 1 + (-scrollTop / 800) this.topfunOp.value = 0 }else{ //向上滑动 this.bgimgTop.value = - scrollTop this.bgimgSca.value = 1 let opTemp = (scrollTop / 200) this.topfunOp.value = opTemp >= 1 ? 1 : opTemp } }, Tip:由于代码量较大,以下说明不会附上详细代码,待后续整理好源码,会开源的 手势系统:提升用户留存率,神之一手 [图片] 这个实例呢,和第一个非常相似,是因为其中也 用到了worklet动画,换句话说,如果使用了Skyline框架,那么worklet将萦绕整个开发进展,它即代替了wss和css动画。 另外,更重要的是这个实例运用了手势系统,概而述之,就是使用<pan-gesture-handler><vertical-drag-gesture-handler>去更精细化的代理使用和监听<scroll-view>组件。 一、首页由<pan-gesture-handler>判断页面状态,状态1:往上拖动,不处理滚动事件,而是负责顶部搜索按钮和搜索框的动画效果、背景图片的缩放效果、以及整个容器的高度变化;状态2:向下滑动,则处理滚动事件 二、当判断向下滑动,处理滚动事件时,先由<vertical-drag-gesture-handler>判断<scroll-view>是否触顶,接下来也有两种状态,状态1:触顶,则改变由<vertical-drag-gesture-handler>接管,处理容器下滑的动画;状态2:没有触顶,则是<scroll-view>的正常滑动。 自定义路由 :小程序焕然一新,媲美原生 示例一: [图片] 这个详情弹出页的效果和webview下的 <page-container>组件的效果基本相似,如果这里简单的使用跳转新页面,则不符合UI设计稿,使用页面弹出组件时,又会发生用户的返回操作直接退出商品列表页,所以这里使用的自定义路由,并且还利用了worlklet和手势系统,实现了手势下滑与左滑时,如果下滑距离和下滑速度足够,则退出,不足够,则回弹恢复。 示例二: [图片] 这里的自定义路由,效果不明显,但是恰恰就需要这种效果。简单来说就是用户点击搜索框,需要跳转到搜索页面,但是设计稿中需要这种跳转是无感的,而自定义路由中将transitionDuration跳转时间设置为10或者更短,并且将handlePrimaryAnimation跳转效果设置成空值,即可完美实现。以下是简单的自定义路由配置代码: const ShopSearchRouteBuilder = (customRouteContext) => { console.info('skyline: half page route build') /** * 1. 手势拖动时采用原始值 * 2. 页面进入时采用 curve 曲线生成的值 * 3. 页面返回时采用 reverseCurve 生成的值 */ const handlePrimaryAnimation = () => { 'worklet' return { } } return { opaque: false, handlePrimaryAnimation, transitionDuration: 10, reverseTransitionDuration: 10, barrierDismissible:true, canTransitionTo: false, canTransitionFrom: false, } } 共享元素 :我的奇奇怪怪的使用 [图片] 如果没有看出这个例子的共享元素是如何使用的,那么我用webview的页面来分解一下。 [图片] [图片] 这里的商品列表页和购物车其实是两个页面,但是底部的购物车按钮元素,又需要在两个页面跳转时,无感展示,所以这里是最适合使用共享元素的,我个人认为,这比跳转页面时使详情图片在不同页面间飞跃更加高级和实用。 但是既然说到了图片飞跃的效果,下面我也来展示几个实用例子: [图片] [图片] 这里的两个例子也都是列表页跳转至详情页时,实例图片的飞跃。 当然小程序中的动画效果可不止以上罗列的这么几个,总而言之,使用了Skyline之后,以上的动画效果在小程序中随处可见,如果不是本人的代码能力有限,我想小程序的整体效果完全可以媲美APP的原生效果。 希望以上这些能够为即将进行微信小程序开发,而苦恼于是否使用Skyline的同学们,带来新的思路。 Skyline的bug:背刺与惊喜 新技术需要有勇气去使用,去克服遇到的一切问题我遇到了很多bug,虽然他总会在我意料不到的地方背刺我一刀,但是Skyline的效果实在让我欲罢不能,万分庆幸的是我真的要非常感谢微信团队的老师们,给了我很多建议与帮助。 还没有经过老师同意上图,暂时先打个码。 [图片] [图片] 好了,接下来说一下,现在还没有解决的bug,希望微信团队能尽快修复。 1、小程序使用的是自定义tabbar,在ios下,相隔较长时间再重新打开小程序,会出现白屏的现象,是完全不显示任何元素,但vconsole是可以看到各生命周期已经正常允许了的。这里实在不好上传相关代码,因为首页代码很复杂。还关联了全局状态管理。 与其他同学沟通过,发现并不是我的单一案例,也有人复现了。 解决方案:万幸我使用的是自定义tabbar,小程序启动首页被我设置成了某一个单页面,然后从单页面跳转到tabbar首页,并且这个单页面还能设置成宣传海报首页,这也算是一个自洽的解决方法。 2、wx.switchTab()方法:前提依旧是自定义tabbar,假设tabbar有A、B、C、D首先进入了tabbar首页A页面,然后跳转到其他页面,如果这时使用wx.switchTab()跳转至B\C\D页面,会出现tabbar栏不出现的情况。 解决方案:全部跳转至A首页,或者tabbar页面底部安置一个假的tabbar栏。 3、最麻烦的问题:如果我不考虑兼容的问题,那么即代表我需要抛弃一部分没有升级微信版本习惯的用户,因为低版本的微信无法正常使用Skyline框架开发的小程序。 解决方案:如果您的开发成本和时间足够,完全可以慢慢兼容、如果还在纠结,建议您单页面慢慢升级Skyline,Skyline支持单页面渲染(这可真是太棒了),但是对于我这种有代码强迫症的人来说,这是硬伤,我不能忍受一个页面是webview,另一个是skyline,我只能慢慢熬过用户更新的阵痛期。 最后,我还想说几句话: 1、开源这件事,我已经做好了准备,但是开源成本太大,我还没考虑好是否花时间进行开源,总之,有需要的同学,可以写个评论支持下。 2、开发: (1)前端:有80%以上的图片与文字,都是通过后台数据渲染的,也就是说部署后,基本不需要修改小程序代码,但是自由度极高。 (2)后台框架:考虑到是小项目,只使用了java的springboot,数据库使用的是mySql,缓存则也是通过代码实现的(不使用redis,是因为云托管不支持,单独买太贵了),存储使用的腾讯的COS。 (3)后台部署:我抛弃了传统服务器,拥抱了微信云托管。 (4)管理admin页面:使用的是antd admin(vue2),部署方面同样也是使用的云托管的静态资源存储。
2023-10-10