blogs
blogs copied to clipboard
微信小程序的其他研究
- 下拉刷新
- 瀑布流
- 纵向轮播
- 生成图片保存到相册
- 伪 AR 效果
微信小程序的小型专题研究,至于 案例 可前往 我的仓库 查看
下拉刷新
在 app.json 的 window 选项中或页面配置中开启 enablePullDownRefresh; 在页面中写上 onPullDownRefresh 监听下拉刷新的触发; 当处理完数据刷新后,wx.stopPullDownRefresh 可以停止当前页面的下拉刷新。
注意事项
- 全局的 showToast 和 showLoading 在苹果机上会严重影响下拉的回弹效果,不宜一起使用。
- 现在各页面都有自己的 json,可以分页面设置是否下拉刷新哟(官方文档)。
简单的美化
app.wxss 里 page { background: #e5e5e5 }
让背景色偏灰
app.json 里 "backgroundTextStyle": "dark"
让三个点变成黑色
感谢:http://www.wxappclub.com/topic/935
自写下拉刷新
除开配置中的 tabBar 的 position 为 top 这种样式外,可能你还会遇上超坑比的产品跟你说,顶部还得再加个搜索,或者顶部导航换个样式,那样我们就无法使用小程序自带的下拉刷新了。
而在小程序中,fixed 慢慢得到了比较好的支持,不会像 H5 那样特别不听话。 所以直接将顶部元素 fixed 一下,本页设为可下拉刷新,效果也是很棒的。
// page.json
{
"enablePullDownRefresh": true
}
// page.wxml
<view class="top-bar">顶部导航</view>
<view class="section others">test</view>
// page.wxss
.top-bar {
position: fixed;
top: 0; left: 0; right: 0;
z-index: 2;
background: #fff;
box-shadow: 0 2rpx 5rpx rgba(0,0,0,.1);
}
// page.js
Page({
onPullDownRefresh: function () {
wx.stopPullDownRefresh();
},
})
但需求往往无穷无尽,当遇到页面需要不同滑动区域时,比如 tabs 对应多个 scroll-view。 那么上面方案就会遇到一个问题,它们的滚动条是同一个呀,切换时滚动到的位置是一致的。 当然,用数组去记录切换前的已滑动位置,也是个不错的办法。
而不死心的我还是想试试有没有更直接的办法,实则是有的,只是不太妙而已。 原理:当 scroll-view 有高度时会使用 scroll-view 的滚动条,无高度时会使用全局的滚动条。 这样的话,我们只需监听 scroll 判断滑到顶了则去掉高度,那不就可以用全局的下拉刷新了吗。 方法是可行,方案也很骚,但苹果机上有问题,下拉刷新时的三个点是在 web-view 外面。
// page.wxml
<scroll-view
style='height:{{listHeight}}px'
scroll-y
enable-back-to-top
bindscrolltolower='onReachBottom'
bindscroll='scroll'>
<view>列表内容</view
</scroll-view>
// page.js
let listHeight = 0;
Page({
onLoad: function() {
// 计算列表高度,这里的 50 为 .top-bar 高度,因为我设了 100rpx 的定值
wx.getSystemInfo({success: res => {
listHeight = res.windowHeight - 50;
this.setData({ listHeight: res.windowHeight - 50 });
}})
},
scroll: function(e) {
var st = e.detail.scrollTop
if (st < 1) this.setData({ listHeight: NaN })
},
onPullDownRefresh: function () {
setTimeout(() => {
this.setData({ listHeight: listHeight })
wx.stopPullDownRefresh();
}, 1000);
},
})
真的去自己重构 scroll-view 的话,我个人觉得是不必的。 只使用 scroll-view 的 bindscrolltoupper 触顶刷新其实是最好的, 让用户不用非要进行下拉刷新的操作,无意识滑回顶部时发现数据变了可能更令人开心。
瀑布流
CSS3 的 column 众所周知,用起来非常舒爽,但有顺序问题(会先把第一栏填满再填后面的)。 而更多 jquery 插件,不能引用是一回事,其算法在小程序中也比较难实现(宽高位置获取太麻烦)。 相对而言,花瓣网 的这种分栏式瀑布流要更容易实现一些。
布局和结构不难理解,分为 cols 个数的栏目,分别有自己的数组 list[col]。
<view class="list-box">
<block wx:for="{{cols}}" wx:key="*this" wx:for-item='col'>
<view class='list list{{col}}'> <!-- 单个栏目 -->
<block wx:for="{{list[col]}}" wx:key="unique" wx:for-index='i' wx:for-item='one'>
<view class='item'> <!-- 栏目中的一项 -->
<!-- 图片 -->
<image src='{{one.img}}' style='width:{{one.width}}px;height:{{one.height}}px'></image>
<!-- 文字,可多行 -->
<view class='desc'>{{one.text}}</view>
</view>
</block>
</view>
</block>
</view>
// index.js
data: {
cols: 2,
list: [[], []],
}
然后将新数据插入到栏目中最矮的一个栏目中去,那么我们就需要知道图片高度和文本高度。 获取图片高度可通过 wx.getImageInfo,用 bindload 也可以。 获取文本高度可通过 wx.createSelectorQuery().select('.desc').boundingClientRect。
// 返回单个列表项的高度,obj 为数据,index 为数据索引
getHeight: function(obj, index, callback) {
// 获取图片信息
wx.getImageInfo({
src: obj.img,
success: img => {
// 修改图片尺寸,宽度等于 col 宽,高度自适应
var ratio = img.width / img.height
obj.width = img.width = this.colWidth; // 这个 col 宽度需要另外获取
obj.height = img.height = obj.width / ratio;
// 获取文字高度
var $dom = wx.createSelectorQuery().select('#text_' + index);
$dom.boundingClientRect(rect => {
var height = img.height + rect.height;
obj.itemHeight = height;
callback && callback(height);
}).exec();
},
});
},
看到网上大多实现下来,最终列表项的顺序可能是无序的,所以我们最好是所有高度获取完了再计算。
// 传入新数据,计算列表分布,更新列表
update_list: function (r) { // r 为新数据
var total = r.reduce(x => ++x, 0);
r.forEach((item, i) => {
// 获取每个列表项的高度
this.getHeight(item, i, (height) => {
if (--total < 1) { // 高度全部获取完毕,开始计算
this.data.list = this.theNewList(r, this.data.list);
this.setData({ list: this.data.list });
}
})
})
},
// 根据后加入的列表,产生新的列表
theNewList: function (temp, list) { // temp 即 r,为新数据
temp.forEach(item => {
// 选出当前 col 高度最小值,_height 存储着各栏目的当前高度
var min = Math.min.apply(null, _height);
// 选出当前 col 高度最小值的索引
min = _height.indexOf(min);
// 进行赋值,这样做才是有顺序的列表
_height[min] += item.itemHeight;
list[min].push(item);
})
return this.data.list;
},
纵向的轮播组件
其实写轮播并不是件难事,小程序的轮播也是如此。 需处理的几个方面即可:拖拽操作,防止事件混淆,回调与自定义项。
另一方面,轮播的样式和场景处理也有不同流派 小程序的轮播是以父级高度为主,子级是 absolute 的,所以还可以完成 重叠/3d 等形式的动画切换。 而 swiper 的轮播是以 flex 布局为主,子级撑起父级高度的样式方案,只有单轴向的动画切换。
本案采用后者。当然这是由项目需求决定的,但其中大多流程不会改变。
// ... 代码整理中
生成图片并保存到相册
步骤说清楚是简单的,但不得不说,小程序的坑能把人脚给崴了。
- canvas 绘制(wx.createCanvasContext 等)
- wx.canvasToTempFilePath(options) 导出图片
- wx.saveImageToPhotosAlbum 保存到相册
大致列举一下,后期会不断更新:
- setTextAlign 和 setTextBaseline 在少量机型上会无效,所以不推荐使用,自己计算宽度比较保险。
- drawImage 是异步的,前面画的图可能会覆盖后面的字,推荐先画图然后延时1s再重新画图画字。
- 保存到相册少量机型不支持,所以也要做提示长按保存的兼容方案。
- 保存到相册功能会被禁用,所以在 complete 回调里要判断是否 deny。
// ... 代码整理中
伪 AR 效果
大致介绍一下,就是打开了摄像头,进行现实场景的识别,然后给予虚拟场景下的反馈。比如扫个图形出来个三维小萝莉什么的。
真 AR 能识别到视频流的每一帧,对小萝莉还能进行三维场景下的定位处理,这必然是很烧的事情,涉及到截图效率/场景识别/三维定位计算/三维绘制几个比较头疼的事情。
所以小程序能做的还是伪 AR,定时截个图,识别成功出结果,放个三维动画结束。
// ... 代码整理中
不错不错