refactor: optimize the code
This commit is contained in:
parent
15b89986b2
commit
6107b19d03
@ -3,7 +3,7 @@
|
|||||||
<div class="img-slide-wrapper">
|
<div class="img-slide-wrapper">
|
||||||
<div
|
<div
|
||||||
class="img-slide-list"
|
class="img-slide-list"
|
||||||
ref="wrapperEl"
|
ref="slideListEl"
|
||||||
@touchstart.passive="touchStart"
|
@touchstart.passive="touchStart"
|
||||||
@touchmove="touchMove"
|
@touchmove="touchMove"
|
||||||
@touchend="touchEnd"
|
@touchend="touchEnd"
|
||||||
@ -233,7 +233,7 @@ const props = defineProps({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
const wrapperEl = ref(null)
|
const slideListEl = ref(null)
|
||||||
|
|
||||||
//用于解决,touch事件触发startPlay,然后click事件又触发stopLoop的问题
|
//用于解决,touch事件触发startPlay,然后click事件又触发stopLoop的问题
|
||||||
let lockDatetime = 0
|
let lockDatetime = 0
|
||||||
@ -301,7 +301,7 @@ function startLoop() {
|
|||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
await nextTick()
|
await nextTick()
|
||||||
slideInit(wrapperEl.value, state, SlideType.HORIZONTAL)
|
slideInit(slideListEl.value, state, SlideType.HORIZONTAL)
|
||||||
startPlay()
|
startPlay()
|
||||||
// setTimeout(() => {
|
// setTimeout(() => {
|
||||||
// state.operationStatus = SlideAlbumOperationStatus.Zooming
|
// state.operationStatus = SlideAlbumOperationStatus.Zooming
|
||||||
@ -348,11 +348,11 @@ onBeforeUpdate(() => {
|
|||||||
watch(
|
watch(
|
||||||
() => state.localIndex,
|
() => state.localIndex,
|
||||||
() => {
|
() => {
|
||||||
GM.$setCss(wrapperEl.value, 'transition-duration', `300ms`)
|
GM.$setCss(slideListEl.value, 'transition-duration', `300ms`)
|
||||||
GM.$setCss(
|
GM.$setCss(
|
||||||
wrapperEl.value,
|
slideListEl.value,
|
||||||
'transform',
|
'transform',
|
||||||
`translate3d(${getSlideOffset(state, wrapperEl.value)}px, 0, 0)`
|
`translate3d(${getSlideOffset(state, slideListEl.value)}px, 0, 0)`
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@ -406,7 +406,7 @@ function touchStart(e) {
|
|||||||
// Utils.$showNoticeDialog('start'+e.touches.length)
|
// Utils.$showNoticeDialog('start'+e.touches.length)
|
||||||
console.log('start', e.touches.length)
|
console.log('start', e.touches.length)
|
||||||
if (e.touches.length === 1) {
|
if (e.touches.length === 1) {
|
||||||
slideTouchStart(e, wrapperEl.value, state)
|
slideTouchStart(e, slideListEl.value, state)
|
||||||
} else {
|
} else {
|
||||||
if (state.operationStatus === SlideAlbumOperationStatus.Zooming) {
|
if (state.operationStatus === SlideAlbumOperationStatus.Zooming) {
|
||||||
// state.start.center = Utils.getCenter(state.start.point1, state.start.point2)
|
// state.start.center = Utils.getCenter(state.start.point1, state.start.point2)
|
||||||
@ -454,7 +454,7 @@ function touchMove(e) {
|
|||||||
state.isAutoPlay = false
|
state.isAutoPlay = false
|
||||||
slideTouchMove(
|
slideTouchMove(
|
||||||
e,
|
e,
|
||||||
wrapperEl.value,
|
slideListEl.value,
|
||||||
state,
|
state,
|
||||||
canNext,
|
canNext,
|
||||||
() => {
|
() => {
|
||||||
@ -580,7 +580,7 @@ function touchEnd(e) {
|
|||||||
startLoop()
|
startLoop()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
slideReset(e, wrapperEl.value, state)
|
slideReset(e, slideListEl.value, state)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -40,7 +40,7 @@ const emit = defineEmits(['update:index'])
|
|||||||
|
|
||||||
let ob = null
|
let ob = null
|
||||||
//slide-list的ref引用
|
//slide-list的ref引用
|
||||||
const wrapperEl = ref(null)
|
const slideListEl = ref(null)
|
||||||
|
|
||||||
const state = reactive({
|
const state = reactive({
|
||||||
judgeValue: 20, //一个用于判断滑动朝向的固定值
|
judgeValue: 20, //一个用于判断滑动朝向的固定值
|
||||||
@ -67,19 +67,19 @@ watch(
|
|||||||
if (state.localIndex !== newVal) {
|
if (state.localIndex !== newVal) {
|
||||||
state.localIndex = newVal
|
state.localIndex = newVal
|
||||||
if (props.changeActiveIndexUseAnim) {
|
if (props.changeActiveIndexUseAnim) {
|
||||||
_css(wrapperEl.value, 'transition-duration', `300ms`)
|
_css(slideListEl.value, 'transition-duration', `300ms`)
|
||||||
}
|
}
|
||||||
_css(
|
_css(
|
||||||
wrapperEl.value,
|
slideListEl.value,
|
||||||
'transform',
|
'transform',
|
||||||
`translate3d(${getSlideOffset(state, wrapperEl.value)}px, 0, 0)`
|
`translate3d(${getSlideOffset(state, slideListEl.value)}px, 0, 0)`
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
slideInit(wrapperEl.value, state)
|
slideInit(slideListEl.value, state)
|
||||||
|
|
||||||
if (props.autoplay) {
|
if (props.autoplay) {
|
||||||
setInterval(() => {
|
setInterval(() => {
|
||||||
@ -94,9 +94,9 @@ onMounted(() => {
|
|||||||
//观察子元素数量变动,获取最新数量
|
//观察子元素数量变动,获取最新数量
|
||||||
//childrenLength用于canNext方法判断当前页是否是最后一页,是则不能滑动,不捕获事件
|
//childrenLength用于canNext方法判断当前页是否是最后一页,是则不能滑动,不捕获事件
|
||||||
ob = new MutationObserver(() => {
|
ob = new MutationObserver(() => {
|
||||||
state.wrapper.childrenLength = wrapperEl.value.children.length
|
state.wrapper.childrenLength = slideListEl.value.children.length
|
||||||
})
|
})
|
||||||
ob.observe(wrapperEl.value, { childList: true })
|
ob.observe(slideListEl.value, { childList: true })
|
||||||
})
|
})
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
@ -104,16 +104,16 @@ onUnmounted(() => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
function touchStart(e) {
|
function touchStart(e) {
|
||||||
slideTouchStart(e, wrapperEl.value, state)
|
slideTouchStart(e, slideListEl.value, state)
|
||||||
}
|
}
|
||||||
|
|
||||||
function touchMove(e) {
|
function touchMove(e) {
|
||||||
slideTouchMove(e, wrapperEl.value, state)
|
slideTouchMove(e, slideListEl.value, state)
|
||||||
}
|
}
|
||||||
|
|
||||||
function touchEnd(e) {
|
function touchEnd(e) {
|
||||||
slideTouchEnd(e, state)
|
slideTouchEnd(e, state)
|
||||||
slideReset(e, wrapperEl.value, state, emit)
|
slideReset(e, slideListEl.value, state, emit)
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@ -130,7 +130,7 @@ function touchEnd(e) {
|
|||||||
|
|
||||||
<div
|
<div
|
||||||
class="slide-list"
|
class="slide-list"
|
||||||
ref="wrapperEl"
|
ref="slideListEl"
|
||||||
@pointerdown="touchStart"
|
@pointerdown="touchStart"
|
||||||
@pointermove="touchMove"
|
@pointermove="touchMove"
|
||||||
@pointerup="touchEnd"
|
@pointerup="touchEnd"
|
||||||
|
|||||||
@ -31,7 +31,7 @@ const props = defineProps({
|
|||||||
const emit = defineEmits(['update:index'])
|
const emit = defineEmits(['update:index'])
|
||||||
|
|
||||||
//slide-list的ref引用
|
//slide-list的ref引用
|
||||||
const wrapperEl = ref(null)
|
const slideListEl = ref(null)
|
||||||
|
|
||||||
const state = reactive({
|
const state = reactive({
|
||||||
judgeValue: 20, //一个用于判断滑动朝向的固定值
|
judgeValue: 20, //一个用于判断滑动朝向的固定值
|
||||||
@ -58,32 +58,32 @@ watch(
|
|||||||
if (state.localIndex !== newVal) {
|
if (state.localIndex !== newVal) {
|
||||||
state.localIndex = newVal
|
state.localIndex = newVal
|
||||||
if (props.changeActiveIndexUseAnim) {
|
if (props.changeActiveIndexUseAnim) {
|
||||||
_css(wrapperEl.value, 'transition-duration', `300ms`)
|
_css(slideListEl.value, 'transition-duration', `300ms`)
|
||||||
}
|
}
|
||||||
_css(
|
_css(
|
||||||
wrapperEl.value,
|
slideListEl.value,
|
||||||
'transform',
|
'transform',
|
||||||
`translate3d(0,${getSlideOffset(state, wrapperEl.value)}px, 0)`
|
`translate3d(0,${getSlideOffset(state, slideListEl.value)}px, 0)`
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
slideInit(wrapperEl.value, state)
|
slideInit(slideListEl.value, state)
|
||||||
})
|
})
|
||||||
|
|
||||||
function touchStart(e) {
|
function touchStart(e) {
|
||||||
slideTouchStart(e, wrapperEl.value, state)
|
slideTouchStart(e, slideListEl.value, state)
|
||||||
}
|
}
|
||||||
|
|
||||||
function touchMove(e) {
|
function touchMove(e) {
|
||||||
slideTouchMove(e, wrapperEl.value, state)
|
slideTouchMove(e, slideListEl.value, state)
|
||||||
}
|
}
|
||||||
|
|
||||||
function touchEnd(e) {
|
function touchEnd(e) {
|
||||||
slideTouchEnd(e, state)
|
slideTouchEnd(e, state)
|
||||||
slideReset(e, wrapperEl.value, state, emit)
|
slideReset(e, slideListEl.value, state, emit)
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@ -91,7 +91,7 @@ function touchEnd(e) {
|
|||||||
<div class="slide v">
|
<div class="slide v">
|
||||||
<div
|
<div
|
||||||
class="slide-list flex-direction-column"
|
class="slide-list flex-direction-column"
|
||||||
ref="wrapperEl"
|
ref="slideListEl"
|
||||||
@pointerdown="touchStart"
|
@pointerdown="touchStart"
|
||||||
@pointermove="touchMove"
|
@pointermove="touchMove"
|
||||||
@pointerup="touchEnd"
|
@pointerup="touchEnd"
|
||||||
|
|||||||
@ -34,6 +34,7 @@ const props = defineProps({
|
|||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
//页面中同时存在多少个SlideItem
|
||||||
virtualTotal: {
|
virtualTotal: {
|
||||||
type: Number,
|
type: Number,
|
||||||
default: () => 5
|
default: () => 5
|
||||||
@ -59,7 +60,7 @@ const emit = defineEmits(['update:index', 'loadMore', 'refresh'])
|
|||||||
|
|
||||||
const appInsMap = new Map()
|
const appInsMap = new Map()
|
||||||
const itemClassName = 'slide-item'
|
const itemClassName = 'slide-item'
|
||||||
const wrapperEl = ref<HTMLDivElement>(null)
|
const slideListEl = ref<HTMLDivElement>(null)
|
||||||
const state = reactive({
|
const state = reactive({
|
||||||
judgeValue: 20,
|
judgeValue: 20,
|
||||||
type: SlideType.VERTICAL_INFINITE,
|
type: SlideType.VERTICAL_INFINITE,
|
||||||
@ -90,7 +91,7 @@ watch(
|
|||||||
// 为了在用户快速滑动时,无需频繁等待请求接口加载数据,给用户更好的使用体验
|
// 为了在用户快速滑动时,无需频繁等待请求接口加载数据,给用户更好的使用体验
|
||||||
// 这里额外加载3条数据。所以此刻,html里面有原本的5个加新增的3个,一共8个dom
|
// 这里额外加载3条数据。所以此刻,html里面有原本的5个加新增的3个,一共8个dom
|
||||||
// 用户往下滑动时只删除前面多余的dom,等滑动到临界值(virtualTotal/2+1)时,再去执行新增逻辑
|
// 用户往下滑动时只删除前面多余的dom,等滑动到临界值(virtualTotal/2+1)时,再去执行新增逻辑
|
||||||
// let lastSlideItem = wrapperEl.value.querySelector(`.${itemClassName}:last-child`)
|
// let lastSlideItem = slideListEl.value.querySelector(`.${itemClassName}:last-child`)
|
||||||
// let top = _css(lastSlideItem, 'top')
|
// let top = _css(lastSlideItem, 'top')
|
||||||
// let newListStartIndex = Number(lastSlideItem.getAttribute('data-index')) + 1
|
// let newListStartIndex = Number(lastSlideItem.getAttribute('data-index')) + 1
|
||||||
// // console.log('newListStartIndex', newListStartIndex)
|
// // console.log('newListStartIndex', newListStartIndex)
|
||||||
@ -101,7 +102,7 @@ watch(
|
|||||||
// //因为有一条情况:当滑动最后一条和二条的时候top值不会继续加。此时新增的数据如果还
|
// //因为有一条情况:当滑动最后一条和二条的时候top值不会继续加。此时新增的数据如果还
|
||||||
// // 计算top值的,会和前面的对不上
|
// // 计算top值的,会和前面的对不上
|
||||||
// _css(el, 'top', top)
|
// _css(el, 'top', top)
|
||||||
// wrapperEl.value.appendChild(el)
|
// slideListEl.value.appendChild(el)
|
||||||
// state.wrapper.childrenLength++
|
// state.wrapper.childrenLength++
|
||||||
// })
|
// })
|
||||||
}
|
}
|
||||||
@ -152,37 +153,46 @@ watch(
|
|||||||
)
|
)
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
slideInit(wrapperEl.value, state)
|
slideInit(slideListEl.value, state)
|
||||||
})
|
})
|
||||||
|
|
||||||
function insertContent(list = props.list) {
|
/**
|
||||||
if (!list.length) return
|
* 插入SlideItem
|
||||||
wrapperEl.value.innerHTML = ''
|
*/
|
||||||
|
function insertContent() {
|
||||||
|
if (!props.list.length) return
|
||||||
|
//清空SlideList
|
||||||
|
slideListEl.value.innerHTML = ''
|
||||||
let half = (props.virtualTotal - 1) / 2
|
let half = (props.virtualTotal - 1) / 2
|
||||||
|
//因为我们只渲染 props.virtualTotal 条数据到dom中,并且当前index有可能不是0,所以需要计算出起始下标和结束下标
|
||||||
let start = 0
|
let start = 0
|
||||||
if (state.localIndex >= half) {
|
if (state.localIndex > half) {
|
||||||
start = state.localIndex - half
|
start = state.localIndex - half
|
||||||
}
|
}
|
||||||
let end = start + props.virtualTotal
|
let end = start + props.virtualTotal
|
||||||
if (end >= list.length) {
|
if (end >= props.list.length) {
|
||||||
end = list.length
|
end = props.list.length
|
||||||
start = end - props.virtualTotal
|
start = end - props.virtualTotal
|
||||||
}
|
}
|
||||||
if (start < 0) start = 0
|
if (start < 0) start = 0
|
||||||
|
|
||||||
// console.log('start', start, end)
|
// console.log('start', start, end)
|
||||||
list.slice(start, end).map((item, index) => {
|
//插入start到end范围内的数据到dom中
|
||||||
//自动播放,当前条(可能是0,可能是其他),试了下用jq来找元素,然后trigger play事件,要慢点样
|
props.list.slice(start, end).map((item, index) => {
|
||||||
let el = getInsEl(item, start + index, start + index === state.localIndex)
|
let el = getInsEl(item, start + index, start + index === state.localIndex)
|
||||||
wrapperEl.value.appendChild(el)
|
slideListEl.value.appendChild(el)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
//设置SlideList的偏移量
|
||||||
_css(
|
_css(
|
||||||
wrapperEl.value,
|
slideListEl.value,
|
||||||
'transform',
|
'transform',
|
||||||
`translate3d(0px,${getSlideOffset(state, wrapperEl.value)}px, 0px)`
|
`translate3d(0px,${getSlideOffset(state, slideListEl.value)}px, 0px)`
|
||||||
)
|
)
|
||||||
|
|
||||||
if (state.localIndex > 2 && list.length > 5) {
|
//因为index有可能不是0,所以要设置Item的top偏移量
|
||||||
let list = wrapperEl.value.querySelectorAll(`.${itemClassName}`)
|
if (state.localIndex > 2 && props.list.length > 5) {
|
||||||
|
let list = slideListEl.value.querySelectorAll(`.${itemClassName}`)
|
||||||
list.forEach((item) => {
|
list.forEach((item) => {
|
||||||
if (list.length - state.localIndex > 2) {
|
if (list.length - state.localIndex > 2) {
|
||||||
_css(item, 'top', (state.localIndex - 2) * state.wrapper.height)
|
_css(item, 'top', (state.localIndex - 2) * state.wrapper.height)
|
||||||
@ -191,13 +201,13 @@ function insertContent(list = props.list) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
state.wrapper.childrenLength = wrapperEl.value.children.length
|
state.wrapper.childrenLength = slideListEl.value.children.length
|
||||||
// console.log('list[state.localIndex]',list[state.localIndex])
|
// console.log('list[state.localIndex]',list[state.localIndex])
|
||||||
bus.emit(EVENT_KEY.CURRENT_ITEM, list[state.localIndex])
|
bus.emit(EVENT_KEY.CURRENT_ITEM, props.list[state.localIndex])
|
||||||
}
|
}
|
||||||
|
|
||||||
function dislike() {
|
function dislike() {
|
||||||
// let currentItem = $(wrapperEl.value).find(`.${itemClassName}[data-index=${state.localIndex}]`)
|
// let currentItem = $(slideListEl.value).find(`.${itemClassName}[data-index=${state.localIndex}]`)
|
||||||
// let replaceItem = getInsEl(item, state.localIndex, true)
|
// let replaceItem = getInsEl(item, state.localIndex, true)
|
||||||
// $(replaceItem).css('top', currentItem.css('top'))
|
// $(replaceItem).css('top', currentItem.css('top'))
|
||||||
// currentItem.replaceWith(replaceItem)
|
// currentItem.replaceWith(replaceItem)
|
||||||
@ -205,6 +215,12 @@ function dislike() {
|
|||||||
|
|
||||||
defineExpose({ dislike })
|
defineExpose({ dislike })
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取Vue组件渲染之后的dom元素
|
||||||
|
* @param item
|
||||||
|
* @param index
|
||||||
|
* @param play
|
||||||
|
*/
|
||||||
function getInsEl(item, index, play = false) {
|
function getInsEl(item, index, play = false) {
|
||||||
// console.log('index', cloneDeep(item), index, play)
|
// console.log('index', cloneDeep(item), index, play)
|
||||||
let slideVNode = props.render(item, index, play, props.uniqueId)
|
let slideVNode = props.render(item, index, play, props.uniqueId)
|
||||||
@ -213,6 +229,7 @@ function getInsEl(item, index, play = false) {
|
|||||||
if (import.meta.env.PROD) {
|
if (import.meta.env.PROD) {
|
||||||
parent.classList.add('slide-item')
|
parent.classList.add('slide-item')
|
||||||
parent.setAttribute('data-index', index)
|
parent.setAttribute('data-index', index)
|
||||||
|
//将Vue组件渲染到一个div上
|
||||||
vueRender(slideVNode, parent)
|
vueRender(slideVNode, parent)
|
||||||
appInsMap.set(index, {
|
appInsMap.set(index, {
|
||||||
unmount: () => {
|
unmount: () => {
|
||||||
@ -222,6 +239,7 @@ function getInsEl(item, index, play = false) {
|
|||||||
})
|
})
|
||||||
return parent
|
return parent
|
||||||
} else {
|
} else {
|
||||||
|
//创建一个新的Vue实例,并挂载到一个div上
|
||||||
const app = createApp({
|
const app = createApp({
|
||||||
render() {
|
render() {
|
||||||
return <SlideItem data-index={index}>{slideVNode}</SlideItem>
|
return <SlideItem data-index={index}>{slideVNode}</SlideItem>
|
||||||
@ -234,12 +252,11 @@ function getInsEl(item, index, play = false) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function touchStart(e) {
|
function touchStart(e) {
|
||||||
slideTouchStart(e, wrapperEl.value, state)
|
slideTouchStart(e, slideListEl.value, state)
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO 2022-3-28:在最顶部,反复滑动会抖动一下,初步猜测是因为方向变了,导致的加判断距离变成了减
|
|
||||||
function touchMove(e) {
|
function touchMove(e) {
|
||||||
slideTouchMove(e, wrapperEl.value, state, canNext)
|
slideTouchMove(e, slideListEl.value, state, canNext)
|
||||||
}
|
}
|
||||||
|
|
||||||
function touchEnd(e) {
|
function touchEnd(e) {
|
||||||
@ -252,73 +269,73 @@ function touchEnd(e) {
|
|||||||
emit('refresh')
|
emit('refresh')
|
||||||
}
|
}
|
||||||
slideTouchEnd(e, state, canNext, (isNext) => {
|
slideTouchEnd(e, state, canNext, (isNext) => {
|
||||||
let half = (props.virtualTotal + 1) / 2
|
let half = (props.virtualTotal - 1) / 2
|
||||||
if (props.list.length > props.virtualTotal) {
|
if (props.list.length > props.virtualTotal) {
|
||||||
|
//往下滑
|
||||||
if (isNext) {
|
if (isNext) {
|
||||||
if (state.localIndex > props.list.length - props.virtualTotal && state.localIndex >= half) {
|
if (state.localIndex > props.list.length - props.virtualTotal && state.localIndex > half) {
|
||||||
emit('loadMore')
|
emit('loadMore')
|
||||||
}
|
}
|
||||||
let addItemIndex = state.localIndex + 2
|
let addItemIndex = state.localIndex + half
|
||||||
let res = wrapperEl.value.querySelector(`.${itemClassName}[data-index='${addItemIndex}']`)
|
let res = slideListEl.value.querySelector(`.${itemClassName}[data-index='${addItemIndex}']`)
|
||||||
if (state.wrapper.childrenLength < props.virtualTotal) {
|
|
||||||
if (!res) {
|
if (!res) {
|
||||||
wrapperEl.value.appendChild(getInsEl(props.list[addItemIndex], addItemIndex))
|
slideListEl.value.appendChild(getInsEl(props.list[addItemIndex], addItemIndex))
|
||||||
}
|
}
|
||||||
}
|
if (state.localIndex > half) {
|
||||||
if (
|
let index = slideListEl.value
|
||||||
state.wrapper.childrenLength === props.virtualTotal &&
|
|
||||||
state.localIndex >= (props.virtualTotal + 1) / 2 &&
|
|
||||||
state.localIndex <= props.list.length - 3
|
|
||||||
) {
|
|
||||||
if (!res) {
|
|
||||||
wrapperEl.value.appendChild(getInsEl(props.list[addItemIndex], addItemIndex))
|
|
||||||
let index = wrapperEl.value
|
|
||||||
.querySelector(`.${itemClassName}:first-child`)
|
.querySelector(`.${itemClassName}:first-child`)
|
||||||
.getAttribute('data-index')
|
.getAttribute('data-index')
|
||||||
appInsMap.get(Number(index)).unmount()
|
appInsMap.get(Number(index)).unmount()
|
||||||
wrapperEl.value.querySelectorAll(`.${itemClassName}`).forEach((item) => {
|
|
||||||
_css(item, 'top', (state.localIndex - 2) * state.wrapper.height)
|
slideListEl.value.querySelectorAll(`.${itemClassName}`).forEach((item) => {
|
||||||
})
|
_css(item, 'top', (state.localIndex - half) * state.wrapper.height)
|
||||||
}
|
|
||||||
}
|
|
||||||
if (state.wrapper.childrenLength > props.virtualTotal) {
|
|
||||||
wrapperEl.value.querySelectorAll(`.${itemClassName}`).forEach((item) => {
|
|
||||||
let index = Number(item.getAttribute('data-index'))
|
|
||||||
if (index < state.localIndex - 2) {
|
|
||||||
appInsMap.get(index).unmount()
|
|
||||||
}
|
|
||||||
_css(item, 'top', (state.localIndex - 2) * state.wrapper.height)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let addItemIndex = state.localIndex - 2
|
let addIndex = state.localIndex - half
|
||||||
let res = wrapperEl.value.querySelector(`.${itemClassName}[data-index='${addItemIndex}']`)
|
if (addIndex >= 0) {
|
||||||
|
let res = slideListEl.value.querySelector(`.${itemClassName}[data-index='${addIndex}']`)
|
||||||
if (state.localIndex > 1 && state.localIndex <= props.list.length - 4) {
|
|
||||||
if (!res) {
|
if (!res) {
|
||||||
wrapperEl.value.prepend(getInsEl(props.list[addItemIndex], addItemIndex))
|
slideListEl.value.prepend(getInsEl(props.list[addIndex], addIndex))
|
||||||
let index = wrapperEl.value
|
|
||||||
.querySelector(`.${itemClassName}:last-child`)
|
|
||||||
.getAttribute('data-index')
|
|
||||||
appInsMap.get(Number(index)).unmount()
|
|
||||||
// $(wrapperEl.value).find(".base-slide-item:last").remove()
|
|
||||||
wrapperEl.value.querySelectorAll(`.${itemClassName}`).forEach((item) => {
|
|
||||||
_css(item, 'top', (state.localIndex - 2) * state.wrapper.height)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state.wrapper.childrenLength > props.virtualTotal) {
|
if (state.localIndex >= half) {
|
||||||
let index = wrapperEl.value
|
let index = slideListEl.value
|
||||||
.querySelector(`.${itemClassName}:last-child`)
|
.querySelector(`.${itemClassName}:last-child`)
|
||||||
.getAttribute('data-index')
|
.getAttribute('data-index')
|
||||||
appInsMap.get(Number(index)).unmount()
|
appInsMap.get(Number(index)).unmount()
|
||||||
|
|
||||||
|
slideListEl.value.querySelectorAll(`.${itemClassName}`).forEach((item) => {
|
||||||
|
_css(item, 'top', (state.localIndex - half) * state.wrapper.height)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if (state.localIndex > 1 && state.localIndex <= props.list.length - 4) {
|
||||||
|
// if (!res) {
|
||||||
|
// slideListEl.value.prepend(getInsEl(props.list[addIndex], addIndex))
|
||||||
|
// let index = slideListEl.value
|
||||||
|
// .querySelector(`.${itemClassName}:last-child`)
|
||||||
|
// .getAttribute('data-index')
|
||||||
|
// appInsMap.get(Number(index)).unmount()
|
||||||
|
// // $(slideListEl.value).find(".base-slide-item:last").remove()
|
||||||
|
// slideListEl.value.querySelectorAll(`.${itemClassName}`).forEach((item) => {
|
||||||
|
// _css(item, 'top', (state.localIndex - 2) * state.wrapper.height)
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// if (state.wrapper.childrenLength > props.virtualTotal) {
|
||||||
|
// let index = slideListEl.value
|
||||||
|
// .querySelector(`.${itemClassName}:last-child`)
|
||||||
|
// .getAttribute('data-index')
|
||||||
|
// appInsMap.get(Number(index)).unmount()
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
state.wrapper.childrenLength = wrapperEl.value.children.length
|
state.wrapper.childrenLength = slideListEl.value.children.length
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
slideReset(e, wrapperEl.value, state, emit)
|
slideReset(e, slideListEl.value, state, emit)
|
||||||
}
|
}
|
||||||
|
|
||||||
function canNext(state, isNext) {
|
function canNext(state, isNext) {
|
||||||
@ -334,7 +351,7 @@ function canNext(state, isNext) {
|
|||||||
<Loading v-if="props.loading && props.list.length === 0" />
|
<Loading v-if="props.loading && props.list.length === 0" />
|
||||||
<div
|
<div
|
||||||
class="slide-list flex-direction-column"
|
class="slide-list flex-direction-column"
|
||||||
ref="wrapperEl"
|
ref="slideListEl"
|
||||||
@click="null"
|
@click="null"
|
||||||
@pointerdown="touchStart"
|
@pointerdown="touchStart"
|
||||||
@pointermove="touchMove"
|
@pointermove="touchMove"
|
||||||
|
|||||||
365
src/components/slide/SlideVerticalInfinite2.vue
Normal file
365
src/components/slide/SlideVerticalInfinite2.vue
Normal file
@ -0,0 +1,365 @@
|
|||||||
|
<script setup lang="tsx">
|
||||||
|
import { createApp, onMounted, reactive, ref, render as vueRender, watch } from 'vue'
|
||||||
|
import {
|
||||||
|
getSlideOffset,
|
||||||
|
slideInit,
|
||||||
|
slideReset,
|
||||||
|
slideTouchEnd,
|
||||||
|
slideTouchMove,
|
||||||
|
slideTouchStart
|
||||||
|
} from '@/utils/slide'
|
||||||
|
import { SlideType } from '@/utils/const_var'
|
||||||
|
import SlideItem from '@/components/slide/SlideItem.vue'
|
||||||
|
import bus, { EVENT_KEY } from '../../utils/bus'
|
||||||
|
import Loading from '@/components/Loading.vue'
|
||||||
|
import { useBaseStore } from '@/store/pinia'
|
||||||
|
import { _css } from '@/utils/dom'
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
index: {
|
||||||
|
type: Number,
|
||||||
|
default: () => {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
render: {
|
||||||
|
type: Function,
|
||||||
|
default: () => {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
list: {
|
||||||
|
type: Array,
|
||||||
|
default: () => {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
//页面中同时存在多少个SlideItem
|
||||||
|
virtualTotal: {
|
||||||
|
type: Number,
|
||||||
|
default: () => 5
|
||||||
|
},
|
||||||
|
name: {
|
||||||
|
type: String,
|
||||||
|
default: () => ''
|
||||||
|
},
|
||||||
|
uniqueId: {
|
||||||
|
type: String,
|
||||||
|
default: () => ''
|
||||||
|
},
|
||||||
|
loading: {
|
||||||
|
type: Boolean,
|
||||||
|
default: () => false
|
||||||
|
},
|
||||||
|
active: {
|
||||||
|
type: Boolean,
|
||||||
|
default: () => false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const emit = defineEmits(['update:index', 'loadMore', 'refresh'])
|
||||||
|
|
||||||
|
const appInsMap = new Map()
|
||||||
|
const itemClassName = 'slide-item'
|
||||||
|
const slideListEl = ref<HTMLDivElement>(null)
|
||||||
|
const state = reactive({
|
||||||
|
judgeValue: 20,
|
||||||
|
type: SlideType.VERTICAL_INFINITE,
|
||||||
|
name: props.name,
|
||||||
|
localIndex: props.index,
|
||||||
|
needCheck: true,
|
||||||
|
next: false,
|
||||||
|
isDown: false,
|
||||||
|
start: { x: 0, y: 0, time: 0 },
|
||||||
|
move: { x: 0, y: 0 },
|
||||||
|
wrapper: { width: 0, height: 0, childrenLength: 0 }
|
||||||
|
})
|
||||||
|
const baseStore = useBaseStore()
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.list,
|
||||||
|
(newVal, oldVal) => {
|
||||||
|
// console.log('watch-list', newVal.length, oldVal.length, newVal)
|
||||||
|
//新数据长度比老数据长度小,说明是刷新
|
||||||
|
if (newVal.length < oldVal.length) {
|
||||||
|
insertContent()
|
||||||
|
} else {
|
||||||
|
//没数据就直接插入
|
||||||
|
if (oldVal.length === 0) {
|
||||||
|
insertContent()
|
||||||
|
} else {
|
||||||
|
// 走到这里,说明是通过接口加载了下一页的数据,
|
||||||
|
// 为了在用户快速滑动时,无需频繁等待请求接口加载数据,给用户更好的使用体验
|
||||||
|
// 这里额外加载3条数据。所以此刻,html里面有原本的5个加新增的3个,一共8个dom
|
||||||
|
// 用户往下滑动时只删除前面多余的dom,等滑动到临界值(virtualTotal/2+1)时,再去执行新增逻辑
|
||||||
|
// let lastSlideItem = slideListEl.value.querySelector(`.${itemClassName}:last-child`)
|
||||||
|
// let top = _css(lastSlideItem, 'top')
|
||||||
|
// let newListStartIndex = Number(lastSlideItem.getAttribute('data-index')) + 1
|
||||||
|
// // console.log('newListStartIndex', newListStartIndex)
|
||||||
|
// newVal.slice(newListStartIndex, newListStartIndex + 3).map((item, index) => {
|
||||||
|
// let el = getInsEl(item, newListStartIndex + index)
|
||||||
|
// //这里必须要设置个top值,不然会把前面的条目给覆盖掉
|
||||||
|
// //2022-3-27,这里不用计算,直接用已用slide-item最后一条的top值,
|
||||||
|
// //因为有一条情况:当滑动最后一条和二条的时候top值不会继续加。此时新增的数据如果还
|
||||||
|
// // 计算top值的,会和前面的对不上
|
||||||
|
// _css(el, 'top', top)
|
||||||
|
// slideListEl.value.appendChild(el)
|
||||||
|
// state.wrapper.childrenLength++
|
||||||
|
// })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.index,
|
||||||
|
(newVal, oldVal) => {
|
||||||
|
state.localIndex = newVal
|
||||||
|
// console.log('watch-index', newVal, oldVal)
|
||||||
|
if (!props.list.length) return
|
||||||
|
bus.emit(EVENT_KEY.CURRENT_ITEM, props.list[newVal])
|
||||||
|
bus.emit(EVENT_KEY.SINGLE_CLICK_BROADCAST, {
|
||||||
|
uniqueId: props.uniqueId,
|
||||||
|
index: newVal,
|
||||||
|
type: EVENT_KEY.ITEM_PLAY
|
||||||
|
})
|
||||||
|
setTimeout(() => {
|
||||||
|
bus.emit(EVENT_KEY.SINGLE_CLICK_BROADCAST, {
|
||||||
|
uniqueId: props.uniqueId,
|
||||||
|
index: oldVal,
|
||||||
|
type: EVENT_KEY.ITEM_STOP
|
||||||
|
})
|
||||||
|
}, 200)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.active,
|
||||||
|
(newVal) => {
|
||||||
|
//当激活此页时,如果list为空,那么向上发射事件通知父组件请求数据
|
||||||
|
if (newVal && !props.list.length) {
|
||||||
|
return emit('refresh')
|
||||||
|
}
|
||||||
|
// console.log('active', 'newVal', newVal, 'oldVal', oldVal)
|
||||||
|
if (newVal) {
|
||||||
|
bus.emit(EVENT_KEY.CURRENT_ITEM, props.list[state.localIndex])
|
||||||
|
}
|
||||||
|
bus.emit(EVENT_KEY.SINGLE_CLICK_BROADCAST, {
|
||||||
|
uniqueId: props.uniqueId,
|
||||||
|
index: state.localIndex,
|
||||||
|
type: newVal === false ? EVENT_KEY.ITEM_STOP : EVENT_KEY.ITEM_PLAY
|
||||||
|
})
|
||||||
|
},
|
||||||
|
{ immediate: true }
|
||||||
|
)
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
slideInit(slideListEl.value, state)
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 插入SlideItem
|
||||||
|
*/
|
||||||
|
function insertContent() {
|
||||||
|
if (!props.list.length) return
|
||||||
|
//清空SlideList
|
||||||
|
slideListEl.value.innerHTML = ''
|
||||||
|
let half = (props.virtualTotal - 1) / 2
|
||||||
|
//因为我们只渲染 props.virtualTotal 条数据到dom中,并且当前index有可能不是0,所以需要计算出起始下标和结束下标
|
||||||
|
let start = 0
|
||||||
|
if (state.localIndex > half) {
|
||||||
|
start = state.localIndex - half
|
||||||
|
}
|
||||||
|
let end = start + props.virtualTotal
|
||||||
|
if (end >= props.list.length) {
|
||||||
|
end = props.list.length
|
||||||
|
start = end - props.virtualTotal
|
||||||
|
}
|
||||||
|
if (start < 0) start = 0
|
||||||
|
|
||||||
|
// console.log('start', start, end)
|
||||||
|
//插入start到end范围内的数据到dom中
|
||||||
|
props.list.slice(start, end).map((item, index) => {
|
||||||
|
let el = getInsEl(item, start + index, start + index === state.localIndex)
|
||||||
|
slideListEl.value.appendChild(el)
|
||||||
|
})
|
||||||
|
|
||||||
|
//设置SlideList的偏移量
|
||||||
|
_css(
|
||||||
|
slideListEl.value,
|
||||||
|
'transform',
|
||||||
|
`translate3d(0px,${getSlideOffset(state, slideListEl.value)}px, 0px)`
|
||||||
|
)
|
||||||
|
|
||||||
|
//因为index有可能不是0,所以要设置Item的top偏移量
|
||||||
|
if (state.localIndex > 2 && props.list.length > 5) {
|
||||||
|
let list = slideListEl.value.querySelectorAll(`.${itemClassName}`)
|
||||||
|
list.forEach((item) => {
|
||||||
|
if (list.length - state.localIndex > 2) {
|
||||||
|
_css(item, 'top', (state.localIndex - 2) * state.wrapper.height)
|
||||||
|
} else {
|
||||||
|
_css(item, 'top', start * state.wrapper.height)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
state.wrapper.childrenLength = slideListEl.value.children.length
|
||||||
|
// console.log('list[state.localIndex]',list[state.localIndex])
|
||||||
|
bus.emit(EVENT_KEY.CURRENT_ITEM, props.list[state.localIndex])
|
||||||
|
}
|
||||||
|
|
||||||
|
function dislike() {
|
||||||
|
// let currentItem = $(slideListEl.value).find(`.${itemClassName}[data-index=${state.localIndex}]`)
|
||||||
|
// let replaceItem = getInsEl(item, state.localIndex, true)
|
||||||
|
// $(replaceItem).css('top', currentItem.css('top'))
|
||||||
|
// currentItem.replaceWith(replaceItem)
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({ dislike })
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取Vue组件渲染之后的dom元素
|
||||||
|
* @param item
|
||||||
|
* @param index
|
||||||
|
* @param play
|
||||||
|
*/
|
||||||
|
function getInsEl(item, index, play = false) {
|
||||||
|
// console.log('index', cloneDeep(item), index, play)
|
||||||
|
let slideVNode = props.render(item, index, play, props.uniqueId)
|
||||||
|
const parent = document.createElement('div')
|
||||||
|
//TODO 打包到线上时用这个,这个在开发时任何修改都会刷新页面
|
||||||
|
if (import.meta.env.PROD) {
|
||||||
|
parent.classList.add('slide-item')
|
||||||
|
parent.setAttribute('data-index', index)
|
||||||
|
//将Vue组件渲染到一个div上
|
||||||
|
vueRender(slideVNode, parent)
|
||||||
|
appInsMap.set(index, {
|
||||||
|
unmount: () => {
|
||||||
|
vueRender(null, parent)
|
||||||
|
parent.remove()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return parent
|
||||||
|
} else {
|
||||||
|
//创建一个新的Vue实例,并挂载到一个div上
|
||||||
|
const app = createApp({
|
||||||
|
render() {
|
||||||
|
return <SlideItem data-index={index}>{slideVNode}</SlideItem>
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const ins = app.mount(parent)
|
||||||
|
appInsMap.set(index, app)
|
||||||
|
return ins.$el
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function touchStart(e) {
|
||||||
|
slideTouchStart(e, slideListEl.value, state)
|
||||||
|
}
|
||||||
|
|
||||||
|
function touchMove(e) {
|
||||||
|
slideTouchMove(e, slideListEl.value, state, canNext)
|
||||||
|
}
|
||||||
|
|
||||||
|
function touchEnd(e) {
|
||||||
|
let isNext = state.move.y < 0
|
||||||
|
if (
|
||||||
|
state.localIndex === 0 &&
|
||||||
|
!isNext &&
|
||||||
|
state.move.y > baseStore.homeRefresh + baseStore.judgeValue
|
||||||
|
) {
|
||||||
|
emit('refresh')
|
||||||
|
}
|
||||||
|
slideTouchEnd(e, state, canNext, (isNext) => {
|
||||||
|
let half = (props.virtualTotal - 1) / 2
|
||||||
|
if (props.list.length > props.virtualTotal) {
|
||||||
|
//往下滑
|
||||||
|
if (isNext) {
|
||||||
|
if (state.localIndex > props.list.length - props.virtualTotal && state.localIndex > half) {
|
||||||
|
emit('loadMore')
|
||||||
|
}
|
||||||
|
let addItemIndex = state.localIndex + 2
|
||||||
|
console.log('addItemIndex', addItemIndex)
|
||||||
|
let res = slideListEl.value.querySelector(`.${itemClassName}[data-index='${addItemIndex}']`)
|
||||||
|
if (state.wrapper.childrenLength < props.virtualTotal) {
|
||||||
|
if (!res) {
|
||||||
|
slideListEl.value.appendChild(getInsEl(props.list[addItemIndex], addItemIndex))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
state.wrapper.childrenLength === props.virtualTotal &&
|
||||||
|
state.localIndex > half &&
|
||||||
|
state.localIndex <= props.list.length - 3
|
||||||
|
) {
|
||||||
|
if (!res) {
|
||||||
|
slideListEl.value.appendChild(getInsEl(props.list[addItemIndex], addItemIndex))
|
||||||
|
let index = slideListEl.value
|
||||||
|
.querySelector(`.${itemClassName}:first-child`)
|
||||||
|
.getAttribute('data-index')
|
||||||
|
appInsMap.get(Number(index)).unmount()
|
||||||
|
slideListEl.value.querySelectorAll(`.${itemClassName}`).forEach((item) => {
|
||||||
|
_css(item, 'top', (state.localIndex - 2) * state.wrapper.height)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (state.wrapper.childrenLength > props.virtualTotal) {
|
||||||
|
slideListEl.value.querySelectorAll(`.${itemClassName}`).forEach((item) => {
|
||||||
|
let index = Number(item.getAttribute('data-index'))
|
||||||
|
if (index < state.localIndex - 2) {
|
||||||
|
appInsMap.get(index).unmount()
|
||||||
|
}
|
||||||
|
_css(item, 'top', (state.localIndex - 2) * state.wrapper.height)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let addItemIndex = state.localIndex - 2
|
||||||
|
let res = slideListEl.value.querySelector(`.${itemClassName}[data-index='${addItemIndex}']`)
|
||||||
|
|
||||||
|
if (state.localIndex > 1 && state.localIndex <= props.list.length - 4) {
|
||||||
|
if (!res) {
|
||||||
|
slideListEl.value.prepend(getInsEl(props.list[addItemIndex], addItemIndex))
|
||||||
|
let index = slideListEl.value
|
||||||
|
.querySelector(`.${itemClassName}:last-child`)
|
||||||
|
.getAttribute('data-index')
|
||||||
|
appInsMap.get(Number(index)).unmount()
|
||||||
|
// $(slideListEl.value).find(".base-slide-item:last").remove()
|
||||||
|
slideListEl.value.querySelectorAll(`.${itemClassName}`).forEach((item) => {
|
||||||
|
_css(item, 'top', (state.localIndex - 2) * state.wrapper.height)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state.wrapper.childrenLength > props.virtualTotal) {
|
||||||
|
let index = slideListEl.value
|
||||||
|
.querySelector(`.${itemClassName}:last-child`)
|
||||||
|
.getAttribute('data-index')
|
||||||
|
appInsMap.get(Number(index)).unmount()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
state.wrapper.childrenLength = slideListEl.value.children.length
|
||||||
|
}
|
||||||
|
})
|
||||||
|
slideReset(e, slideListEl.value, state, emit)
|
||||||
|
}
|
||||||
|
|
||||||
|
function canNext(state, isNext) {
|
||||||
|
return !(
|
||||||
|
(state.localIndex === 0 && !isNext) ||
|
||||||
|
(state.localIndex === props.list.length - 1 && isNext)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="slide slide-infinite">
|
||||||
|
<Loading v-if="props.loading && props.list.length === 0" />
|
||||||
|
<div
|
||||||
|
class="slide-list flex-direction-column"
|
||||||
|
ref="slideListEl"
|
||||||
|
@click="null"
|
||||||
|
@pointerdown="touchStart"
|
||||||
|
@pointermove="touchMove"
|
||||||
|
@pointerup="touchEnd"
|
||||||
|
>
|
||||||
|
<slot></slot>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
Loading…
Reference in New Issue
Block a user