261 lines
8.4 KiB
JavaScript
261 lines
8.4 KiB
JavaScript
import Dom from "../../utils/dom";
|
||
import * as Vue from "vue";
|
||
|
||
export default class Slide {
|
||
|
||
constructor(id, config = {index: 0}) {
|
||
this.slideList = this.create('<div class="slide-list"></div>')
|
||
this.slideList.addEventListener('touchstart', this.touchstart.bind(this))
|
||
this.slideList.addEventListener('touchmove', this.touchmove.bind(this))
|
||
this.slideList.addEventListener('touchend', this.touchend.bind(this))
|
||
|
||
let container = new Dom(id)
|
||
// container.css('height','100%')
|
||
container.css('width', '100%')
|
||
container.css('overflow', 'hidden')
|
||
container.append(new Dom(this.slideList))
|
||
this.total = 0
|
||
this.pageSize = 10
|
||
this.pageNo = 0
|
||
this.list = []
|
||
this.index = config.index || 0
|
||
this.listMap = new Map()
|
||
this.loading = false
|
||
|
||
|
||
this.judgeValue = 0
|
||
this.startTime = 0
|
||
this.startLocationX = 0
|
||
this.startLocationY = 0
|
||
this.moveXDistance = 0
|
||
this.moveYDistance = 0
|
||
this.virtualTotal = 5
|
||
|
||
this.height = parseFloat(container.css('height'))
|
||
this.isDrawDown = true
|
||
this.config = config
|
||
this.appInsMap = new Map()
|
||
|
||
|
||
this.isRecommend = !(this.config.list && this.config.list.length);
|
||
|
||
this.getData(this.pageNo)
|
||
}
|
||
|
||
async getData(pageNo, init = true) {
|
||
if (this.isRecommend) {
|
||
this.getRecommend(pageNo).then(r => {
|
||
init && this.init()
|
||
})
|
||
} else {
|
||
if (init) {
|
||
this.total = this.config.list.length
|
||
for (let i = 0; i < this.config.list.length / this.virtualTotal; i++) {
|
||
this.listMap.set(i, this.config.list.slice(i * this.virtualTotal, (i + 1) * this.virtualTotal))
|
||
}
|
||
this.init()
|
||
}
|
||
}
|
||
}
|
||
|
||
async getRecommend(pageNo) {
|
||
// if (pageNo === 1) return
|
||
if (this.config.request) {
|
||
if (this.listMap.has(pageNo)) return
|
||
this.loading = true
|
||
let res = await this.config.request({pageNo, pageSize: this.pageSize})
|
||
this.loading = true
|
||
if (res.code === 200) {
|
||
this.total = res.data.total
|
||
this.pageNo = pageNo
|
||
// console.log('请求数据', res.data.list)
|
||
this.listMap.set(pageNo, res.data.list)
|
||
// this.list = this.list.concat(res.data.list)
|
||
}
|
||
}
|
||
}
|
||
|
||
init() {
|
||
//计算出正确的开始下标和结束下标
|
||
// 情况一,数据是推荐的,默认取前面virtualTotal(5)条
|
||
// 情况二,数据是固定的,当前要播放的视频在中间,那么取前2,后2
|
||
// 情况二,数据是固定的,当前要播放的视频在后面,那么往前取,直到5条
|
||
let start = 0
|
||
let end = start + this.virtualTotal
|
||
if (this.total > this.virtualTotal) {
|
||
if (this.index > 1) start = this.index - 2
|
||
end = start + this.virtualTotal
|
||
if (end > this.total) {
|
||
start = start - (end - this.total)
|
||
}
|
||
}
|
||
console.log('startIndex', start)
|
||
console.log('endIndex', end)
|
||
this.getList().slice(start, end).map((v, i) => {
|
||
let el = this.getInsEl(v, start + i, false)
|
||
this.slideList.appendChild(el)
|
||
})
|
||
|
||
//this.total > this.virtualTotal,只有总条数在不少this.virtualTotal条数的情况下用top
|
||
//this.index > 1 和setTop保持统一,这里其实可以用this.index > 2
|
||
if (this.index > 1 && this.total > this.virtualTotal) {
|
||
this.css(this.slideList, 'transform', `translate3d(0px, ${this.getHeight()}px, 0px)`)
|
||
|
||
this.slideList.childNodes.forEach(v => {
|
||
//((this.total - 1 - this.index) > 1 ? 0 : 2),如果当前是最后两条,那么要多减去N个height
|
||
console.log('((this.total - this.index) > 1 ? 0 : 2)', ((this.total - 1 - this.index) > 1 ? 0 : this.total - 1 - this.index))
|
||
this.css(v, 'top', (this.index - 2 - ((this.total - 1 - this.index) > 1 ? 0 : this.total - 1 - this.index)) * this.height + 'px')
|
||
})
|
||
}
|
||
// this.setTop()
|
||
this.setActive()
|
||
}
|
||
|
||
getInsEl(item, index, play = false) {
|
||
// console.log('index',index,play)
|
||
let slideVNode = this.config.render(item, index, play)
|
||
const app = Vue.createApp({
|
||
render() {
|
||
return slideVNode
|
||
}
|
||
})
|
||
const ins = app.mount(document.createElement('div'))
|
||
this.appInsMap.set(index, app)
|
||
|
||
let parent = this.create(`<div class="slide-item slide-item-${index}"></div>`)
|
||
parent.appendChild(ins.$el)
|
||
return parent
|
||
}
|
||
|
||
touchstart(e) {
|
||
// console.log('touchstart')
|
||
this.startLocationX = e.touches[0].pageX
|
||
this.startLocationY = e.touches[0].pageY
|
||
this.startTime = Date.now()
|
||
// console.log('touchstart', this.startTime)
|
||
this.css(this.slideList, 'transition-duration', '0ms')
|
||
}
|
||
|
||
touchmove(e) {
|
||
this.moveXDistance = e.touches[0].pageX - this.startLocationX
|
||
this.moveYDistance = e.touches[0].pageY - this.startLocationY
|
||
// console.log('touchmove', this.moveXDistance)
|
||
// console.log('touchmove', this.moveYDistance)
|
||
this.isDrawDown = this.moveYDistance < 0
|
||
// console.log('isDrawDown', this.isDrawDown)
|
||
if (this.isDrawDown) {
|
||
if ( this.index === this.getList().length - 1) {
|
||
this.css(this.slideList, 'transform', `translate3d(0px, ${this.getHeight() + (Math.abs(this.moveYDistance) > this.height / 5 ? -this.height / 5 : this.moveYDistance)}px, 0px)`)
|
||
return
|
||
}
|
||
} else {
|
||
if (this.index === 0) return
|
||
}
|
||
|
||
this.css(this.slideList, 'transform', `translate3d(0px, ${this.getHeight() + this.moveYDistance + (this.isDrawDown ? this.judgeValue : -this.judgeValue)}px, 0px)`)
|
||
}
|
||
|
||
touchend() {
|
||
//如果向下划,并且加载中,并且还是已有数据的最后一条
|
||
if (this.isDrawDown && this.loading && this.index === this.getList().length - 1) {
|
||
return console.log('加载中')
|
||
}
|
||
|
||
let canSlide = this.height / 8 < Math.abs(this.moveYDistance) && Math.abs(this.moveYDistance) > 40;
|
||
if (Date.now() - this.startTime < 40) canSlide = false
|
||
|
||
if (canSlide) {
|
||
if (this.isDrawDown) {
|
||
if (this.index < this.getList().length - 1) {
|
||
this.index += 1
|
||
}
|
||
if (this.index < this.total - 2) {
|
||
let addIndex = this.index + 2
|
||
let removeIndex = this.index - 3
|
||
|
||
//如果没有新数据,则不进行操作
|
||
if (this.getList()[addIndex]) {
|
||
let res = this.slideList.querySelector(`.slide-item-${addIndex}`)
|
||
if (!res) {
|
||
this.slideList.appendChild(this.getInsEl(this.getList()[addIndex], addIndex))
|
||
}
|
||
let res2 = this.slideList.querySelector(`.slide-item-${removeIndex}`)
|
||
if (res2) {
|
||
// this.appInsMap.get(removeIndex).unmount()
|
||
this.slideList.removeChild(res2)
|
||
}
|
||
} else {
|
||
this.getData(this.pageNo + 1, false)
|
||
console.log('没有这条数据')
|
||
}
|
||
|
||
if (this.index + 5 > this.getList().length) {
|
||
this.getData(this.pageNo + 1, false)
|
||
}
|
||
}
|
||
} else {
|
||
if (this.index > 0) {
|
||
this.index -= 1
|
||
}
|
||
if (this.index > 1) {
|
||
let addIndex = this.index - 2
|
||
let removeIndex = this.index + 3
|
||
|
||
let res = this.slideList.querySelector(`.slide-item-${addIndex}`)
|
||
if (!res) {
|
||
this.slideList.insertBefore(this.getInsEl(this.getList()[addIndex], addIndex), this.slideList.firstChild)
|
||
}
|
||
let res2 = this.slideList.querySelector(`.slide-item-${removeIndex}`)
|
||
if (res2) {
|
||
// this.appInsMap.get(removeIndex).unmount()
|
||
this.slideList.removeChild(res2)
|
||
}
|
||
}
|
||
}
|
||
this.setTop()
|
||
this.setActive()
|
||
}
|
||
|
||
|
||
this.css(this.slideList, 'transition-duration', '300ms')
|
||
this.css(this.slideList, 'transform', `translate3d(0px, ${this.getHeight()}px, 0px)`)
|
||
}
|
||
|
||
getList() {
|
||
return Array.from(this.listMap.values()).flat()
|
||
}
|
||
|
||
create(template) {
|
||
let tempNode = document.createElement('div');
|
||
tempNode.innerHTML = template.trim();
|
||
return tempNode.firstChild;
|
||
}
|
||
|
||
css(el, ...args) {
|
||
el.style[args[0]] = args[1]
|
||
}
|
||
|
||
getHeight() {
|
||
return -this.index * this.height
|
||
}
|
||
|
||
setActive() {
|
||
this.slideList.childNodes.forEach(v => {
|
||
v.classList.remove('active')
|
||
})
|
||
this.slideList.childNodes.forEach(v => {
|
||
if (v.classList.value.search(this.index) !== -1) {
|
||
v.classList.add('active')
|
||
}
|
||
})
|
||
}
|
||
|
||
setTop() {
|
||
if (this.index > 1 && this.index < this.getList().length - 2) {
|
||
this.slideList.childNodes.forEach(v => {
|
||
this.css(v, 'top', (this.index - 2) * this.height + 'px')
|
||
})
|
||
}
|
||
}
|
||
}
|