Skip to content

Commit 6d3e0b0

Browse files
committed
fix(SwipeAction): 修复 SwipeAction 滑动卡顿的问题
1 parent c601ebf commit 6d3e0b0

File tree

5 files changed

+136
-170
lines changed

5 files changed

+136
-170
lines changed

packages/taro-ui/config/rollup.config.js

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -29,17 +29,6 @@ export default {
2929
file: resolveFile(Package.module),
3030
format: 'es',
3131
sourcemap: true
32-
},
33-
{
34-
file: resolveFile(Package.browser),
35-
format: 'umd',
36-
name: 'taro-ui',
37-
sourcemap: true,
38-
globals: {
39-
react: 'React',
40-
'@tarojs/components': 'components',
41-
'@tarojs/taro': 'Taro'
42-
}
4332
}
4433
],
4534
external: externalPackages,

packages/taro-ui/package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
"name": "taro-ui",
33
"version": "3.0.0-alpha.3",
44
"description": "UI KIT for Taro",
5-
"browser": "dist/index.umd.js",
65
"module": "dist/index.esm.js",
76
"main": "dist/index.js",
87
"source": "src/index.ts",

packages/taro-ui/src/components/swipe-action/index.tsx

Lines changed: 128 additions & 158 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,14 @@
11
import classNames from 'classnames'
2-
import _inRange from 'lodash/inRange'
3-
import _isEmpty from 'lodash/isEmpty'
42
import PropTypes, { InferProps } from 'prop-types'
53
import React from 'react'
6-
import { Text, View } from '@tarojs/components'
7-
import { CommonEvent, ITouchEvent } from '@tarojs/components/types/common'
4+
import { Text, View, MovableArea, MovableView } from '@tarojs/components'
5+
import { CommonEvent } from '@tarojs/components/types/common'
86
import {
97
AtSwipeActionProps,
108
AtSwipeActionState,
119
SwipeActionOption
1210
} from '../../../types/swipe-action'
13-
import {
14-
delayGetClientRect,
15-
delayGetScrollOffset,
16-
uuid
17-
} from '../../common/utils'
11+
import { delayQuerySelector, uuid } from '../../common/utils'
1812
import AtSwipeActionOptions from './options/index'
1913

2014
export default class AtSwipeAction extends React.Component<
@@ -24,48 +18,22 @@ export default class AtSwipeAction extends React.Component<
2418
public static defaultProps: AtSwipeActionProps
2519
public static propTypes: InferProps<AtSwipeActionProps>
2620

27-
private endValue: number
28-
private startX: number
29-
private startY: number
3021
private maxOffsetSize: number
31-
private domInfo: any
32-
private isMoving: boolean
33-
private isTouching: boolean
22+
private moveX: number
23+
private eleWidth: number
3424

3525
public constructor(props: AtSwipeActionProps) {
3626
super(props)
3727
const { isOpened } = props
38-
this.endValue = 0
39-
this.startX = 0
40-
this.startY = 0
4128
this.maxOffsetSize = 0
42-
this.domInfo = {
43-
top: 0,
44-
bottom: 0,
45-
left: 0,
46-
right: 0
47-
}
48-
this.isMoving = false
49-
this.isTouching = false
5029
this.state = {
5130
componentId: uuid(),
5231
offsetSize: 0,
53-
_isOpened: !!isOpened
32+
_isOpened: !!isOpened,
33+
needAnimation: false
5434
}
55-
}
56-
57-
private getDomInfo(): Promise<void> {
58-
return Promise.all([
59-
delayGetClientRect({
60-
delayTime: 0,
61-
selectorStr: `#swipeAction-${this.state.componentId}`
62-
}),
63-
delayGetScrollOffset({ delayTime: 0 })
64-
]).then(([rect, scrollOffset]) => {
65-
rect[0].top += scrollOffset[0].scrollTop
66-
rect[0].bottom += scrollOffset[0].scrollTop
67-
this.domInfo = rect[0]
68-
})
35+
this.moveX = 0
36+
this.eleWidth = 0
6937
}
7038

7139
public UNSAFE_componentWillReceiveProps(nextProps: AtSwipeActionProps): void {
@@ -77,31 +45,49 @@ export default class AtSwipeAction extends React.Component<
7745
}
7846
}
7947

