123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417 |
- import baseComponent from '../helpers/baseComponent'
- import classNames from '../helpers/libs/classNames'
- import { getSystemInfoSync } from '../helpers/hooks/useNativeAPI'
- import { useRect } from '../helpers/hooks/useDOM'
- const defaultStyle = 'transition: transform .4s; transform: translate3d(0px, 0px, 0px) scale(1);'
- baseComponent({
- properties: {
- prefixCls: {
- type: String,
- value: 'wux-refresher',
- },
- pullingIcon: {
- type: String,
- value: '',
- },
- pullingText: {
- type: String,
- value: '下拉刷新',
- },
- refreshingIcon: {
- type: String,
- value: '',
- },
- refreshingText: {
- type: String,
- value: '正在刷新',
- },
- disablePullingRotation: {
- type: Boolean,
- value: false,
- },
- distance: {
- type: Number,
- value: 30,
- },
- prefixLCls: {
- type: String,
- value: 'wux-loader',
- },
- isShowLoadingText: {
- type: Boolean,
- value: false,
- },
- loadingText: {
- type: String,
- value: '正在加载',
- },
- loadNoDataText: {
- type: String,
- value: '没有更多数据',
- },
- scrollTop: {
- type: Number,
- value: 0,
- observer: 'onScroll',
- },
- },
- data: {
- style: defaultStyle,
- visible: false,
- active: false,
- refreshing: false,
- tail: false,
- lVisible: false,
- noData: false, // 是否没有更多数据
- windowHeight: 0, // 窗口高度
- newContentHeight: 0, // 新节点内容高度
- oldContentHeight: 0, // 旧节点内容高度
- loading: false, // 判断是否正在加载
- },
- computed: {
- classes: ['prefixCls, pullingText, pullingIcon, disablePullingRotation, refreshingText, refreshingIcon, visible, active, refreshing, tail, prefixLCls, loading, noData', function(prefixCls, pullingText, pullingIcon, disablePullingRotation, refreshingText, refreshingIcon, visible, active, refreshing, tail, prefixLCls, loading, noData) {
- const wrap = classNames(prefixCls, {
- [`${prefixCls}--hidden`]: !visible,
- [`${prefixCls}--visible`]: visible,
- [`${prefixCls}--active`]: active,
- [`${prefixCls}--refreshing`]: refreshing,
- [`${prefixCls}--refreshing-tail`]: tail,
- })
- const cover = `${prefixCls}__cover`
- const content = classNames(`${prefixCls}__content`, {
- [`${prefixCls}__content--text`]: pullingText || refreshingText,
- })
- const iconPulling = classNames(`${prefixCls}__icon-pulling`, {
- [`${prefixCls}__icon-pulling--disabled`]: disablePullingRotation,
- })
- const textPulling = `${prefixCls}__text-pulling`
- const iconRefreshing = `${prefixCls}__icon-refreshing`
- const textRefreshing = `${prefixCls}__text-refreshing`
- const pIcon = pullingIcon || `${prefixCls}__icon--arrow-down`
- const rIcon = refreshingIcon || `${prefixCls}__icon--refresher`
- const lWrap = classNames(prefixLCls, {
- [`${prefixLCls}--hidden`]: !loading,
- [`${prefixLCls}--visible`]: loading,
- [`${prefixLCls}--end`]: noData,
- })
- const lContent = `${prefixLCls}__content`
- const loadingText = `${prefixLCls}__text-loading`
- return {
- wrap,
- cover,
- content,
- iconPulling,
- textPulling,
- iconRefreshing,
- textRefreshing,
- pIcon,
- rIcon,
- lWrap,
- lContent,
- loadingText,
- }
- }],
- },
- methods: {
- /**
- * 显示
- */
- activate() {
- this.setData({
- style: defaultStyle,
- visible: true,
- })
- },
- /**
- * 隐藏
- */
- deactivate() {
- if (this.activated) this.activated = false
- this.setData({
- style: defaultStyle,
- visible: false,
- active: false,
- refreshing: false,
- tail: false,
- })
- },
- /**
- * 正在刷新
- */
- refreshing() {
- this.setData({
- style: 'transition: transform .4s; transform: translate3d(0, 50px, 0) scale(1);',
- visible: true,
- active: true,
- refreshing: true,
- // 刷新时重新初始化加载状态
- loading: false,
- noData: false,
- newContentHeight: 0,
- oldContentHeight: 0,
- lVisible: false,
- })
- },
- /**
- * 刷新后隐藏动画
- */
- tail() {
- this.setData({
- visible: true,
- active: true,
- refreshing: true,
- tail: true,
- })
- },
- /**
- * 加载后隐藏动画
- */
- hide() {
- this.setData({
- lVisible: false,
- })
- },
- /**
- * 正在下拉
- * @param {Number} diffY 距离
- */
- translate(diffY) {
- const style = `transition-duration: 0s; transform: translate3d(0, ${diffY}px, 0) scale(1);`
- const className = diffY < this.data.distance ? 'visible' : 'active'
- this.setData({
- style,
- [className]: true,
- })
- },
- /**
- * 判断是否正在刷新
- */
- isRefreshing() {
- return this.data.refreshing
- },
- /**
- * 判断是否正在加载
- */
- isLoading() {
- return this.data.loading
- },
- /**
- * 获取触摸点坐标
- */
- getTouchPoints(e, index = 0) {
- const { pageX: x, pageY: y } = e.touches[index] || e.changedTouches[index]
- return {
- x,
- y,
- }
- },
- /**
- * 获取触摸移动方向
- */
- getSwipeDirection(x1, x2, y1, y2) {
- return Math.abs(x1 - x2) >= Math.abs(y1 - y2) ? (x1 - x2 > 0 ? 'Left' : 'Right') : (y1 - y2 > 0 ? 'Up' : 'Down')
- },
- /**
- * 创建定时器
- */
- requestAnimationFrame(callback) {
- let currTime = new Date().getTime()
- let timeToCall = Math.max(0, 16 - (currTime - this.lastTime))
- let timeout = setTimeout(() => {
- callback.bind(this)(currTime + timeToCall)
- }, timeToCall)
- this.lastTime = currTime + timeToCall
- return timeout
- },
- /**
- * 清空定时器
- */
- cancelAnimationFrame(timeout) {
- clearTimeout(timeout)
- },
- /**
- * 下拉刷新完成后的函数
- */
- finishPullToRefresh() {
- setTimeout(() => {
- this.requestAnimationFrame(this.tail)
- setTimeout(() => this.deactivate(), 200)
- }, 200)
- },
- /**
- * 上拉加载完成后的函数
- */
- finishLoadmore(isEnd) {
- if (isEnd === true) {
- setTimeout(() => {
- this.setData({
- noData: true,
- loading: false,
- })
- }, 200)
- } else {
- setTimeout(() => {
- this.setData({
- noData: false,
- loading: false,
- })
- this.requestAnimationFrame(this.hide)
- setTimeout(() => this.deactivate(), 200)
- }, 200)
- }
- },
- /**
- * 手指触摸动作开始
- */
- onTouchStart(e) {
- if (this.isRefreshing() || this.isLoading()) return
- this.start = this.getTouchPoints(e)
- this.diffX = this.diffY = 0
- this.isMoved = false
- this.direction = false
- this.activate()
- },
- /**
- * 手指触摸后移动
- */
- onTouchMove(e) {
- if (!this.start || this.isRefreshing() || this.isLoading()) return
- if (!this.isMoved) {
- this.isMoved = true
- }
- this.move = this.getTouchPoints(e)
- this.diffX = this.move.x - this.start.x
- this.diffY = this.move.y - this.start.y
- this.direction = this.getSwipeDirection(this.start.x, this.move.x, this.start.y, this.move.y)
- if (this.diffY < 0 || this.direction !== 'Down') return
- this.diffY = Math.pow(this.diffY, 0.8)
- this.triggerPull(this.diffY)
- },
- /**
- * 手指触摸动作结束
- */
- onTouchEnd(e) {
- if (!this.isMoved) return
- this.start = false
- this.isMoved = false
- if (this.diffY <= 0 || this.direction !== 'Down' || this.isRefreshing() || this.isLoading()) return
- this.triggerRefresh(this.diffY)
- },
- /**
- * Pulling
- */
- triggerPull(diffY) {
- const { distance } = this.data
- if (!this.activated && diffY > distance) {
- this.activated = true
- this.triggerEvent('pulling')
- } else if (this.activated && diffY < distance) {
- this.activated = false
- }
- this.translate(diffY)
- },
- /**
- * Refresh
- */
- triggerRefresh(diffY = this.data.distance) {
- this.triggerPull(diffY)
- this.deactivate()
- if (Math.abs(diffY) >= this.data.distance) {
- this.refreshing()
- this.triggerEvent('refresh')
- }
- },
- /**
- * Scrool
- */
- onScroll(n) {
- // disabled scroll func
- if (this.isMoved) return
- // 获取节点高度
- useRect(`#${this.id}`)
- .then((res) => {
- const newContentHeight = res.height
- if (this.data.newContentHeight !== newContentHeight) {
- this.setData({ newContentHeight })
- }
- const {
- oldContentHeight,
- windowHeight,
- distance,
- loading,
- noData,
- } = this.data
- if (windowHeight && !this.isRefreshing()) {
- // 到临界点时触发上拉加载
- // 防止节点高度一致时引发重复加载
- if (
- n > newContentHeight - windowHeight - (distance * 1.5) &&
- loading === false &&
- noData === false && newContentHeight !== oldContentHeight
- ) {
- this.setData({
- loading: true,
- refreshing: false,
- oldContentHeight: newContentHeight,
- })
- this.triggerEvent('loadmore')
- } else if (
- loading === false &&
- noData === false
- ) {
- // 隐藏上拉加载动画
- this.hide()
- } else if (loading === true) {
- // 如果在加载中,保持内容的高度一致,以此来防止临界点重复加载
- this.setData({
- oldContentHeight: newContentHeight,
- })
- }
- this.deactivate()
- }
- })
- },
- noop() {},
- },
- created() {
- this.lastTime = 0
- this.activated = false
- },
- attached() {
- const windowHeight = getSystemInfoSync(['window']).windowHeight
- if (this.data.windowHeight !== windowHeight) {
- this.setData({
- windowHeight,
- })
- }
- },
- })
|