1.实现效果
2.实现原理
scroll-view:可滚动视图区域。使用竖向滚动时,需要给scroll-view一个固定高度,通过 WXSS 设置 height。组件属性的长度单位默认为px,2.4.0起支持传入单位(rpx/px)。
scroll-into-view:值应为某子元素id(id不能以数字开头)。设置哪个方向可滚动,则在哪个方向滚动到该元素
1.为列表中的字母索引添加相应id,当触摸右侧固定侧边栏时候,动态设置当前id值,实现锚点定位。
2.搜索城市时,隐藏其他数据,根据搜索值去匹配(indexOf)去匹配关键字,将结果push到一个新的数组中,展示该数组数据。
3.catchtouchstart,catchtouchmove,catchtouchend,catchtouchcancel实现触摸滑动一系列过程。
1. 当触摸开始,移动时,拿到pageY,距离左上角的高度。
2. 当前选中位置高度Height=pageY-侧边栏本身距离左上角高度(res.windowWidth / 750 * 180 (rpx,该高度为top:180rpx))
3. 当前选中索引位置为Index=Math.floor(Height/(每一项字母的高度))
4. 每一项字母的高度=res.windowHeight- res.windowWidth / 750 * 300(300为侧边栏页面空余高度)/总共字母数
3.主要代码
<view class="head {{search&&'r_head'}}">
<!-- 搜索框 -->
<view class="flex-row">
<view class="head_input">
<image src="/img/search_icon.png" class="search_icon"></image>
<input type="text" placeholder="搜索" placeholder-class="place_holder" bindinput="getValue" value="{{search}}"></input>
</view>
<view class="sha_icon" catchtap="clear_input">取消</view>
</view>
<view class="flex-row head_curr" wx:if="{{!search}}">
<image src="/img/add_icon.png" class="h_c_icon" />
<view>当前定位城市:{{current_city}}</view>
</view>
</view>
<scroll-view wx:if="{{!search}}" scroll-y="true" class="sy_container" scroll-into-view="{{scrollViewId}}">
<view class="hot_city">
<view class="title">热门城市</view>
<view class="flex-row flex-wrap box">
<block wx:for="{{hot_city}}" wx:key="hot">
<view class="name" hover-class="sel_city" hover-stay-time="150">{{item.name}}</view>
</block>
</view>
</view>
<view class="all_city">
<view wx:for="{{city_list}}" wx:key="city_list" wx:if="{{item.data.length>0}}">
<view class="letter_name" id="{{item.letter}}">{{item.letter}}</view>
<view class="city">
<block wx:for="{{item.data}}" wx:key="data" wx:for-index="index0" wx:for-item="item0">
<view class="name flex-row" hover-class="city_hover" hover-stay-time='150'>{{item0.cityName}}</view>
</block>
</view>
</view>
</view>
</scroll-view>
<!-- 侧边选择索引 -->
<view wx:if="{{!search}}">
<view class="fixed_bar" style="height: {{barHeight}}px;" catchtouchstart="touchStart" catchtouchmove="touchMove" catchtouchend="touchEnd" catchtouchcancel="touchCancel">
<view wx:for="{{city_list}}" wx:key="index" style="height: {{barHeight/22}}px;">
<view class="bar_item flex-column j_c {{curr==index&&'bar_item_active'}}" style="width: {{barHeight/22*0.75}}px;height: {{barHeight/22*0.75}}px;">{{item.letter}}</view>
</view>
</view>
<view wx:if="{{showLetter &&city_list[curr].letter}}" class="fixed_letter">{{city_list[curr].letter}}</view>
</view>
<view wx:if="{{search}}" class="result_list">
<view wx:if="{{result.length>0}}">
<block wx:for="{{result}}" wx:key="result">
<view class="r_item" hover-stay-time='150' hover-class="r_item_hover">{{item.name}}</view>
</block>
</view>
<view wx:else class="flex-column no_data">
<image src="https://i.postimg.cc/7P00ckMG/image.png" />
<view>请输入正确的城市名称呢</view>
</view>
</view>
/* pages/jsCase/citySel/index.wxss */
page {
height: 100%;
background: #f2f5f7;
}
.head {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 170rpx;
box-sizing: border-box;
padding: 3rpx 20rpx 0;
background: #fff;
z-index: 9999;
}
.r_head {
height: 90rpx;
}
.head_input {
position: relative;
flex: 1;
}
.search_icon {
position: absolute;
top: 50%;
left: 0;
margin-top: -15rpx;
width: 28rpx;
height: 30rpx;
padding-left: 23rpx;
}
.head input {
height: 60rpx;
padding-left: 75rpx;
font-size: 28rpx;
border-radius: 30rpx;
background: #f2f5f7;
}
.place_holder {
font-size: 28rpx;
color: #999999;
}
.sha_icon {
margin-left: 18rpx;
font-size: 28rpx;
color: #333333;
}
.head_curr {
height: 100rpx;
color: #333;
font-size: 28rpx;
padding-top: 20rpx;
box-sizing: border-box;
}
.h_c_icon {
flex-shrink: 0;
margin-right: 15rpx;
width: 40rpx;
height: 40rpx;
}
.sy_container {
box-sizing: border-box;
width: 100%;
height: 100%;
padding-top: 170rpx;
}
.hot_city .title {
padding: 10rpx 30rpx;
color: #999;
font-size: 26rpx;
}
.hot_city .box {
background-color: #fff;
padding: 5rpx 30rpx 15rpx 40rpx;
box-sizing: border-box;
}
.hot_city .box .name {
vertical-align: top;
display: inline-block;
min-width: 140rpx;
line-height: 56rpx;
height: 56rpx;
border-radius: 28rpx;
font-size: 28rpx;
color: #333;
text-align: center;
padding: 0 20rpx;
box-sizing: border-box;
margin-top: 10rpx;
margin-right: 20rpx;
position: relative;
}
.hot_city .box .name::after {
content: '';
position: absolute;
width: 200%;
height: 200%;
-webkit-transform-origin: 0 0;
transform-origin: 0 0;
-webkit-transform: scale(0.5, 0.5);
transform: scale(0.5, 0.5);
-webkit-box-sizing: border-box;
box-sizing: border-box;
left: 0;
top: 0;
border-radius: 56rpx;
border: 1rpx solid rgb(235, 225, 225);
}
.sel_city {
color: #fff !important;
background: pink;
}
.all_city .letter_name {
height: 48rpx;
font-size: 24rpx;
color: #999;
background: #f2f5f7;
padding: 0 30rpx;
line-height: 48rpx;
}
.all_city .city {
background-color: #fff;
}
.all_city .city .name {
width: 100%;
padding: 30rpx;
font-size: 28rpx;
color: #333;
position: relative;
overflow: hidden;
}
.all_city .city .name::after {
content: '';
position: absolute;
border-bottom: 1rpx solid #eaeef1;
-webkit-transform: scaleY(0.5);
transform: scaleY(0.5);
bottom: 0;
right: 0;
left: 30rpx;
}
.city_hover {
background-color: #eee !important;
}
.fixed_bar {
position: fixed;
z-index: 999;
top: 180rpx;
right: 0px;
padding-right: 10rpx;
width: 50rpx;
font-size: 22rpx;
text-align: center;
}
.bar_item {
background-color: rgb(233, 228, 220);
border-radius: 50%;
}
@media screen and (max-width: 320px) {
.fixed_bar {
font-size: 20rpx;
}
}
.bar_item_active {
background-color: #fff;
box-shadow: 5rpx 5rpx 5rpx #f7c3ee;
}
.fixed_letter {
position: absolute;
z-index: 20;
width: 160rpx;
height: 160rpx;
left: 50%;
top: 50%;
margin-left: -80rpx;
margin-top: -80rpx;
border-radius: 80rpx;
text-align: center;
line-height: 160rpx;
font-size: 70rpx;
color: #fff;
background-color: rgba(0, 0, 0, 0.5);
box-shadow: 5rpx 5rpx 5rpx #f7c3ee;
}
/* 搜索结果 */
.result_list {
padding-top: 90rpx;
background: #fff;
width: 100%;
}
.r_item {
width: 100%;
position: relative;
padding: 30rpx 0 30rpx 30rpx;
font-size: 28rpx;
color: #333;
box-sizing: border-box;
}
.r_item::after {
content: '';
position: absolute;
border-bottom: 1rpx solid #eaeef1;
-webkit-transform: scaleY(0.5);
transform: scaleY(0.5);
bottom: 0;
right: 0;
left: 30rpx;
}
.r_item_hover {
background-color: #eee !important;
}
.no_data {
height: 500rpx;
justify-content: center;
font-size: 27rpx;
color: #999;
}
.no_data image {
width: 250rpx;
height: 162rpx;
margin-bottom: 30rpx;
}
const { cityData } = require('./city.js')
Page({
data: {
current_city: "",
search: "",
hot_city: [
{
name: "北京"
}, {
name: "上海"
}, {
name: "广州"
}, {
name: "长沙"
}, {
name: "张家口"
}, {
name: "杭州"
}, {
name: "西安"
}, {
name: "南京"
}, {
name: "苏州"
},
],
city_list: [],
barHeight: 0,
curr: -1,
scrollViewId: "",
barTop: 0,
showLetter: false,
result: [],//搜索结果
},
onLoad: function (options) {
this.setData({
current_city: options.currentCity || "南京",
city_list: cityData
})
wx.getSystemInfo({
success: (res) => {
let winHeight = res.windowHeight
let barHeight = winHeight - res.windowWidth / 750 * 300;
this.setData({
barHeight: barHeight,
barTop: res.windowWidth / 750 * 180,
})
}
})
},
/**
* 获取value值
* @param {*} e
*/
getValue(e) {
this.setData({
search: e.detail.value
}, () => {
this.search(e.detail.value)
})
},
/**
* 搜索成功
*/
search(e) {
let result = [], { city_list } = this.data;
city_list.forEach((item1) => {
item1.data.forEach((item2) => {
if (item2.keyword.indexOf(e.toLocaleUpperCase()) !== -1) {
result.push({ name: item2.cityName })
}
})
})
this.setData({
result,
})
},
/**
* 清空验证码
*/
clear_input() {
this.setData({
search: ""
})
},
......
})