Skip to content

Commit

Permalink
fix(SwipeAction): 修复 SwipeAction 滑动卡顿的问题
Browse files Browse the repository at this point in the history
  • Loading branch information
luckyadam committed Dec 19, 2020
1 parent c601ebf commit 0345587
Show file tree
Hide file tree
Showing 6 changed files with 119 additions and 204 deletions.
11 changes: 0 additions & 11 deletions packages/taro-ui/config/rollup.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,17 +29,6 @@ export default {
file: resolveFile(Package.module),
format: 'es',
sourcemap: true
},
{
file: resolveFile(Package.browser),
format: 'umd',
name: 'taro-ui',
sourcemap: true,
globals: {
react: 'React',
'@tarojs/components': 'components',
'@tarojs/taro': 'Taro'
}
}
],
external: externalPackages,
Expand Down
1 change: 0 additions & 1 deletion packages/taro-ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
"name": "taro-ui",
"version": "3.0.0-alpha.3",
"description": "UI KIT for Taro",
"browser": "dist/index.umd.js",
"module": "dist/index.esm.js",
"main": "dist/index.js",
"source": "src/index.ts",
Expand Down
272 changes: 103 additions & 169 deletions packages/taro-ui/src/components/swipe-action/index.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,14 @@
import classNames from 'classnames'
import _inRange from 'lodash/inRange'
import _isEmpty from 'lodash/isEmpty'
import PropTypes, { InferProps } from 'prop-types'
import React from 'react'
import { Text, View } from '@tarojs/components'
import { CommonEvent, ITouchEvent } from '@tarojs/components/types/common'
import { Text, View, MovableArea, MovableView } from '@tarojs/components'
import { CommonEvent } from '@tarojs/components/types/common'
import {
AtSwipeActionProps,
AtSwipeActionState,
SwipeActionOption
} from '../../../types/swipe-action'
import {
delayGetClientRect,
delayGetScrollOffset,
uuid
} from '../../common/utils'
import { uuid } from '../../common/utils'
import AtSwipeActionOptions from './options/index'

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

private endValue: number
private startX: number
private startY: number
private maxOffsetSize: number
private domInfo: any
private isMoving: boolean
private isTouching: boolean
private moveX: number
private eleWidth: number

public constructor(props: AtSwipeActionProps) {
super(props)
const { isOpened } = props
this.endValue = 0
this.startX = 0
this.startY = 0
this.maxOffsetSize = 0
this.domInfo = {
top: 0,
bottom: 0,
left: 0,
right: 0
}
this.isMoving = false
this.isTouching = false
const { isOpened, maxDistance, areaWidth } = props
this.maxOffsetSize = maxDistance
this.state = {
componentId: uuid(),
offsetSize: 0,
_isOpened: !!isOpened
_isOpened: !!isOpened,
needAnimation: false
}
}

private getDomInfo(): Promise<void> {
return Promise.all([
delayGetClientRect({
delayTime: 0,
selectorStr: `#swipeAction-${this.state.componentId}`
}),
delayGetScrollOffset({ delayTime: 0 })
]).then(([rect, scrollOffset]) => {
rect[0].top += scrollOffset[0].scrollTop
rect[0].bottom += scrollOffset[0].scrollTop
this.domInfo = rect[0]
})
this.moveX = 0
this.eleWidth = areaWidth
}

public UNSAFE_componentWillReceiveProps(nextProps: AtSwipeActionProps): void {
Expand All @@ -78,118 +46,40 @@ export default class AtSwipeAction extends React.Component<
}

private _reset(isOpened: boolean): void {
this.isMoving = false
this.isTouching = false

if (isOpened) {
this.endValue = -this.maxOffsetSize
this.setState({
_isOpened: true,
offsetSize: -this.maxOffsetSize
offsetSize: 0
})
} else {
this.endValue = 0
this.setState({
offsetSize: 0,
_isOpened: false
})
this.setState(
{
offsetSize: this.moveX
},
() => {
this.setState({
offsetSize: this.maxOffsetSize,
_isOpened: false
})
}
)
}
}

private computeTransform = (value: number): string | null =>
// if (Taro.getEnv() === Taro.ENV_TYPE.ALIPAY) {
// return !_isNil(value) ? `translate3d(${value}px,0,0)` : null
// }
value ? `translate3d(${value}px,0,0)` : null

