123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164 |
- import { ref, unref, watch, watchEffect, onMounted, onBeforeUnmount } from 'vue'
- import TWEEN from '@tweenjs/tween.js'
- export default function useSmoothSwipe({
- scrollTargetRef,
- maxTranslateLength,
- viewportWidth = document.documentElement.clientWidth,
- anchorPosList,
- initialAnchorIdx = 0,
- } = {}) {
- const isOperating = ref(false)
- const haveSwipedThisTime = ref(false)
- let hasOperatedThisTimeTimeoutId = null
- let translateLength = ref(anchorPosList[initialAnchorIdx])
- let maxTranslateLengthInner = Infinity
- let animationFrameId = null
- let tween = null
- let isTweening = false
- let fingerPosXWhenTouchStart = 0
- let anchorPosListInner = [...anchorPosList].sort((a, b) => {
- return a > b
- })
- let anchorPosListInnerReverse = [...anchorPosListInner].reverse()
- const currentAnchorIdx = ref(initialAnchorIdx)
- const goingToAnchorIdx = ref(null)
- watch(isOperating, (v) => {
- if (v) {
- clearTimeout(hasOperatedThisTimeTimeoutId)
- haveSwipedThisTime.value = false
- } else {
- clearTimeout(hasOperatedThisTimeTimeoutId)
- hasOperatedThisTimeTimeoutId = setTimeout(() => {
- haveSwipedThisTime.value = false
- }, 333)
- }
- })
- // compute maxTranslateLengthInner todo: 不能用if包裹吧?
- if (maxTranslateLength) {
- watchEffect(() => {
- maxTranslateLengthInner = unref(maxTranslateLength) - unref(viewportWidth)
- if (maxTranslateLengthInner < 0) {
- maxTranslateLengthInner = 0
- }
- })
- } else if (scrollTargetRef) {
- watchEffect(() => {
- if (scrollTargetRef.value) {
- maxTranslateLengthInner = scrollTargetRef.value.scrollWidth - unref(viewportWidth)
- if (maxTranslateLengthInner < 0) {
- maxTranslateLengthInner = 0
- }
- } else {
- maxTranslateLengthInner = Infinity
- }
- })
- }
- function onTouchStart(e) {
- isOperating.value = true
- fingerPosXWhenTouchStart = e.changedTouches[0].pageX
- }
- const onTouchMove = (event) => {
- if (isTweening) {
- return
- }
- let currentX = event.changedTouches[0].pageX
- let dX = currentX - fingerPosXWhenTouchStart
- if (dX < -1) { // 左滑,要往右移动
- const destinationIdx = anchorPosListInner.findIndex((item) => {
- return item - translateLength.value > 1
- })
- if ((destinationIdx !== -1) && !isTweening) {
- isTweening = true
- tween = new TWEEN.Tween(translateLength)
- tween.to({
- value: anchorPosListInner[destinationIdx],
- }, 1200)
- tween.easing(TWEEN.Easing.Cubic.InOut)
- tween.start()
- goingToAnchorIdx.value = destinationIdx
- tween.onComplete(() => {
- isTweening = false
- currentAnchorIdx.value = destinationIdx
- goingToAnchorIdx.value = null
- })
- }
- } else if (dX > 1) {
- const destinationIdx = anchorPosListInnerReverse.findIndex((item) => {
- return item - translateLength.value < -1
- })
- if ((destinationIdx !== -1) && !isTweening) {
- isTweening = true
- tween = new TWEEN.Tween(translateLength)
- tween.to({
- value: anchorPosListInnerReverse[destinationIdx],
- }, 1200)
- tween.easing(TWEEN.Easing.Cubic.InOut)
- tween.start()
- goingToAnchorIdx.value = anchorPosList.length - destinationIdx - 1
- tween.onComplete(() => {
- isTweening = false
- currentAnchorIdx.value = anchorPosList.length - destinationIdx - 1
- goingToAnchorIdx.value = null
- })
- }
- }
- }
- function onTouchEnd(e) {
- isOperating.value = false
- }
- function onTouchCancel() {
- isOperating.value = false
- }
- // 在每一帧更新镜头速度、位置
- function animationFrameTask() {
- TWEEN.update()
- animationFrameId = requestAnimationFrame(animationFrameTask)
- }
- onMounted(() => {
- animationFrameId = requestAnimationFrame(animationFrameTask)
- })
- onBeforeUnmount(() => {
- cancelAnimationFrame(animationFrameId)
- tween && tween.stop()
- })
- if (scrollTargetRef) { // todo: 不能用if包裹吧?
- watch(scrollTargetRef, (v) => {
- if (v) {
- v.addEventListener('touchstart', onTouchStart)
- v.addEventListener('touchmove', onTouchMove)
- v.addEventListener('touchend', onTouchEnd)
- v.addEventListener('touchcancel', onTouchCancel)
- }
- }, {
- immediate: true,
- })
- }
- return {
- onTouchStart,
- onTouchMove,
- onTouchEnd,
- onTouchCancel,
- translateLength,
- haveSwipedThisTime,
- currentAnchorIdx,
- goingToAnchorIdx,
- }
- }
|