This commit is contained in:
hhh 2023-01-20 19:36:26 +08:00
parent 41a6f99e8e
commit 19f3733aa7
7 changed files with 264 additions and 157 deletions

View File

@ -96,7 +96,7 @@
ref="virtualList"
:list="videos"
:renderSlide="render"
v-model:active-index="videoActiveIndex"
v-model:active-index="videoActixveIndex"
@end="end"
>
</SlideColumnVirtualList>

View File

@ -1,9 +1,8 @@
<script setup>
import {ref, onMounted, reactive} from "vue";
import {onMounted, reactive, ref, watch} from "vue";
import GM from '../../utils'
import bus from "../../utils/bus";
import Common from "../../utils";
import {slideReset, slideTouchStart} from "./common";
import {getSlideDistance, slideReset, slideTouchEnd, slideTouchMove, slideTouchStart} from "./common";
import {SlideType} from "../../utils/const_var";
const props = defineProps({
index: {
@ -17,20 +16,33 @@ const emit = defineEmits(['update:index'])
const judgeValue = 20
const wrapperEl = ref(null)
const wrapper = reactive({width: 0, height: 0, childrenLength: 0})
const state = reactive({
name: 'SlideVertical',
localIndex: 0,
name: 'SlideHorizontal',
localIndex: props.index,
needCheck: true,
next: false,
start: {x: 0, y: 0, time: 0},
move: {x: 0, y: 0},
wrapper: {width: 0, height: 0, childrenLength: 0}
})
// ref
watch(
() => props.index,
(newVal) => {
console.log('w')
if (state.localIndex !== newVal) {
state.localIndex = newVal
GM.$setCss(wrapperEl.value, 'transition-duration', `300ms`)
GM.$setCss(wrapperEl.value, 'transform', `translate3d(${getSlideDistance(state, SlideType.HORIZONTAL)}px, 0, 0)`)
}
}
)
onMounted(() => {
wrapper.width = GM.$getCss(wrapperEl.value, 'width')
wrapper.height = GM.$getCss(wrapperEl.value, 'height')
wrapper.childrenLength = wrapperEl.value.children.length
state.wrapper.width = GM.$getCss(wrapperEl.value, 'width')
state.wrapper.height = GM.$getCss(wrapperEl.value, 'height')
state.wrapper.childrenLength = wrapperEl.value.children.length
})
function touchStart(e) {
@ -38,58 +50,20 @@ function touchStart(e) {
}
function touchMove(e) {
state.move.x = e.touches[0].pageX - state.start.x
state.move.y = e.touches[0].pageY - state.start.y
let isNext = state.move.x < 0
if ((state.localIndex === 0 && !isNext) || (state.localIndex === wrapper.childrenLength - 1 && isNext)) return
checkDirection(e)
if (state.next) {
bus.emit(state.name + '-moveX', state.move.x)
GM.$stopPropagation(e)
GM.$setCss(wrapperEl.value, 'transform',
`translate3d(${getDistance()
+ state.move.x
+ (isNext ? judgeValue : -judgeValue)
}px, 0, 0)`)
}
slideTouchMove(e, wrapperEl.value, state, judgeValue, canNext, SlideType.HORIZONTAL)
}
function touchEnd(e) {
let isRight = state.move.x < 0
if ((state.localIndex === 0 && !isRight) || (state.localIndex === wrapper.childrenLength - 1 && isRight)) state.next = false
slideTouchEnd(e, state, canNext, () => {
let endTime = Date.now()
let gapTime = endTime - state.start.time
if (Math.abs(state.move.x) < 20) gapTime = 1000
if (Math.abs(state.move.x) > (wrapper.width / 4)) gapTime = 100
if (gapTime < 150 && state.next) {
if (isRight) {
state.localIndex++
} else {
state.localIndex--
}
}
slideReset(wrapperEl.value, getDistance(), state, emit)
})
slideReset(wrapperEl.value, state, SlideType.HORIZONTAL, emit)
}
function checkDirection(e) {
if (!state.needCheck) return
if (Math.abs(state.move.x) > judgeValue || Math.abs(state.move.y) > judgeValue) {
let angle = (Math.abs(state.move.x) * 10) / (Math.abs(state.move.y) * 10)
state.next = angle > 1;
// console.log(angle)
return state.needCheck = false
}
return state.needCheck = true
}
function getDistance() {
return -state.localIndex * wrapper.width
function canNext(isNext) {
return !((state.localIndex === 0 && !isNext) || (state.localIndex === state.wrapper.childrenLength - 1 && isNext));
}
</script>
<template>

View File

@ -1,112 +1,56 @@
<script setup>
import {ref, onMounted, reactive} from "vue";
import Common from '../../utils'
import bus from "../../utils/bus";
import {onMounted, reactive, ref} from "vue";
import GM from '../../utils'
import {slideReset, slideTouchEnd, slideTouchMove, slideTouchStart} from "./common";
import {SlideType} from "../../utils/const_var";
const props = defineProps({
list: {
type: Array,
index: {
type: Number,
default: () => {
return []
return 0
}
},
})
const emit = defineEmits(['update:index'])
const judgeValue = 20
const wrapperEl = ref(null)
const wrapper = reactive({width: 0, height: 0, childrenLength: 0})
const start = reactive({x: 0, y: 0, time: 0})
const move = reactive({x: 0, y: 0})
const state = reactive({
name: 'SlideVertical',
localIndex: 0,
needCheck: true,
next: false,
start: {x: 0, y: 0, time: 0},
move: {x: 0, y: 0},
wrapper: {width: 0, height: 0, childrenLength: 0}
})
onMounted(() => {
checkChildren()
state.wrapper.width = GM.$getCss(wrapperEl.value, 'width')
state.wrapper.height = GM.$getCss(wrapperEl.value, 'height')
state.wrapper.childrenLength = wrapperEl.value.children.length
})
function checkChildren() {
wrapper.width = Common.$getCss(wrapperEl.value, 'width')
wrapper.height = Common.$getCss(wrapperEl.value, 'height')
wrapper.childrenLength = wrapperEl.value.children.length
}
function touchStart(e) {
Common.$setCss(wrapperEl.value, 'transition-duration', `0ms`)
start.x = e.touches[0].pageX
start.y = e.touches[0].pageY
start.time = Date.now()
slideTouchStart(e, wrapperEl.value, state)
}
function touchMove(e) {
move.x = e.touches[0].pageX - start.x
move.y = e.touches[0].pageY - start.y
let isNext = move.x < 0
checkDirection(e)
if (state.localIndex === 0 && !isNext && state.next) {
// bus.emit(this.name + '-moveY', this.moveY)
}
if ((state.localIndex === 0 && !isNext) || (state.localIndex === props.list.length- 1 && isNext)) return
if (state.next) {
Common.$stopPropagation(e)
Common.$setCss(wrapperEl.value, 'transform',
`translate3d(${getDistance()
+ move.y
+ (isNext ? judgeValue : -judgeValue)
}px, 0)`)
}
slideTouchMove(e, wrapperEl.value, state, judgeValue, canNext, SlideType.VERTICAL)
}
function touchEnd(e) {
let isRight = move.x < 0
if ((state.localIndex === 0 && !isRight) || (state.localIndex === wrapper.childrenLength - 1 && isRight)) state.next = false
slideTouchEnd(e, state, canNext, () => {
let endTime = Date.now()
let gapTime = endTime - start.time
if (Math.abs(move.x) < 20) gapTime = 1000
if (Math.abs(move.x) > (wrapper.width / 4)) gapTime = 100
if (gapTime < 150 && state.next) {
if (isRight) {
state.localIndex++
} else {
state.localIndex--
}
}
reset()
}, SlideType.VERTICAL)
slideReset(wrapperEl.value, state, SlideType.VERTICAL, emit)
}
function checkDirection(e) {
if (!state.needCheck) return
if (Math.abs(move.x) > judgeValue || Math.abs(move.y) > judgeValue) {
let angle = (Math.abs(move.x) * 10) / (Math.abs(move.y) * 10)
state.next = angle <= 1;
// console.log(angle)
return state.needCheck = false
}
return state.needCheck = true
function canNext(isNext) {
return !((state.localIndex === 0 && !isNext) || (state.localIndex === state.wrapper.childrenLength - 1 && isNext));
}
function reset() {
Common.$setCss(wrapperEl.value, 'transition-duration', `300ms`)
Common.$setCss(wrapperEl.value, 'transform',
`translate3d(${getDistance()}px, 0, 0)`)
move.x = 0
state.next = false
state.needCheck = true
start.time = null
// this.$emit('update:index', state.localIndex)
// bus.emit(this.name + '-end', state.localIndex)
}
function getDistance() {
return -state.localIndex * wrapper.height
}
</script>
<template>
@ -120,4 +64,4 @@ function getDistance() {
<slot></slot>
</div>
</div>
</template>
</template>

View File

@ -0,0 +1,67 @@
<script setup>
import {onMounted, reactive, ref} from "vue";
import GM from '../../utils'
import {slideReset, slideTouchEnd, slideTouchMove, slideTouchStart} from "./common";
import {SlideType} from "../../utils/const_var";
const props = defineProps({
index: {
type: Number,
default: () => {
return 0
}
},
})
const emit = defineEmits(['update:index'])
const judgeValue = 20
const wrapperEl = ref(null)
const state = reactive({
name: 'SlideVertical',
localIndex: 0,
needCheck: true,
next: false,
start: {x: 0, y: 0, time: 0},
move: {x: 0, y: 0},
wrapper: {width: 0, height: 0, childrenLength: 0}
})
onMounted(() => {
state.wrapper.width = GM.$getCss(wrapperEl.value, 'width')
state.wrapper.height = GM.$getCss(wrapperEl.value, 'height')
state.wrapper.childrenLength = wrapperEl.value.children.length
})
function touchStart(e) {
slideTouchStart(e, wrapperEl.value, state)
}
function touchMove(e) {
slideTouchMove(e, wrapperEl.value, state, judgeValue, canNext, SlideType.VERTICAL)
}
function touchEnd(e) {
slideTouchEnd(e, state, canNext, () => {
}, SlideType.VERTICAL)
slideReset(wrapperEl.value, state, SlideType.VERTICAL, emit)
}
function canNext(isNext) {
return !((state.localIndex === 0 && !isNext) || (state.localIndex === state.wrapper.childrenLength - 1 && isNext));
}
</script>
<template>
<div class="slide">
<div class="slide-wrapper flex-direction-column"
ref="wrapperEl"
@touchstart="touchStart"
@touchmove="touchMove"
@touchend="touchEnd"
>
<slot></slot>
</div>
</div>
</template>

View File

@ -1,10 +1,88 @@
import bus from "../../utils/bus";
import Utils from '../../utils'
import GM from "../../utils";
import {SlideType} from "../../utils/const_var";
export function slideReset(el, distance, state, emit) {
export function slideTouchStart(e, el, state) {
GM.$setCss(el, 'transition-duration', `0ms`)
state.start.x = e.touches[0].pageX
state.start.y = e.touches[0].pageY
state.start.time = Date.now()
}
export function canSlide(state, judgeValue, type = SlideType.HORIZONTAL) {
if (state.needCheck) {
if (Math.abs(state.move.x) > judgeValue || Math.abs(state.move.y) > judgeValue) {
let angle = (Math.abs(state.move.x) * 10) / (Math.abs(state.move.y) * 10)
state.next = type === SlideType.HORIZONTAL ? angle > 1 : angle <= 1;
// console.log(angle)
state.needCheck = false
} else {
return false
}
}
return state.next
}
export function slideTouchMove(e, el, state, judgeValue, cb, type = SlideType.HORIZONTAL) {
state.move.x = e.touches[0].pageX - state.start.x
state.move.y = e.touches[0].pageY - state.start.y
let isNext = type === SlideType.HORIZONTAL ? state.move.x < 0 : state.move.y < 0
if (!cb?.(isNext)) return
if (canSlide(state, judgeValue, type)) {
bus.emit(state.name + '-moveX', state.move.x)
GM.$stopPropagation(e)
let t = getSlideDistance(state, type) + (isNext ? judgeValue : -judgeValue)
let dx1 = 0
let dx2 = 0
if (type === SlideType.HORIZONTAL) {
dx1 = t + state.move.x
} else {
dx2 = t + state.move.y
}
GM.$setCss(el, 'transform', `translate3d(${dx1}px, ${dx2}px, 0)`)
}
}
export function slideTouchEnd(e, state, canNextCb, nextCb, type = SlideType.HORIZONTAL) {
let isHorizontal = type === SlideType.HORIZONTAL;
let isNext = isHorizontal ? state.move.x < 0 : state.move.y < 0
if (!canNextCb?.(isNext)) return
if (state.next) {
GM.$stopPropagation(e)
let endTime = Date.now()
let gapTime = endTime - state.start.time
let distance = isHorizontal ? state.move.x : state.move.y
let judgeValue = isHorizontal ? state.wrapper.width : state.wrapper.height
if (Math.abs(distance) < 20) gapTime = 1000
if (Math.abs(distance) > (judgeValue / 3)) gapTime = 100
if (gapTime < 150) {
if (isNext) {
state.localIndex++
} else {
state.localIndex--
}
nextCb?.()
}
}
}
export function slideReset(el, state, type, emit) {
Utils.$setCss(el, 'transition-duration', `300ms`)
Utils.$setCss(el, 'transform', `translate3d(${distance}px, 0, 0)`)
let t = getSlideDistance(state, type)
let dx1 = 0
let dx2 = 0
if (type === SlideType.HORIZONTAL) {
dx1 = t
} else {
dx2 = t
}
GM.$setCss(el, 'transform', `translate3d(${dx1}px, ${dx2}px, 0)`)
state.start.x = state.start.y = state.start.time = state.move.x = state.move.y = 0
state.next = false
state.needCheck = true
@ -12,9 +90,10 @@ export function slideReset(el, distance, state, emit) {
bus.emit(state.name + '-end', state.localIndex)
}
export function slideTouchStart(e, el, state) {
GM.$setCss(el, 'transition-duration', `0ms`)
state.start.x = e.touches[0].pageX
state.start.y = e.touches[0].pageY
state.start.time = Date.now()
export function getSlideDistance(state, type = SlideType.HORIZONTAL) {
if (type === SlideType.HORIZONTAL) {
return -state.localIndex * state.wrapper.width
} else {
return -state.localIndex * state.wrapper.height
}
}

View File

@ -1,35 +1,61 @@
<template>
<div class="slide2">
<H class="h" v-model:index="state.index">
<SlideItem class="slide-item">
<!-- <V>-->
<!-- <SlideItem class="slide-item"></SlideItem>-->
<!-- <SlideItem class="slide-item"></SlideItem>-->
<!-- </V>-->
<H class="h" v-model:index="state.baseIndex">
<SlideItem class="slide-item gray">
<H class="h" v-model:index="state.index">
<SlideItem class="slide-item gray">
<div class="big">找红包</div>
</SlideItem>
<SlideItem class="slide-item gray">
<div class="big">热点</div>
</SlideItem>
<SlideItem class="slide-item gray">
<div class="big">社区</div>
</SlideItem>
<SlideItem class="slide-item gray">
<div class="big">关注</div>
</SlideItem>
<SlideItem class="slide-item gray">
<div class="big">商城</div>
</SlideItem>
<SlideItem class="slide-item">
<VInfinite>
<SlideItem class="slide-item red"></SlideItem>
<SlideItem class="slide-item yellow"></SlideItem>
<SlideItem class="slide-item blue"></SlideItem>
</VInfinite>
</SlideItem>
</H>
<Footer v-bind:init-tab="1"/>
</SlideItem>
<SlideItem class="slide-item">
<!-- <V></V>-->
<SlideItem class="slide-item gray">
<div class="big" v-for="i in 100">主页</div>
</SlideItem>
</H>
<span>{{ state.index }}</span>
<Footer v-bind:init-tab="1"/>
<span>{{ state.baseIndex }}</span>
<button @click="state.baseIndex++"></button>
<button @click="state.baseIndex--"></button>
</div>
</template>
<script setup>
import H from './H'
import V from './V'
import VInfinite from './VInfinite.vue'
import SlideItem from './SlideItem'
import {reactive} from "vue";
const state = reactive({
index: 0
baseIndex: 1,
index: 1
})
</script>
<style scoped lang="less">
.slide2 {
font-size: 14rem;
width: 100%;
height: 100%;
@ -37,6 +63,11 @@ const state = reactive({
color: white;
font-size: 24rem;
}
.big {
font-weight: bold;
font-size: 100rem;
}
}
.h {
@ -44,12 +75,20 @@ const state = reactive({
height: 80vh;
overflow: hidden;
.slide-item {
.red {
background-color: red;
}
&:last-child {
background-color: gray;
}
.yellow {
background-color: yellow;
}
.blue {
background-color: blue;
}
.gray {
background-color: gray;
}
}

View File

@ -19,3 +19,7 @@ export default {
REQUEST_FOLLOW: 4//关注请求
}
}
export const SlideType = {
HORIZONTAL:0,
VERTICAL:1,
}