Skip to content

Commit

Permalink
feat: 抽离长按组件
Browse files Browse the repository at this point in the history
  • Loading branch information
heavenly-zy committed Mar 31, 2024
1 parent 32c86d7 commit eee3c4a
Show file tree
Hide file tree
Showing 2 changed files with 66 additions and 34 deletions.
59 changes: 59 additions & 0 deletions src/components/LongPressable.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import type { ElementType } from 'react'
import { useCallback, useEffect, useRef } from 'react'

interface Props {
children: React.ReactNode
className?: string
onClick?: () => void
onLongPressComplete?: () => void
as?: ElementType
}

export const LongPressable: React.FC<Props> = ({ children, className, onClick, onLongPressComplete, as: Element = 'div' }) => {
const touchTimer = useRef<number>()
const touchPosition = useRef<{ x?: number, y?: number }>({ x: undefined, y: undefined })

const onTouchStart = useCallback((e: React.TouchEvent) => {
touchTimer.current = window.setTimeout(() => {
onLongPressComplete?.()
}, 500)
const { clientX: x, clientY: y } = e.touches[0]
touchPosition.current = { x, y }
}, [onLongPressComplete])

const onTouchMove = useCallback((e: React.TouchEvent) => {
const { clientX: newX, clientY: newY } = e.touches[0]
const { x, y } = touchPosition.current
if (x === undefined || y === undefined) { return }
const distance = Math.sqrt((newX - x) ** 2 + (newY - y) ** 2)

// 移动距离小于 10 就视为触发了长按
if (distance <= 10) { return }
window.clearTimeout(touchTimer.current)
touchTimer.current = undefined
}, [])

const onTouchEnd = useCallback(() => {
if (!touchTimer.current) { return }
window.clearTimeout(touchTimer.current)
touchTimer.current = undefined
}, [])

useEffect(() => {
return () => {
touchTimer.current && window.clearTimeout(touchTimer.current)
}
}, [])

return (
<Element
className={className}
onClick={onClick}
onTouchStart={onTouchStart}
onTouchMove={onTouchMove}
onTouchEnd={onTouchEnd}
>
{children}
</Element>
)
}
41 changes: 7 additions & 34 deletions src/pages/ItemsNewPage/Tags.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ import { Link, useNavigate } from 'react-router-dom'
import clsx from 'clsx'
import useSWRInfinite from 'swr/infinite'
import styled from 'styled-components'
import { useRef } from 'react'
import { Icon } from '../../components/Icon'
import { ajax } from '../../lib/ajax'
import { LongPressable } from '../../components/LongPressable'

const Tip = styled.div`
padding: 16px;
Expand All @@ -20,6 +20,8 @@ interface Props {
const fetcher = async (path: string) => (await ajax.get<Resources<Tag>>(path)).data

export const Tags: React.FC<Props> = ({ kind, value, onChange }) => {
const nav = useNavigate()

const getKey = (pageIndex: number, prev: Resources<Item>) => {
if (prev) {
const sendCount = (prev.pager.page - 1) * prev.pager.per_page + prev.resources.length
Expand All @@ -38,34 +40,6 @@ export const Tags: React.FC<Props> = ({ kind, value, onChange }) => {
} = useSWRInfinite(getKey, fetcher, { revalidateFirstPage: false })
const isLoadingMore = !error && data?.[page - 1] === undefined

// 长按跳转
const touchTimer = useRef<number>()
const touchPosition = useRef<{ x?: number, y?: number }>({ x: undefined, y: undefined })
const nav = useNavigate()
const onTouchStart = (e: React.TouchEvent<HTMLLIElement>, id: Tag['id']) => {
touchTimer.current = window.setTimeout(() => {
nav(`/tags/${id}`)
}, 500)
const { clientX: x, clientY: y } = e.touches[0]
touchPosition.current = { x, y }
}
const onTouchMove = (e: React.TouchEvent<HTMLLIElement>) => {
const { clientX: newX, clientY: newY } = e.touches[0]
const { x, y } = touchPosition.current
if (x === undefined || y === undefined) { return }
const distance = Math.sqrt((newX - x) ** 2 + (newY - y) ** 2)
if (distance > 10) {
window.clearTimeout(touchTimer.current)
touchTimer.current = undefined
}
}
const onTouchEnd = () => {
if (touchTimer.current) {
window.clearTimeout(touchTimer.current)
touchTimer.current = undefined
}
}

// 初次加载
if (!data) {
return (
Expand All @@ -82,12 +56,11 @@ export const Tags: React.FC<Props> = ({ kind, value, onChange }) => {
const hasMore = (lastPage - 1) * per_page + lastItem.resources.length < count

const renderTag = (tag: Tag) => (
<li
<LongPressable
as="li"
key={tag.id}
onClick={() => onChange?.([tag.id])}
onTouchStart={e => onTouchStart(e, tag.id)}
onTouchMove={e => onTouchMove(e)}
onTouchEnd={onTouchEnd}
onLongPressComplete={() => { nav(`/tags/${tag.id}`) }}
>
<span
h-48px
Expand All @@ -104,7 +77,7 @@ export const Tags: React.FC<Props> = ({ kind, value, onChange }) => {
{tag.sign}
</span>
<span text-12px text="#666" text-center>{tag.name}</span>
</li>
</LongPressable>
)

return (
Expand Down

0 comments on commit eee3c4a

Please sign in to comment.