Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

feat(carousel): touch operation #275

Merged
merged 2 commits into from
Jun 23, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.en-US.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# CHANGELOG

## Pending

### Feats

- `n-carousel` supports touch operation, closes [#271](https://github.com/TuSimple/naive-ui/issues/271).

## 2.14.0 (2021-06-23)

### Breaking Changes
Expand Down
6 changes: 6 additions & 0 deletions CHANGELOG.zh-CN.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# CHANGELOG

## Pending

### Feats

- `n-carousel` 支持触控操作,关闭 [#271](https://github.com/TuSimple/naive-ui/issues/271)

## 2.14.0 (2021-06-23)

### Breaking Changes
Expand Down
70 changes: 66 additions & 4 deletions src/carousel/src/Carousel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
onBeforeUnmount
} from 'vue'
import { indexMap } from 'seemly'
import { on, off } from 'evtd'
import { useConfig, useTheme } from '../../_mixins'
import type { ThemeProps } from '../../_mixins'
import { ExtractPublicPropTypes } from '../../_utils'
Expand All @@ -36,6 +37,9 @@ export default defineComponent({
const { mergedClsPrefixRef } = useConfig(props)
const currentRef = ref(1)
const lengthRef = { value: 1 }
const touchingRef = ref(false)
const dragOffsetRef = ref(0)
const selfElRef = ref<HTMLDivElement | null>(null)
let timerId: number | null = null
let inTransition = false
// current from 0 to length + 1
Expand Down Expand Up @@ -84,9 +88,9 @@ export default defineComponent({
const nextCurrent =
current === 0 ? length : current === length + 1 ? 1 : null
if (nextCurrent !== null) {
target.style.transition = 'none'
currentRef.value = nextCurrent
void nextTick(() => {
target.style.transition = 'none'
void target.offsetWidth
target.style.transition = ''
inTransition = false
Expand All @@ -102,6 +106,57 @@ export default defineComponent({
setCurrent(current)
}
}
let dragStartX = 0
let dragStartTime = 0
let memorizedContainerWidth = 0
function handleTouchstart (e: TouchEvent): void {
if (timerId !== null) {
window.clearInterval(timerId)
}
e.preventDefault()
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
memorizedContainerWidth = selfElRef.value!.offsetWidth
touchingRef.value = true
dragStartTime = Date.now()
dragStartX = e.touches[0].clientX
on('touchmove', document, handleTouchmove)
on('touchend', document, handleTouchend)
on('touchcancel', document, handleTouchend)
}
function handleTouchmove (e: TouchEvent): void {
const dragOffset = e.touches[0].clientX - dragStartX
dragOffsetRef.value =
dragOffset > memorizedContainerWidth
? memorizedContainerWidth
: dragOffset < -memorizedContainerWidth
? -memorizedContainerWidth
: dragOffset
}
function handleTouchend (): void {
if (props.autoplay) resetInterval()
void nextTick(() => {
touchingRef.value = false
})
const { value: selfEl } = selfElRef
if (selfEl) {
const { offsetWidth } = selfEl
const { value: dragOffset } = dragOffsetRef
const duration = Date.now() - dragStartTime
// more than 50% width or faster than 0.4px per ms
if (dragOffset > offsetWidth / 2 || dragOffset / duration > 0.4) {
prev()
} else if (
dragOffset < -offsetWidth / 2 ||
dragOffset / duration < -0.4
) {
next()
}
}
dragOffsetRef.value = 0
off('touchmove', document, handleTouchmove)
off('touchend', document, handleTouchend)
off('touchcancel', document, handleTouchend)
}
function resetInterval (): void {
if (timerId !== null) {
window.clearInterval(timerId)
Expand Down Expand Up @@ -134,13 +189,17 @@ export default defineComponent({
props
)
return {
selfElRef,
mergedClsPrefix: mergedClsPrefixRef,
current: currentRef,
lengthRef,
touching: touchingRef,
dragOffset: dragOffsetRef,
prev,
next,
setCurrent,
handleKeydown,
handleTouchstart,
handleTransitionEnd,
cssVars: computed(() => {
const {
Expand Down Expand Up @@ -173,14 +232,17 @@ export default defineComponent({
<div
class={`${mergedClsPrefix}-carousel`}
style={this.cssVars as CSSProperties}
ref="selfElRef"
>
<div
class={`${mergedClsPrefix}-carousel__slides`}
onTouchstart={this.handleTouchstart}
style={{
width: `${total}00%`,
transform: `translate3d(-${
(100 / total) * (current % total)
}%, 0, 0)`
transition: this.touching ? 'none' : '',
transform:
`translate3d(-${(100 / total) * (current % total)}%, 0, 0)` +
(this.touching ? `translateX(${this.dragOffset}px)` : '')
}}
onTransitionend={this.handleTransitionEnd}
role="listbox"
Expand Down