先上最终效果图,表格可以上下、左右滑动,表格内容宽度固定,高度自适应内容大小,附上滑动条,滑动条长度宽度也是自适应表格的。
在编写过程中遇到的难点
- 表格的编写,小程序无table标签,需要自己用view+flex布局编写。
- 表格内容宽度固定,长度自适应。
- 滚动条逻辑及代码编写。
- 滚动条的卡段优化。
- 小程序组件的编写及应用。
1. 表格的编写
之前写过一篇简单利用view以及flex布局编写表格的文章。 链接: 小程序绘制表格table(表头固定,可上下左右滑动).
2. 表格内容宽度固定 长度自适应
当把表格填写进去后发现当内容宽度大于表格宽度时,居然不会换行,直接溢出。。 后来搜索了一下发现view是不会自动换行的,需要在.th .tr样式里加上这句:
white-space: pre-line;
display: flex;
justify-content: center;
align-items: center;
text-align: center;
加上后就变成了这样:
3. 滚动条逻辑及代码编写
为什么要自制滑动条呢,明明微信的scrollview有自带滑动条的功能: 自带的滑动条很好,也足够我们用了,但是!为什么ios的横向滑动条不显示啊????我搜索了一番发现我不是个例,于是我就开始自己编写滑动条,对了记得把小程序自带的滑动条屏蔽,因为安卓用户是显示的。用show-scrollbar="false"也是不生效的,需要在wxss中添加以下代码片段:
::-webkit-scrollbar {
display: none;
width: 0;
height: 0;
color: transparent;
}
滚动条的编写逻辑参考了这篇文章:https://www.cnblogs.com/jiechen/p/4712631.html 主要是抓准一个比例关系:滚动条总长度 :滚动条长度 = 层级2 :层级1 由于滚动条的总长度通常与层级1的高度一致,所以:滚动条的长度 = (层级1 * 层级1)/ 层级2
当表格滑动时,滑动条也要跟着滑动,因此我们需要计算出滑动条滑动的距离: 首先算出滑动比例 = 滑动条可滑动距离 / 表格可滑动距离; 即:滑动比例 =(滑动条总长度 - 滑动条长度)/(层级2 - 层级1)= (层级1 - 滑动条长度)/(层级2 - 层级1) 那么滑动条需要滑动的距离= 滑动比例 * 层级滑动的距离 层级滑动的距离小程序已经在scrollview组件中给了我们: 以上逻辑形成js代码片段如下:
data: {
sliderWidth: '',
barWidth: '',
totalWidth: '',
slideRatioX: '',
slideLeft: '',
},
methods: {
getRatioX: function (e) {
var query = wx.createSelectorQuery().in(this);
var _totalWidth = 0;
query.select("#tableContent").boundingClientRect((res) => {
_totalWidth = res.width;
}).exec()
query.select('#tableX').boundingClientRect((res) => {
var _barWidth = res.width;
var _showWidth = (_barWidth * _barWidth) / _totalWidth;
var _ratio = (_barWidth - _showWidth) / (_totalWidth - _barWidth);
this.setData({
sliderWidth: _showWidth,
barWidth: _barWidth,
totalWidth: _totalWidth,
slideRatioX: _ratio,
})
}).exec()
},
}
滑动事件:
<scroll-view scroll-x="true" bindscroll="getleft">
</scroll-view>
<view class="slidex">
<view class='slidex-bar' id="barWidth" style="width: {{barWidth}}px;">
<view class="slidex-show" id="sliderWidth" style="width: {{sliderWidth}}px;margin-left:{{slideLeft}}"></view>
</view>
</view>
getleft: function (e) {
this.setDate({
slideLeft: e.detail.scrollTop * this.data.slideRatioX
});
}
4. 滚动条的卡段优化
原本滑动条的部分到这里就结束了,我在用调试测试的时候也好好的,直到我试了一下真机调试。。。 滑动条卡到我怀疑人生,明明表格已经滑到最后了,但是滑动条还要隔两三秒才滑动。。。
后面排查了一轮发现是小程序通信耗时的问题,官方文档中也给出了我们解决方案: 链接: WXS响应事件. 这就好办了,我们只需要把滑块移动的函数写成WXS函数就完事了! 更改代码如下:
<wxs module="wxs" src="./table.wxs"></wxs>
<scroll-view class="tableX" scroll-x bind:scroll="{{wxs.onScrollX}}" data-slideratiox="{{slideRatioX}}">
</scroll-view>
var onScrollX = function(e, ownerInstance) {
var scrollBarBlockLeft = e.detail.scrollLeft * e.currentTarget.dataset.slideratiox;
ownerInstance.selectComponent('#sliderWidth').setStyle({"margin-left": scrollBarBlockLeft + 'px'});
}
module.exports = {
onScrollX: onScrollX
};
<view class="slidex">
<view class='slidex-bar' id="barWidth" style="width: {{barWidth}}px;">
<view class="slidex-show" id="sliderWidth" style="width: {{sliderWidth}}px;"></view>
</view>
</view>
5. 小程序组件的编写及应用
由于表格在项目的多处地方用到,所以把它做成组件的形式比较方便复用。 小程序组件的学习参考了这篇文章:链接: 微信小程序 基础3【组件化开发、自定义组件、全栈开发、使用Express】. 直接上代码把~
html部分:
<!--components/table/table.wxml-->
<wxs module="wxs" src="./table.wxs"></wxs>
<view class="table">
<view style="width: 99%;display: flex; justify-content: center;">
<scroll-view class="tableX" id="tableX" scroll-x show-scrollbar="false" bind:scroll="{{ wxs.onScrollX }}"
data-slideratiox="{{slideRatioX}}">
<view id="tableHeader" style="display: inline-block;width: {{totalWidth}}px;">
<!-- slot -->
<slot name="tableheader"></slot>
<!-- /slot -->
</view>
<scroll-view class="tableY" id="tableY" scroll-y show-scrollbar="false"
style="height: {{tableHeight}};width: {{totalWidth}}px;" bind:scroll="{{ wxs.onScrollY }}"
data-slideratioy="{{slideRatioY}}">
<view id="tableContent" style="display: inline-block">
<!-- slot -->
<slot name="tablecontent"></slot>
<!-- /slot -->
</view>
</scroll-view>
</scroll-view>
<!--滚动条Y部分-->
<view class="slide" wx:if="{{slideBarYshow}}" style="height: {{barHeight}}px;margin-top: {{tableTopHeight}}px;">
<view class='slide-bar' id="barHeight" style="height: {{barHeight}}px;">
<view class="slide-show" id="slideHeight" style="height:{{slideHeight}}px;"></view>
</view>
</view>
</view>
<!--滚动条X部分-->
<view class="slidex">
<view class='slidex-bar' id="barWidth" style="width: {{barWidth}}px;">
<view class="slidex-show" id="sliderWidth" style="width: {{sliderWidth}}px;"></view>
</view>
</view>
</view>
wxss部分:
.table {
width: 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.tableX {
border: 1px solid #dadada;
white-space: nowrap;
border-right: 0;
border-bottom: 0;
width: 93%;
}
.tableY {
display: flex;
justify-content: flex-start;
white-space: nowrap;
}
.tr{
display: flex;
justify-content: flex-start;
}
.th,.td {
width: 300rpx;
padding: 10px 0;
border-bottom: 1px solid #dadada;
border-right: 1px solid #dadada;
text-align: center;
}
.th {
font-weight: bold;
}
.slide {
width: 10rpx;
margin-left: 20rpx;
position: relative;
}
.slide .slide-bar {
position: absolute;
width: 5rpx;
margin: 0 auto;
background: #eee;
}
.slide .slide-bar .slide-show {
height: 100%;
background-color: #0b97f5;
}
.slidex {
width: 99%;
height: 10rpx;
display: flex;
justify-content: center;
margin-top: 10rpx;
}
.slidex-bar {
width: 700rpx;
height: 5rpx;
background-color: #eee;
}
.slidex-show {
height: 100%;
background-color: #0b97f5;
}
::-webkit-scrollbar {
display: none;
width: 0;
height: 0;
color: transparent;
}
js部分:
Component({
properties: {
tableHeight: {
type: String,
value: '',
observer: function (newVal, oldVal) {
if (newVal == '100%') {
this.setData({
slideBarYshow: false,
})
} else {
this.setData({
slideBarYshow: true,
})
}
}
},
},
options: {
multipleSlots: true,
},
data: {
slideBarYshow: true,
slideHeight: '',
barHeight: '',
totalHeight: '',
slideRatioY: '',
tableTopHeight: 0,
sliderWidth: '',
barWidth: '',
totalWidth: '',
slideRatioX: '',
},
methods: {
getRatioY: function (e) {
var query = wx.createSelectorQuery().in(this);
var _totalHeight = 0;
query.select("#tableHeader").boundingClientRect((res) => {
this.setData({
tableTopHeight: res.height
})
}).exec()
query.select("#tableContent").boundingClientRect((res) => {
_totalHeight = res.height;
}).exec()
query.select('#tableY').boundingClientRect((res) => {
var _barHeight = res.height
var _showHeight = (_barHeight * _barHeight) / _totalHeight;
var _ratio = (_barHeight - _showHeight) / (_totalHeight - _barHeight);
this.setData({
barHeight: _barHeight,
slideHeight: _showHeight,
totalHeight: _totalHeight,
slideRatioY: _ratio
})
}).exec()
},
getRatioX: function (e) {
var query = wx.createSelectorQuery().in(this);
var _totalWidth = 0;
query.select("#tableContent").boundingClientRect((res) => {
_totalWidth = res.width;
}).exec()
query.select('#tableX').boundingClientRect((res) => {
var _barWidth = res.width;
var _showWidth = (_barWidth * _barWidth) / _totalWidth;
var _ratio = (_barWidth - _showWidth) / (_totalWidth - _barWidth);
this.setData({
sliderWidth: _showWidth,
barWidth: _barWidth,
totalWidth: _totalWidth,
slideRatioX: _ratio,
})
}).exec()
},
}
})
json文件:
{
"component": true,
"usingComponents": {}
}
wxs文件:
var onScrollY = function(e, ownerInstance) {
var scrollBarBlockLeft = e.detail.scrollTop * e.currentTarget.dataset.slideratioy;
ownerInstance.selectComponent('#slideHeight').setStyle({"margin-top": scrollBarBlockLeft + 'px'});
}
var onScrollX = function(e, ownerInstance) {
var scrollBarBlockLeft = e.detail.scrollLeft * e.currentTarget.dataset.slideratiox;
ownerInstance.selectComponent('#sliderWidth').setStyle({"margin-left": scrollBarBlockLeft + 'px'});
}
module.exports = {
onScrollY: onScrollY,
onScrollX: onScrollX
};
在使用过程中还发现了两个细节问题: 1. 多处使用slot必须在js文件中添加:multipleSlots: true, 还要给slot添加name属性。
options: {
multipleSlots: true,
},
2. 当使用组件并传入值,且值会动态变化时,此处属性值变化有延迟:
properties: {
tableHeight: {
type: String,
value: '',
observer: function (newVal, oldVal) {
if (newVal == '100%') {
this.setData({
slideBarYshow: false,
})
} else {
this.setData({
slideBarYshow: true,
})
}
}
},
},
所以你需要在调用修改后的值的方法那加上延迟函数:
setTimeout(() => {
this.table.getRatioY();
}, 300);
3. 调用组件的顺序不能错:定义组件 > 获取数据 > 调用组件函数
onShow: function () {
this.table = this.selectComponent("#table");
this.changeList();
this.changeMenu();
this.table.getRatioY();
this.table.getRatioX();
},
尾声
到这,基本上所有的问题都解决了~ 这次项目对于我一个新手来说确实很有挑战性,但也在其中学到了很多东西,希望能坚持记录自己成长路上遇到的问题!
|