小程序之index索引器品牌篇实现与步骤详述
图例
index组件
.js
// 品牌索引列表
Component({
/** 组件的属性列表 */
properties: {
/** 数据 */
data: {
type: Object,
value: {},
observer: function(newVal, oldVal) { this.resetRight(newVal); }
},
/** 配置项 */
config: {
type: Object,
value: {
animation: true, // 过渡动画是否开启
search: true, // 是否开启搜索
searchHeight: 45, // 搜索条高度
suctionTop: true // 是否开启标题吸顶
}
}
},
data: {
list: [],
rightArr: [], // 右侧字母展示
jumpNum: '', //跳转到那个字母
topGroup: [], // 内容高度数据
pos: {
isTop: false,
y: 0,
oldIndex: -1
},
listIndex: 0,
moveDistance: 0
},
ready() {},
methods: {
/** 数据重新渲染 */
resetRight(data) {
let rightArr = [];
for (let i in data) { rightArr.push(data[i].key); }
this.setData({
list: data,
rightArr
}, () => {
if (data.length != 0) {
this.queryMultipleNodes();
}
});
},
/** 右侧字母点击事件 */
jumpMt(e) {
let jumpNum = e.currentTarget.dataset.id;
this.setData({ jumpNum });
},
/** 列表点击事件 */
detailMt(e) {
let detail = e.currentTarget.dataset.detail;
let data = {type: "CLICK", data: detail};
this.triggerEvent('brand', data);
},
// 获取搜索输入内容
input(e) {
this.value = e.detail.value;
},
// 基础搜索功能
searchMt() {
let data = {type: "SEARCH", data: this.value ? this.value : ""};
this.triggerEvent('brand', data);
},
/** 监听滚动 */
scroll(e) {
let top = e.detail.scrollTop;
let index = this.currentIndex(top);
let list = this.data.topGroup;
let distance = top - list[this.data.listIndex];
let num = -(list[this.data.listIndex + 1] - top - 40);
// 渲染滚动索引
if (index !== this.data.listIndex) {
this.setData({
listIndex: index,
moveDistance: 40,
})
// 如果监听到 index 的变化 ,一定要return ,否则吸顶会先变化文字后运动,会闪烁
return;
}
if (num < 0) num = 0;
if (num !== this.data.moveDistance) { this.setData({ moveDistance: num }) }
},
/**
* 获取当前滚动索引
*/
currentIndex(y) {
let listHeight = this.data.topGroup;
for (let i = 0; i < listHeight.length; i++) {
let height1 = listHeight[i];
let height2 = listHeight[i + 1];
if (!height2 || (y >= height1 && y < height2)) {
return i;
}
}
return 0;
},
/**
* 获取节点信息
*/
queryMultipleNodes() {
let self = this
const query = wx.createSelectorQuery().in(this);
query.selectAll('.fixed-title-hock').boundingClientRect((res) => {
res.forEach(function(rect) { rect.top // 节点的上边界坐标 })
}).exec((e) => {
let arr = [];
e[0].forEach((rect) => {
let num = 0;
if (rect.top !== 0) {
num = rect.top - (self.data.config.search ? self.data.config.searchHeight : 0);
}
arr.push(num);
})
this.setData({ topGroup: arr });
})
}
}
})
.json
{
"component": true,
"usingComponents": {}
}
.wxml
<view class='list-warpper'>
<!-- 搜索框 -->
<view wx:if="{{config.search}}" class='list-search' style="height:{{config.searchHeight}}px">
<view class='list-search-box'>
<icon type="search" size="15" />
<input placeholder="输入品牌名称" bindinput='input' />
</view>
<button class='search-button' catchtap='searchMt'>搜索</button>
</view>
<view wx:if="{{config.suctionTop}}" class='fiexed-box list-title' style='transform: translateY(-{{moveDistance}}px);top:{{config.search?90:0}}rpx'>
{{list[listIndex].key === "HOT" ? "热门品牌" : list[listIndex].key}}
</view>
<!-- 搜索到所有数据的时候显示 -->
<block wx:if="{{list.length != 0 }}">
<scroll-view class="list-scroll {{config.search?'top':''}}"
style=" padding-top:{{config.search?config.searchHeight:0}}px"
scroll-y="true" scroll-into-view="{{jumpNum}}"
scroll-with-animation="{{config.animation}}"
bindscroll="scroll">
<!-- 主体内容显示 -->
<view id="{{'index'+index}}" wx:for="{{list}}" wx:key="key">
<view class='list-title fixed-title-hock'>{{item.key === "HOT" ? "热门品牌" : item.key}}</view>
<block wx:if="{{ item.key === 'HOT' }}">
<view class="index-brand-box">
<view wx:for="{{item.list}}"
wx:for-index="in"
wx:key="in"
wx:for-item="it"
class="index-brand-card"
data-detail="{{it}}"
catchtap="detailMt">
<view class="brand-img"><image src="{{it.img}}" class="brand-pic"></image></view>
<view class="brand-name"> {{it.brandName}} </view>
</view>
</view>
</block>
<block wx:else>
<view
class="index-brand-item"
wx:for="{{item.list}}"
wx:for-index="in"
wx:key="in"
wx:for-item="it"
data-detail="{{it}}"
catchtap="detailMt">
<view class="dflex-start">
<view class="brand-img"><image src="{{it.img}}" class="brand-pic"></image></view>
<view> {{it.brandName}} </view>
</view>
</view>
</block>
</view>
</scroll-view>
<!-- 右侧索引显示 -->
<view class='list-right-wrapper'>
<block wx:for="{{rightArr}}" wx:key="rightArr">
<view wx:if="{{rightArr[index] === 'HOT'}}" class='right-item {{listIndex == index?"active":""}} icon48' data-id="{{'index'+index}}" catchtap='jumpMt'>♡</view>
<view wx:else class='right-item {{listIndex == index?"active":""}}' data-id="{{'index'+index}}" catchtap='jumpMt'>
{{rightArr[index]}}
</view>
</block>
</view>
</block>
<!-- 没有搜索到数据的时候显示 -->
<block wx:else>
<view class='nodata'>没有搜索到相关的数据哦</view>
</block>
</view>
.wxss
.list-warpper {
position: relative;
width: 100%;
height: 100%;
box-sizing: border-box;
}
.list-scroll {
width: 100%;
height: 100%;
box-sizing: border-box;
}
.list-scroll.top{
padding-top: 90rpx;
}
/* 样式控制 */
.list-title {
background: #f5f5f5;
color: #666;
font-size: 36rpx;
padding-left: 30rpx;
height: 80rpx;
line-height: 80rpx;
}
.list-name {
position: relative;
font-size: 28rpx;
padding: 15rpx;
padding-left: 30rpx;
color: #999;
}
.list-name.border::after {
content: "";
position: absolute;
left: 30rpx;
right: 0;
top: 0;
height: 1px;
background: #f5f5f5;
}
.list-right-wrapper {
position: absolute;
right: 0;
top: 50%;
right: 20rpx;
padding: 20rpx 0;
z-index: 999;
transform: translateY(-50%);
background: #ddd;
border-radius: 40rpx;
box-sizing: border-box;
text-align: center;
}
.right-item {
display: flex;
justify-content: center;
align-items: center;
padding: 2rpx 10rpx;
font-size: 40rpx;
color: #666;
}
.icon48{
font-size: 56rpx;
}
.right-item.active {
color: #ff9900;
}
.list-search {
position: absolute;
top: 0;
left: 0;
display: flex;
align-items: center;
width: 100%;
height: 90rpx;
padding: 10rpx 30rpx;
box-sizing: border-box;
z-index: 20;
background: #FFF;
border-bottom:1px #f5f5f5 solid;
}
.search-title {
flex-shrink: 0;
font-size: 28rpx;
padding-right: 10rpx;
}
.list-search-box {
display: flex;
align-items: center;
padding: 0 30rpx;
width: 100%;
height: 70rpx;
background: #f5f5f5;
border-radius: 90rpx;
font-size: 28rpx;
box-sizing: border-box;
}
.list-search-box input {
width: 100%;
padding-left: 10rpx;
}
.search-button {
flex-shrink: 0;
height: 60rpx;
line-height: 60rpx;
font-size: 28rpx;
margin-left: 10rpx;
}
.fiexed-box {
position: absolute;
top: 90rpx;
z-index: 19;
width: 100%;
}
/* new */
.index-brand-box{
width: 100%;
background-color: #F3F3F3;
display: flex;
justify-content: flex-start;
flex-wrap: wrap;
padding-left: 30rpx;
box-sizing: border-box;
}
.index-brand-box .index-brand-card{
width: 148rpx;
height: 164rpx;
background-color: #fff;
margin: 20rpx 30rpx 20rpx 0;
}
.index-brand-box .index-brand-card:nth-child(4n) {
margin-right: 0;
}
.index-brand-box .index-brand-card .brand-img{
width: 100%;
height: 102rpx;
padding: 10rpx;
border-bottom: 2rpx solid #ededed;
box-sizing: border-box;
}
.index-brand-box .index-brand-card .brand-pic{
width: 100%;
height: 100%;
}
.index-brand-box .index-brand-card .brand-name{
width: 100%;
height: 60rpx;
line-height: 60rpx;
text-align: center;
}
.index-brand-item{
width: 100%;
height: 80rpx;
background-color: #fff;
padding: 0 30rpx;
border-bottom: 2rpx solid #f2f2f2;
box-sizing: border-box;
}
.dflex-start{
display: flex;
justify-content: flex-start;
align-items: center;
color: #000;
}
.index-brand-item .brand-img{
width: 100rpx;
height: 80rpx;
background-color: chartreuse;
margin-right: 30rpx;
}
/* 无数据 */
.nodata {
padding-top: 200rpx;
text-align: center;
font-size: 32rpx;
color: #ddd;
}
调用
.js
var _default =
{
data: function data() {
return {
indexHeight: 0,
queryBrandName: "",
brandList: [
{ key: "HOT", list: [
{ img:"/static/img/brand/geli.png", brandName:"格力" },
{ img:"/static/img/brand/meidi.png", brandName:"美的" },
{ img:"/static/img/brand/feilipu.png", brandName:"飞利浦" },
{ img:"/static/img/brand/shenling.png", brandName:"申菱" },
{ img:"/static/img/brand/geli.png", brandName:"格力" },
{ img:"/static/img/brand/meidi.png", brandName:"美的" },
{ img:"/static/img/brand/feilipu.png", brandName:"飞利浦" },
{ img:"/static/img/brand/shenling.png", brandName:"申菱" }
]},
{ key: "M", list: [
{ img:"/static/img/brand/meidi.png", brandName:"美的" },
{ img:"/static/img/brand/meibo.png", brandName:"美博" }
]},
{
key: "G", list: [
{ img:"/static/img/brand/geli.png", brandName:"格力" },
{ img:"/static/img/brand/gelanshi.png", brandName:"格兰仕" }
]
},
{
key: "T", list: [
{ img:"/static/img/brand/tcl.png", brandName:"TCL" }
]
},
{
key: "S", list: [
{ img:"/static/img/brand/shenling.png", brandName:"申菱" },
{ img:"/static/img/brand/shenling.png", brandName:"申菱" },
{ img:"/static/img/brand/shenling.png", brandName:"申菱" },
{ img:"/static/img/brand/shenling.png", brandName:"申菱" },
{ img:"/static/img/brand/shenling.png", brandName:"申菱" },
{ img:"/static/img/brand/shenling.png", brandName:"申菱" },
{ img:"/static/img/brand/shenling.png", brandName:"申菱" },
{ img:"/static/img/brand/shenling.png", brandName:"申菱" },
{ img:"/static/img/brand/shenling.png", brandName:"申菱" },
{ img:"/static/img/brand/shenling.png", brandName:"申菱" },
{ img:"/static/img/brand/shenling.png", brandName:"申菱" },
{ img:"/static/img/brand/shenling.png", brandName:"申菱" },
{ img:"/static/img/brand/shenling.png", brandName:"申菱" },
{ img:"/static/img/brand/shenling.png", brandName:"申菱" }
]
},
{
key: "Z", list: [
{ img:"/static/img/brand/zhuoli.png", brandName:"卓立" }
]
},
{
key: "3", list: [
{ img:"/static/img/brand/sanling.png", brandName:"三菱" },
]
}
]
};
},
onLoad: function onLoad(option) {
const _this = this;
uni.getSystemInfo({
success:function(res){
let windowHeight = res.windowHeight;
_this.indexHeight = windowHeight > 0 ? (windowHeight - 10) * 2 : 0;
}
});
},
methods: {
toSearch: function toSearch(e){
console.log('查询品牌名称', e)
this.brandList = [
{
key: "T", list: [
{ img:"/static/img/brand/tcl.png", brandName:"TCL" }
]
}
]
},
toBrand: function toBrand(e) {
console.log('选择品牌',e.detail);
}
}
};
.json
{
"navigationBarTitleText": "选择品牌",
"navigationBarBackgroundColor": "#f1f1f1",
"onReachBottomDistance": 50,
"usingComponents": {
"index-list": "../../../components/index-brand/index"
}
}
.wxml
<view>
<view class='wrapper' style="{{'height:'+indexHeight +'rpx;'}}">
<index-list data="{{brandList}}"
data-event-opts="{{[['brand',[['toBrand',['$event']]]]]}}" bindbrand="__e"></index-list>
</view>
</view>
.wxss
@charset "UTF-8";
page { background-color: #f1f1f1; }
.wrapper{
width: 100%;
background-color: #fff;
}