80-
private _reset(isOpened: boolean): void {
81-
this.isMoving = false
82-
this.isTouching = false
48+
public componentDidMount(): void {
49+
if (this.eleWidth === 0) {
50+
delayQuerySelector(`#swipeAction-${this.state.componentId}`, 0).then(
51+
res => {
52+
if (res[0]) {
53+
this.eleWidth = res[0].width
54+
}
55+
}
56+
)
57+
}
58+
}
8359

60+
public componentDidUpdate(): void {
61+
delayQuerySelector(`#swipeAction-${this.state.componentId}`, 0).then(
62+
res => {
63+
if (res[0]) {
64+
this.eleWidth = res[0].width
65+
}
66+
}
67+
)
68+
}
69+
70+
private _reset(isOpened: boolean): void {
8471
if (isOpened) {
85-
this.endValue = -this.maxOffsetSize
8672
this.setState({
8773
_isOpened: true,
88-
offsetSize: -this.maxOffsetSize
74+
offsetSize: 0
8975
})
9076
} else {
91-
this.endValue = 0
92-
this.setState({
93-
offsetSize: 0,
94-
_isOpened: false
95-
})
77+
this.setState(
78+
{
79+
offsetSize: this.moveX
80+
},
81+
() => {
82+
this.setState({
83+
offsetSize: this.maxOffsetSize,
84+
_isOpened: false
85+
})
86+
}
87+
)
9688
}
9789
}
9890

99-
private computeTransform = (value: number): string | null =>
100-
// if (Taro.getEnv() === Taro.ENV_TYPE.ALIPAY) {
101-
// return !_isNil(value) ? `translate3d(${value}px,0,0)` : null
102-
// }
103-
value ? `translate3d(${value}px,0,0)` : null
104-
10591
private handleOpened = (event: CommonEvent): void => {
10692
const { onOpened } = this.props
10793
if (typeof onOpened === 'function' && this.state._isOpened) {
@@ -116,78 +102,16 @@ export default class AtSwipeAction extends React.Component<
116102
}
117103
}
118104

119-
private handleTouchStart = (e: ITouchEvent): void => {
120-
const { clientX, clientY } = e.touches[0]
121-
122-
if (this.props.disabled) return
123-
124-
this.getDomInfo()
125-
126-
this.startX = clientX
127-
this.startY = clientY
128-
this.isTouching = true
129-
}
130-
131-
private handleTouchMove = (e: ITouchEvent): void => {
132-
if (_isEmpty(this.domInfo)) {
133-
return
134-
}
135-
136-
const { startX, startY } = this
137-
const { top, bottom, left, right } = this.domInfo
138-
const { clientX, clientY, pageX, pageY } = e.touches[0]
139-
140-
const x = Math.abs(clientX - startX)
141-
const y = Math.abs(clientY - startY)
142-
143-
const inDom = _inRange(pageX, left, right) && _inRange(pageY, top, bottom)
144-
145-
if (!this.isMoving && inDom) {
146-
this.isMoving =
147-
y === 0 ||
148-
x / y >= Number.parseFloat(Math.tan((45 * Math.PI) / 180).toFixed(2))
149-
}
150-
151-
if (this.isTouching && this.isMoving) {
152-
e.preventDefault()
153-
154-
const offsetSize = clientX - this.startX
155-
const isRight = offsetSize > 0
156-
157-
if (this.state.offsetSize === 0 && isRight) return
158-
159-
const value = this.endValue + offsetSize
160-
this.setState({
161-
offsetSize: value >= 0 ? 0 : value
162-
})
163-
}
164-
}
165-
166-
private handleTouchEnd = (event: ITouchEvent): void => {
167-
this.isTouching = false
168-
169-
const { offsetSize } = this.state
170-
171-
this.endValue = offsetSize
172-
173-
const breakpoint = this.maxOffsetSize / 2
174-
const absOffsetSize = Math.abs(offsetSize)
175-
176-
if (absOffsetSize > breakpoint) {
177-
this._reset(true)
178-
this.handleOpened(event)
179-
return
180-
}
181-
182-
this._reset(false) // TODO: Check behavior
183-
this.handleClosed(event)
184-
}
185-
186105
private handleDomInfo = ({ width }: { width: number }): void => {
187106
const { _isOpened } = this.state
188107

189108
this.maxOffsetSize = width
190109
this._reset(_isOpened)
110+
setTimeout(() => {
111+
this.setState({
112+
needAnimation: true
113+
})
114+
}, 0)
191115
}
192116

193117
private handleClick = (
@@ -206,51 +130,97 @@ export default class AtSwipeAction extends React.Component<
206130
}
207131
}
208132

