使用組件
<Consult consultHeight={consultHeight} />
index.jsx
import React, { useState, useEffect } from "react"
import Taro from "@tarojs/taro"
import { View, Image, ScrollView } from "@tarojs/components"
import { AtTabs, AtTabsPane, AtLoadMore } from "taro-ui"
import defaultIcon from "@images/ic_default.png"
import chuancai from "@images/top/ic_chuancai.svg"
import jiangzhecai from "@images/top/ic_jiangzhecai.svg"
import recommend from "@images/top/ic_recommend.svg"
import Http from "@/utils/http"
import { getPageHeight } from "@/utils/index"
import "./index.scss"
var touchStartX = 0 //觸摸時(shí)的原點(diǎn)
var touchStartY = 0 //觸摸時(shí)的原點(diǎn)
var time = 0 // 時(shí)間記錄,用于滑動(dòng)時(shí)且時(shí)間小于1s則執(zhí)行左右滑動(dòng)
var interval = "" // 記錄/清理時(shí)間記錄
var touchEndX = 0 // x軸方向移動(dòng)的距離
var touchEndY = 0 // y軸方向移動(dòng)的距離
var topHeight = 0
export default function Consult({ consultHeight }) {
const [tabList, setTabList] = useState([{ key: "top", title: "熱門(mén)餐廳" }])
const [currentTab, setTabCurrent] = useState(0) //當(dāng)前tab
const [scrollMove, setScrollMove] = useState(0) //默認(rèn)0向下1向上2
const [currentLocation, setcCurrentLocation] = useState("END") //當(dāng)前熱門(mén)餐廳card位置
const [restaList, setRestaList] = useState([])
const [pageData, setPageData] = useState({
current: 0,
pageSize: 10,
total: 0,
})
const [loadStatus, setLoadStatus] = useState("more")
const [animationData, setAnimationData] = useState({})
const [criticalValue, setVriticalValue] = useState(120)
const duration = 500,
animation = Taro.createAnimation({
duration: 500,
timingFunction: "ease",
})
const handleClick = c => {
setTabCurrent(c)
getRestaurantList({ typeCode: tabList[c].key }, true)
}
// 觸摸開(kāi)始事件
const touchStart = function (e) {
if (!restaList.length) return
touchStartX = e.changedTouches[0].pageX // 獲取觸摸時(shí)的原點(diǎn)
touchStartY = e.changedTouches[0].pageY // 獲取觸摸時(shí)的原點(diǎn)
// 使用js計(jì)時(shí)器記錄時(shí)間
interval = setInterval(function () {
time++
}, 100)
}
// 觸摸結(jié)束事件
const touchEnd = function (e) {
if (!restaList.length) return
touchEndX = e.changedTouches[0].pageX // 獲取觸摸時(shí)的原點(diǎn)
touchEndY = e.changedTouches[0].pageY // 獲取觸摸時(shí)的原點(diǎn)
var moveX = touchEndX - touchStartX
var moveY = touchEndY - touchStartY
if (Math.sign(moveX) == -1) {
moveX = moveX * -1
}
if (Math.sign(moveY) == -1) {
moveY = moveY * -1
}
if (moveX <= moveY) {
// 上下
// 向上滑動(dòng)
if (touchEndY - touchStartY <= -30 && time < 10) {
console.log("向上滑動(dòng)")
setScrollMove(2)
}
// 向下滑動(dòng)
if (touchEndY - touchStartY >= 30 && time < 10) {
console.log("向下滑動(dòng)")
const query = Taro.createSelectorQuery()
query.select(".restaurant-card").boundingClientRect()
query.exec(function (res) {
console.log(res[0].top)
if (res[0].top >= criticalValue) {
setScrollMove(1)
}
})
}
}
clearInterval(interval) // 清除setInterval
time = 0
}
// 熱門(mén)餐廳上拉動(dòng)畫(huà)
const moveToTop = () => {
animation.height(topHeight).step({
duration: duration,
timingFunction: "ease",
})
setcCurrentLocation("TOP")
setScrollMove(0)
setAnimationData(animation.export())
}
// 熱門(mén)餐廳下滑動(dòng)畫(huà)
const moveToEnd = () => {
animation.height(consultHeight).step({
duration: duration,
timingFunction: "ease",
})
setcCurrentLocation("END")
setScrollMove(0)
setAnimationData(animation.export())
}
// 獲取餐廳類型
const getRestaurantType = async () => {
const res = await Http.get("/diet/public/dict/restaurantType")
let types = res.data.map(r => {
r.title = r.value
return r
})
setTabList([...tabList, ...types])
}
// 獲取餐廳列表
const getRestaurantList = (query, tag) => {
setRestaList([])
setLoadStatus("loading")
setVriticalValue(120)
const params = {
typeCode: "top",
current: 1,
pageSize: pageData.pageSize,
...query,
}
Http.get("/diet/restaurant/top", params).then(({ data }) => {
const { list, current, pageSize, total } = data
if (list.length) {
let arr = tag ? [...list] : [...restaList, ...list]
setRestaList(arr)
setPageData({
current: current,
pageSize: pageSize,
total: total,
})
setLoadStatus(total <= pageSize * current ? "noMore" : "more")
} else {
setLoadStatus("noMore")
}
})
}
const goToRestaurant = resa => {
Taro.navigateTo({ url: `/pages/restaurantMenus/index?title=${resa.name}&id=${resa.id}` })
}
const handleInit = async () => {
let pageHeight = await getPageHeight()
topHeight = pageHeight - pageHeight * 0.2
getRestaurantType()
getRestaurantList({ typeCode: "top", current: 1 }, false)
}
useEffect(() => {
handleInit()
}, [])
useEffect(() => {
if (currentLocation === "END" && scrollMove === 2) {
moveToTop()
} else if (currentLocation === "TOP" && scrollMove === 1) {
moveToEnd()
}
}, [scrollMove, currentLocation])
useEffect(() => {
moveToEnd()
}, [consultHeight])
const onScroll = e => {
setVriticalValue(260)
}
const renderRestaurant = resta => {
return (
<View
className="restaurant-card"
key={resta.id}
onClick={() => {
goToRestaurant(resta)
}}
>
<View className="card-title">
<View className="title-name">
<Image className="res-icon" src={resta.img ?? defaultIcon} />
<View className="res-sum">
<View className="res-name">{resta.name}</View>
<View className="res-desc">
<Image className="desc-icon" src={resta.cuisineCode === "jiangzhecai" ? jiangzhecai : chuancai} />
{resta.cuisine}
</View>
</View>
</View>
<View className="title-like">
<View className="like-desc">
<Image className="desc-icon" src={recommend} />
{resta.recommendScore}+
</View>
<View className="like-type">糖友推薦</View>
</View>
</View>
{resta.topFoods.length ? (
<>
<View className="card-divider" />
<View className="card-eated">
<View className="eated-title">糖友吃過(guò)</View>
<View className="eated-list">
{resta.topFoods.map(f => (
<Image className="list-icon" key={f.id} src={f.img} />
))}
</View>
</View>
</>
) : (
""
)}
</View>
)
}
return (
<View className="hot-restaurant" onTouchStart={touchStart} onTouchEnd={touchEnd}>
<AtTabs current={currentTab} tabList={tabList} onClick={handleClick} scroll>
{tabList.map((tab, index) => {
return (
<AtTabsPane current={currentTab} key={tab.key} index={index}>
<ScrollView
animation={animationData}
lowerThreshold={100}
onScrollToLower={() => {
if (restaList.length < pageData.total) {
getRestaurantList({ typeCode: tabList[currentTab].key, current: pageData.current + 1 }, false)
}
}}
scrollY={currentLocation === "TOP"}
className="hot-content"
style={{ height: consultHeight }}
onScroll={onScroll}
>
{restaList.map(resta => renderRestaurant(resta))}
<AtLoadMore status={loadStatus} />
</ScrollView>
</AtTabsPane>
)
})}
</AtTabs>
</View>
)
}
index.scss
.hot-restaurant {
box-sizing: border-box;
background-color: #fbfbfb;
width: 100%;
// min-height: 530rpx;
max-height: 100vh;
border-radius: 40rpx 40rpx 0 0;
position: fixed;
padding: 30rpx 48rpx 0 48rpx;
bottom: 0;
left: 0;
background: linear-gradient(180deg, #ffffff 0%, #ffffff 39%, #f2f2f2 100%);
box-shadow: 0rpx -12rpx 24rpx 0rpx rgba(0, 0, 0, 0.02);
z-index: 2;
}
.at-tabs__header {
text-align: left;
background-color: transparent;
margin-left: -24rpx;
}
.at-tabs__item {
font-size: 28rpx;
font-family: PingFang SC-Regular, PingFang SC;
font-weight: 400;
color: #555555;
}
.at-tabs__underline {
display: none;
}
.at-tabs__item-underline {
width: 76%;
margin-left: 12%;
height: 8rpx;
background-color: #ff941a;
bottom: 27rpx;
opacity: 0.7;
}
.at-tabs__item--active {
font-size: 36rpx;
font-family: PingFang SC-Medium, PingFang SC;
font-weight: 500;
color: #181818;
}
.hot-content {
box-sizing: border-box;
padding: 10rpx 0rpx 0rpx 0rpx;
text-align: center;
.at-button--full {
border: none;
font-size: 28rpx;
}
}
.restaurant-card {
box-sizing: border-box;
width: 100%;
// height: 336rpx;
background: #ffffff;
border-radius: 32rpx;
padding: 24rpx;
margin-bottom: 24rpx;
box-shadow: 0rpx 8rpx 15rpx 0rpx rgba(0, 0, 0, 0.05);
}
.card-title {
height: 50%;
display: flex;
justify-content: center;
align-items: center;
}
.title-name {
flex-shrink: 1;
height: 100%;
flex: 1;
display: flex;
justify-content: flex-start;
}
.res-icon {
background-color: #f5f5f5;
width: 120rpx;
height: 120rpx;
border-radius: 20rpx;
margin-right: 16rpx;
}
.res-sum {
height: 120rpx;
font-size: 34rpx;
font-family: PingFang SC-Regular, PingFang SC;
font-weight: 400;
color: #181818;
display: flex;
flex-direction: column;
justify-content: center;
align-items: flex-start;
}
.res-name {
width: 280rpx;
text-align: left;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.res-desc {
font-size: 26rpx;
font-family: PingFang SC-Regular, PingFang SC;
font-weight: 400;
color: #555555;
display: flex;
justify-content: flex-start;
align-items: center;
margin-top: 4rpx;
}
.desc-icon {
width: 32rpx;
height: 32rpx;
margin-right: 4rpx;
}
.title-like {
width: 172rpx;
height: 104rpx;
background: #f8f8f8;
border-radius: 20rpx 20rpx 20rpx 20rpx;
opacity: 1;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.like-type {
font-size: 28rpx;
font-family: PingFang SC-Regular, PingFang SC;
font-weight: 400;
color: #555555;
}
.like-desc {
font-size: 28rpx;
font-family: PingFang SC-Regular, PingFang SC;
font-weight: 400;
color: #555555;
display: flex;
justify-content: center;
align-items: center;
}
.card-divider {
width: 100%;
height: 0rpx;
opacity: 0.7;
border-bottom: 1rpx solid #e1e1e1;
}
.card-eated {
height: 50%;
}
.eated-title {
font-size: 24rpx;
font-family: PingFang SC-Regular, PingFang SC;
font-weight: 400;
color: #555555;
text-align: left;
margin: 10rpx 0;
}
.eated-list {
display: flex;
justify-content: flex-start;
}
.list-icon {
width: 88rpx;
height: 88rpx;
}