private handleOpened = (event: CommonEvent): void => {
const { onOpened } = this.props
if (typeof onOpened === 'function' && this.state._isOpened) {
if (typeof onOpened === 'function') {
onOpened(event)
}
}

private handleClosed = (event: CommonEvent): void => {
const { onClosed } = this.props
if (typeof onClosed === 'function' && !this.state._isOpened) {
if (typeof onClosed === 'function') {
onClosed(event)
}
}

private handleTouchStart = (e: ITouchEvent): void => {
const { clientX, clientY } = e.touches[0]

if (this.props.disabled) return

this.getDomInfo()

this.startX = clientX
this.startY = clientY
this.isTouching = true
}

private handleTouchMove = (e: ITouchEvent): void => {
if (_isEmpty(this.domInfo)) {
return
}

const { startX, startY } = this
const { top, bottom, left, right } = this.domInfo
const { clientX, clientY, pageX, pageY } = e.touches[0]

const x = Math.abs(clientX - startX)
const y = Math.abs(clientY - startY)

const inDom = _inRange(pageX, left, right) && _inRange(pageY, top, bottom)

if (!this.isMoving && inDom) {
this.isMoving =
y === 0 ||
x / y >= Number.parseFloat(Math.tan((45 * Math.PI) / 180).toFixed(2))
}

if (this.isTouching && this.isMoving) {
e.preventDefault()

const offsetSize = clientX - this.startX
const isRight = offsetSize > 0

if (this.state.offsetSize === 0 && isRight) return

const value = this.endValue + offsetSize
this.setState({
offsetSize: value >= 0 ? 0 : value
})
}
}

private handleTouchEnd = (event: ITouchEvent): void => {
this.isTouching = false

const { offsetSize } = this.state

this.endValue = offsetSize

const breakpoint = this.maxOffsetSize / 2
const absOffsetSize = Math.abs(offsetSize)

if (absOffsetSize > breakpoint) {
this._reset(true)
this.handleOpened(event)
return
}

this._reset(false) // TODO: Check behavior
this.handleClosed(event)
}

private handleDomInfo = ({ width }: { width: number }): void => {
const { _isOpened } = this.state

this.maxOffsetSize = width
this._reset(_isOpened)
}

private handleClick = (
item: SwipeActionOption,
index: number,
Expand All @@ -206,51 +96,93 @@ export default class AtSwipeAction extends React.Component<
}
}

onTouchEnd = e => {
if (this.moveX === 0) {
this._reset(true)
this.handleOpened(e)
return
}
if (this.moveX === this.maxOffsetSize) {
this._reset(false)
this.handleClosed(e)
return
}
if (this.state._isOpened && this.moveX > 0) {
this._reset(false)
this.handleClosed(e)
return
}
if (this.maxOffsetSize - this.moveX < this.maxOffsetSize * 0.4) {
this._reset(false)
this.handleClosed(e)
} else {
this._reset(true)
this.handleOpened(e)
}
}

onChange = e => {
this.moveX = e.detail.x
}

public render(): JSX.Element {
const { offsetSize, componentId } = this.state
const { componentId, offsetSize } = this.state
const { options } = this.props
const rootClass = classNames('at-swipe-action', this.props.className)
const transform = this.computeTransform(offsetSize)
const transformStyle: React.CSSProperties = transform ? { transform } : {}

return (
<View
id={`swipeAction-${componentId}`}
className={rootClass}
onTouchMove={this.handleTouchMove}
onTouchEnd={this.handleTouchEnd}
onTouchStart={this.handleTouchStart}
style={{
width: `${this.eleWidth}px`
}}
>
<View
className={classNames('at-swipe-action__content', {
animtion: !this.isTouching
})}
style={transformStyle}
<MovableArea
className='at-swipe-action__area'
style={{
width: `${this.eleWidth + this.maxOffsetSize}px`,
transform: `translate(-${this.maxOffsetSize}px, 0)`
}}
>
{this.props.children}
</View>

{Array.isArray(options) && options.length > 0 ? (
<AtSwipeActionOptions
options={options}
componentId={componentId}
onQueryedDom={this.handleDomInfo}
<MovableView
className='at-swipe-action__content'
direction='horizontal'
damping={50}
x={offsetSize}
onTouchEnd={this.onTouchEnd}
onChange={this.onChange}
style={{
width: `${this.eleWidth}px`
}}
>
{options.map((item, key) => (
<View
key={`${item.text}-${key}`}
style={item.style}
onClick={(e): void => this.handleClick(item, key, e)}
className={classNames(
'at-swipe-action__option',
item.className
)}
{this.props.children}
{Array.isArray(options) && options.length > 0 ? (
<AtSwipeActionOptions
options={options}
componentId={componentId}
customStyle={{
transform: `translate(${this.maxOffsetSize}px, 0)`,
opacity: 1
}}
>
<Text className='option__text'>{item.text}</Text>
</View>
))}
</AtSwipeActionOptions>
) : null}
{options.map((item, key) => (
<View
key={`${item.text}-${key}`}
style={item.style}
onClick={(e): void => this.handleClick(item, key, e)}
className={classNames(
'at-swipe-action__option',
item.className
)}
>
<Text className='option__text'>{item.text}</Text>
</View>
))}
</AtSwipeActionOptions>
) : null}
</MovableView>
</MovableArea>
</View>
)
}
Expand All @@ -260,7 +192,9 @@ AtSwipeAction.defaultProps = {
options: [],
isOpened: false,
disabled: false,
autoClose: false
autoClose: false,
maxDistance: 0,
areaWidth: 0
}

AtSwipeAction.propTypes = {
Expand Down
Loading

0 comments on commit 0345587

Please sign in to comment.