133+
onTouchEnd = e => {
134+
if (this.moveX === 0) {
135+
this._reset(true)
136+
this.handleOpened(e)
137+
return
138+
}
139+
if (this.moveX === this.maxOffsetSize) {
140+
this._reset(false)
141+
this.handleClosed(e)
142+
return
143+
}
144+
if (this.state._isOpened && this.moveX > 0) {
145+
this._reset(false)
146+
this.handleClosed(e)
147+
return
148+
}
149+
if (this.maxOffsetSize - this.moveX < this.maxOffsetSize * 0.4) {
150+
this._reset(false)
151+
this.handleClosed(e)
152+
} else {
153+
this._reset(true)
154+
this.handleOpened(e)
155+
}
156+
}
157+
158+
onChange = e => {
159+
this.moveX = e.detail.x
160+
}
161+
209162
public render(): JSX.Element {
210-
const { offsetSize, componentId } = this.state
163+
const { componentId, offsetSize, needAnimation } = this.state
211164
const { options } = this.props
212165
const rootClass = classNames('at-swipe-action', this.props.className)
213-
const transform = this.computeTransform(offsetSize)
214-
const transformStyle: React.CSSProperties = transform ? { transform } : {}
215166

216167
return (
217168
<View
218169
id={`swipeAction-${componentId}`}
219170
className={rootClass}
220-
onTouchMove={this.handleTouchMove}
221-
onTouchEnd={this.handleTouchEnd}
222-
onTouchStart={this.handleTouchStart}
171+
style={{
172+
width: this.eleWidth === 0 ? '100%' : `${this.eleWidth}px`
173+
}}
223174
>
224-
<View
225-
className={classNames('at-swipe-action__content', {
226-
animtion: !this.isTouching
227-
})}
228-
style={transformStyle}
175+
<MovableArea
176+
className='at-swipe-action__area'
177+
style={{
178+
width:
179+
this.eleWidth === 0
180+
? '100%'
181+
: `${this.eleWidth + this.maxOffsetSize}px`,
182+
transform:
183+
this.eleWidth === 0
184+
? `translate(0, 0)`
185+
: `translate(-${this.maxOffsetSize}px, 0)`
186+
}}
229187
>
230-
{this.props.children}
231-
</View>
232-
233-
{Array.isArray(options) && options.length > 0 ? (
234-
<AtSwipeActionOptions
235-
options={options}
236-
componentId={componentId}
237-
onQueryedDom={this.handleDomInfo}
188+
<MovableView
189+
className='at-swipe-action__content'
190+
direction='horizontal'
191+
damping={50}
192+
x={offsetSize}
193+
onTouchEnd={this.onTouchEnd}
194+
onChange={this.onChange}
195+
animation={needAnimation}
196+
style={{
197+
width: this.eleWidth === 0 ? '100%' : `${this.eleWidth}px`
198+
}}
238199
>
239-
{options.map((item, key) => (
240-
<View
241-
key={`${item.text}-${key}`}
242-
style={item.style}
243-
onClick={(e): void => this.handleClick(item, key, e)}
244-
className={classNames(
245-
'at-swipe-action__option',
246-
item.className
247-
)}
248-
>
249-
<Text className='option__text'>{item.text}</Text>
250-
</View>
251-
))}
252-
</AtSwipeActionOptions>
253-
) : null}
200+
{this.props.children}
201+
</MovableView>
202+
{Array.isArray(options) && options.length > 0 ? (
203+
<AtSwipeActionOptions
204+
options={options}
205+
componentId={componentId}
206+
onQueryedDom={this.handleDomInfo}
207+
>
208+
{options.map((item, key) => (
209+
<View
210+
key={`${item.text}-${key}`}
211+
style={item.style}
212+
onClick={(e): void => this.handleClick(item, key, e)}
213+
className={classNames(
214+
'at-swipe-action__option',
215+
item.className
216+
)}
217+
>
218+
<Text className='option__text'>{item.text}</Text>
219+
</View>
220+
))}
221+
</AtSwipeActionOptions>
222+
) : null}
223+
</MovableArea>
254224
</View>
255225
)
256226
}

0 commit comments

Comments
 (0)