34 changed files with 369 additions and 3008 deletions
@ -1,104 +0,0 @@
@@ -1,104 +0,0 @@
|
||||
<template> |
||||
<div class="marquee" ref="marquee"> |
||||
<span class="text" ref="marqueeText"> |
||||
{{ text }}<span class="space"></span> |
||||
{{ text }}<span class="space"></span> |
||||
{{ text }}<span class="space"></span> |
||||
</span> |
||||
</div> |
||||
</template> |
||||
<script> |
||||
import Dom from "../utils/dom"; |
||||
import {nextTick} from "vue"; |
||||
|
||||
export default { |
||||
name: "BaseMarquee", |
||||
props: { |
||||
text: { |
||||
type: String, |
||||
default: '' |
||||
}, |
||||
}, |
||||
data() { |
||||
return { |
||||
timer: null, |
||||
contentWidth: 0, |
||||
transformX: 0, |
||||
$marqueeContent: null, |
||||
} |
||||
}, |
||||
methods: { |
||||
pause() { |
||||
// console.log('pause') |
||||
cancelAnimationFrame(this.timer) |
||||
}, |
||||
stop() { |
||||
// console.log('stop') |
||||
cancelAnimationFrame(this.timer) |
||||
this.transformX = 0 |
||||
this.marqueeText.css('transform', `translate3d(0,0,0)`) |
||||
}, |
||||
start() { |
||||
// console.log('start') |
||||
if (this.contentWidth <= 0) { // 没有内容搞什么动画 |
||||
return; |
||||
} |
||||
let fn = () => { |
||||
if (this.transformX > (-this.contentWidth / 3)) { |
||||
this.transformX -= 1 |
||||
this.marqueeText.css('transform', `translate3d(${this.transformX}px,0,0)`) |
||||
} else { |
||||
this.transformX = 0 |
||||
} |
||||
this.timer = requestAnimationFrame(fn) |
||||
} |
||||
fn() |
||||
} |
||||
}, |
||||
mounted() { |
||||
nextTick(() => { |
||||
//直接document.querySelectorAll,找不到dom,必须用$refs的方式 |
||||
this.marqueeText = new Dom(this.$refs.marqueeText) |
||||
//必须在nextTick在调用,不然后新生成dom,获取不到width |
||||
this.contentWidth = this.marqueeText.getWidth() |
||||
|
||||
// console.log(this.name, this.isPlay, this.marqueeText) |
||||
// console.log(this.name, this.isPlay, this.contentWidth) |
||||
|
||||
new Dom(this.$refs.marquee).on('pause', this.pause) |
||||
new Dom(this.$refs.marquee).on('start', this.start) |
||||
new Dom(this.$refs.marquee).on('stop', this.stop) |
||||
if (this.isPlay) { |
||||
this.start() |
||||
} |
||||
}) |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
<style scoped lang="less"> |
||||
.marquee { |
||||
width: 100%; |
||||
display: block; |
||||
margin: 0 auto; |
||||
overflow: hidden; |
||||
white-space: nowrap; |
||||
text-overflow: clip; |
||||
position: relative; |
||||
|
||||
.text { |
||||
color: white; |
||||
display: inline-block; |
||||
position: relative; |
||||
white-space: nowrap; |
||||
|
||||
.space { |
||||
display: inline-block; |
||||
width: 50rem; |
||||
} |
||||
} |
||||
|
||||
} |
||||
|
||||
|
||||
</style> |
@ -1,699 +0,0 @@
@@ -1,699 +0,0 @@
|
||||
<template> |
||||
<div class="video-wrapper" ref="videoWrapper" :class="name"> |
||||
<Loading v-if="loading" style="position: absolute"/> |
||||
<video :src="video.video + '?v=123'" |
||||
:poster="video.video + videoPoster" |
||||
ref="video" |
||||
muted |
||||
preload |
||||
:autoplay="isPlay" loop> |
||||
<p> 您的浏览器不支持 video 标签。</p> |
||||
</video> |
||||
<img src="../../assets/img/icon/play-white.png" class="pause" v-if="!isPlaying"> |
||||
<div class="float" @click="togglePlayVideo"> |
||||
<!-- @click.stop="togglePlayVideo" --> |
||||
<div :style="{opacity:isMove ? 0:1}" class="normal"> |
||||
<div class="toolbar mb1r"> |
||||
<div class="avatar-ctn mb4r"> |
||||
<img class="avatar" :src="lVideo.author.avatar" alt="" |
||||
@click.stop="$emit('goUserInfo')"> |
||||
<transition name="fade"> |
||||
<div v-if="!isAttention" @click.stop="attention" class="options" ref="attention-option"> |
||||
<img class="no" src="../../assets/img/icon/add-light.png" alt=""> |
||||
<img class="yes" src="../../assets/img/icon/ok-red.png" alt=""> |
||||
</div> |
||||
</transition> |
||||
|
||||
</div> |
||||
<div class="love mb2r" @click.stop="loved($event)"> |
||||
<div> |
||||
<img src="../../assets/img/icon/love.svg" class="love-image" v-if="!lVideo.isLoved"> |
||||
<img src="../../assets/img/icon/loved.svg" class="love-image" v-if="lVideo.isLoved"> |
||||
</div> |
||||
<span>{{ formatNumber(lVideo.digg_count) }}</span> |
||||
</div> |
||||
<div class="message mb2r" @click.stop="$emit('showComments')"> |
||||
<!-- <div class="message mb15p" @click.stop="showComment">--> |
||||
<img src="../../assets/img/icon/message.svg" alt="" class="message-image"> |
||||
<span>{{ formatNumber(lVideo.comment_count) }}</span> |
||||
</div> |
||||
<div v-if="!isMy" class="share mb4r" @click.stop="$emit('showShare')"> |
||||
<img src="../../assets/img/icon/share-white-full.png" alt="" class="share-image"> |
||||
<span>{{ formatNumber(lVideo.share_count) }}</span> |
||||
</div> |
||||
<div v-else class="share mb4r" @click.stop="$emit('showShare')"> |
||||
<img src="../../assets/img/icon/share-white-full.png" alt="" class="share-image"> |
||||
</div> |
||||
<BaseMusic |
||||
:cover="lVideo.music.cover" |
||||
:key="name" |
||||
:name="name" |
||||
:isPlay="isPlay" |
||||
@click.stop="$emit('goMusic')" |
||||
/> |
||||
</div> |
||||
<div class="content ml1r mb1r" v-if="!isMy"> |
||||
<div class="location-wrapper" v-if=" lVideo.city || lVideo.address"> |
||||
<div class="location"> |
||||
<img src="../../assets/img/icon/location.webp" alt=""> |
||||
<span>{{ lVideo.city }}</span> |
||||
<template v-if="lVideo.address && lVideo.address"> |
||||
<div class="gang"></div> |
||||
</template> |
||||
<span>{{ lVideo.address }}</span> |
||||
</div> |
||||
</div> |
||||
<div class="name mb1r fb" @click.stop="$emit('goUserInfo')">@{{ lVideo.author.nickname }}</div> |
||||
<div class="description mb1r"> |
||||
{{ lVideo.desc }} |
||||
</div> |
||||
<div class="music" @click.stop="$nav('/music')"> |
||||
<img src="../../assets/img/icon/music.svg" alt="" class="music-image"> |
||||
<BaseMarquee :key="name" |
||||
:name="name" |
||||
:isPlay="isPlay" |
||||
:text="lVideo.music.title" |
||||
@click.stop="$emit('goMusic')"/> |
||||
</div> |
||||
</div> |
||||
<div v-else class="comment-status"> |
||||
<div class="comment"> |
||||
<div class="type-comment"> |
||||
<img src="../../assets/img/icon/head-image.jpeg" alt="" class="avatar"> |
||||
<div class="right"> |
||||
<p> |
||||
<span class="name">zzzzz</span> |
||||
<span class="time">2020-01-20</span> |
||||
</p> |
||||
<p class="text">北京</p> |
||||
</div> |
||||
</div> |
||||
<transition-group name="comment-status" tag="div" class="loveds"> |
||||
<div class="type-loved" :key="i" v-for="i in test"> |
||||
<img src="../../assets/img/icon/head-image.jpeg" alt="" class="avatar"> |
||||
<img src="../../assets/img/icon/love.svg" alt="" class="loved"> |
||||
</div> |
||||
</transition-group> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
<div class="progress" |
||||
v-if="duration > 60" |
||||
:class="progressClass" |
||||
@touchmove="move" |
||||
@touchend="end" |
||||
> |
||||
<div class="time" v-if="isMove"> |
||||
<span class="currentTime">{{ $duration(currentTime) }}</span> |
||||
<span class="duration"> / {{ $duration(duration) }}</span> |
||||
</div> |
||||
<div class="line" :style="durationStyle" ref="line"></div> |
||||
<div class="point" :style="durationStyle" ref="point"></div> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</template> |
||||
|
||||
<script> |
||||
import globalMethods from '../../utils' |
||||
import BaseMarquee from "../BaseMarquee"; |
||||
import Dom from "../../utils/dom"; |
||||
import BaseMusic from "../BaseMusic"; |
||||
import Loading from "../Loading"; |
||||
|
||||
export default { |
||||
name: "Video", |
||||
components: { |
||||
BaseMarquee, |
||||
BaseMusic, |
||||
Loading, |
||||
}, |
||||
props: { |
||||
video: { |
||||
type: Object, |
||||
default: () => { |
||||
return {} |
||||
} |
||||
}, |
||||
index: { |
||||
type: Number, |
||||
default: () => { |
||||
return -1 |
||||
} |
||||
}, |
||||
//用于第一条数据,自动播放,如果都用事件去触发播放,有延迟 |
||||
isPlay: { |
||||
type: Boolean, |
||||
default: () => { |
||||
return true |
||||
} |
||||
}, |
||||
isMy: { |
||||
type: Boolean, |
||||
default: () => { |
||||
return false |
||||
} |
||||
} |
||||
}, |
||||
computed: { |
||||
durationStyle() { |
||||
return {left: this.pageX + 'px'} |
||||
}, |
||||
progressClass() { |
||||
if (this.isMove) { |
||||
return 'stop' |
||||
} else { |
||||
return this.isPlaying ? '' : 'stop' |
||||
} |
||||
}, |
||||
}, |
||||
data() { |
||||
return { |
||||
loading: false, |
||||
name: `v-${this.index}-video`, |
||||
globalMethods: globalMethods, |
||||
duration: 0, |
||||
step: 0, |
||||
currentTime: -1, |
||||
pageX: 0, |
||||
height: 0, |
||||
width: 0, |
||||
isPlaying: this.isPlay, |
||||
isAttention: false, |
||||
line: null, |
||||
point: null, |
||||
isMove: false, |
||||
test: [1, 2], |
||||
lVideo: this.video, |
||||
videoPoster: `?vframe/jpg/offset/0/w/${document.body.clientWidth}`, |
||||
timer: null, |
||||
lastClickTime: 0 |
||||
} |
||||
}, |
||||
mounted() { |
||||
this.height = document.body.clientHeight |
||||
this.width = document.body.clientWidth |
||||
this.line = this.$refs.line |
||||
this.point = this.$refs.point |
||||
let video = this.$refs.video |
||||
video.currentTime = 0 |
||||
let fun = e => { |
||||
this.currentTime = Math.ceil(e.target.currentTime) |
||||
this.pageX = this.currentTime * this.step |
||||
} |
||||
video.addEventListener('loadedmetadata', e => { |
||||
this.duration = video.duration |
||||
if (this.duration > 60) { |
||||
// if (this.duration > 6) { |
||||
this.step = this.width / Math.floor(this.duration) |
||||
video.addEventListener('timeupdate', fun) |
||||
} |
||||
}) |
||||
|
||||
let eventTester = (e, t) => { |
||||
video.addEventListener(e, () => { |
||||
if (e === 'playing') this.loading = false |
||||
if (e === 'progress') this.loading = true |
||||
if (e === 'waiting') this.loading = true |
||||
// |
||||
}, false); |
||||
} |
||||
|
||||
|
||||
// eventTester("loadstart", '客户端开始请求数据'); //客户端开始请求数据 |
||||
// eventTester("abort", '客户端主动终止下载(不是因为错误引起)'); //客户端主动终止下载(不是因为错误引起) |
||||
// eventTester("loadstart", '客户端开始请求数据'); //客户端开始请求数据 |
||||
eventTester("progress", '客户端正在请求数据'); //客户端正在请求数据 |
||||
// // eventTester("suspend", '延迟下载'); //延迟下载 |
||||
// eventTester("abort", '客户端主动终止下载(不是因为错误引起),'); //客户端主动终止下载(不是因为错误引起), |
||||
// eventTester("error", '请求数据时遇到错误'); //请求数据时遇到错误 |
||||
// eventTester("stalled", '网速失速'); //网速失速 |
||||
// eventTester("play", 'play()和autoplay开始播放时触发'); //play()和autoplay开始播放时触发 |
||||
// eventTester("pause", 'pause()触发'); //pause()触发 |
||||
// eventTester("loadedmetadata", '成功获取资源长度'); //成功获取资源长度 |
||||
// eventTester("loadeddata"); // |
||||
eventTester("waiting", '等待数据,并非错误'); //等待数据,并非错误 |
||||
eventTester("playing", '开始回放'); //开始回放 |
||||
// eventTester("canplay", '/可以播放,但中途可能因为加载而暂停'); //可以播放,但中途可能因为加载而暂停 |
||||
// eventTester("canplaythrough", '可以播放,歌曲全部加载完毕'); //可以播放,歌曲全部加载完毕 |
||||
// eventTester("seeking", '寻找中'); //寻找中 |
||||
// eventTester("seeked", '寻找完毕'); //寻找完毕 |
||||
// // eventTester("timeupdate",'播放时间改变'); //播放时间改变 |
||||
// eventTester("ended", '播放结束'); //播放结束 |
||||
// eventTester("ratechange", '播放速率改变'); //播放速率改变 |
||||
// eventTester("durationchange", '资源长度改变'); //资源长度改变 |
||||
// eventTester("volumechange", '音量改变'); //音量改变 |
||||
|
||||
this.$refs.videoWrapper.addEventListener('play', this.play) |
||||
this.$refs.videoWrapper.addEventListener('stop', this.stop) |
||||
}, |
||||
methods: { |
||||
//切换视频状态 |
||||
togglePlayVideo(e) { |
||||
let checkTime = 200 |
||||
let nowTime = new Date().getTime(); |
||||
if (nowTime - this.lastClickTime < checkTime) { |
||||
clearTimeout(this.timer); |
||||
} else { |
||||
this.timer = setTimeout(() => { |
||||
if (this.isPlaying) { |
||||
this.pause() |
||||
} else { |
||||
this.play() |
||||
} |
||||
}, checkTime); |
||||
} |
||||
this.lastClickTime = nowTime |
||||
}, |
||||
play() { |
||||
new Dom(`.${this.name}-marquee`).trigger('start') |
||||
new Dom(`.${this.name}-music`).trigger('start') |
||||
// console.log('trigger-play') |
||||
this.isPlaying = true |
||||
if (this.currentTime !== -1) { |
||||
this.$refs.video.currentTime = this.currentTime |
||||
} |
||||
this.$refs.video.volume = 1 |
||||
this.$refs.video.play() |
||||
}, |
||||
stop() { |
||||
new Dom(`.${this.name}-marquee`).trigger('stop') |
||||
new Dom(`.${this.name}-music`).trigger('stop') |
||||
// console.log('trigger-stop') |
||||
this.$refs.video.pause() |
||||
this.isPlaying = false |
||||
this.$refs.video.currentTime = 0 |
||||
}, |
||||
pause() { |
||||
new Dom(`.${this.name}-marquee`).trigger('pause') |
||||
new Dom(`.${this.name}-music`).trigger('pause') |
||||
// console.log('trigger-pause') |
||||
this.$refs.video.pause() |
||||
this.isPlaying = false |
||||
}, |
||||
formatNumber(v) { |
||||
return globalMethods.formatNumber(v) |
||||
}, |
||||
$duration(v) { |
||||
return globalMethods.$duration(v) |
||||
}, |
||||
attention() { |
||||
let option = this.$refs['attention-option'] |
||||
option.classList.add('attention') |
||||
setTimeout(() => { |
||||
this.isAttention = true |
||||
}, 1000) |
||||
}, |
||||
|
||||
loved(e, index) { |
||||
this.lVideo.isLoved = !this.lVideo.isLoved |
||||
this.$emit('update:video', this.lVideo) |
||||
}, |
||||
start(e) { |
||||
this.pageX = e.touches[0].pageX |
||||
}, |
||||
move(e) { |
||||
if (this.isPlaying) return |
||||
this.isMove = true |
||||
this.pause() |
||||
this.pageX = e.touches[0].pageX |
||||
// console.log(this.step) |
||||
this.currentTime = Math.ceil(Math.ceil(e.touches[0].pageX) / this.step) |
||||
globalMethods.$stopPropagation(e) |
||||
}, |
||||
end(e) { |
||||
if (this.isPlaying) return |
||||
console.log('end', e) |
||||
setTimeout(() => { |
||||
this.isMove = false |
||||
}, 1000) |
||||
this.currentTime = Math.ceil(Math.ceil(e.changedTouches[0].pageX) / this.step) |
||||
this.play() |
||||
globalMethods.$stopPropagation(e) |
||||
} |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
<style scoped lang="less"> |
||||
@import "../../assets/less/color"; |
||||
|
||||
.fade-enter-active, |
||||
.fade-leave-active { |
||||
transition: transform 0.5s linear; |
||||
} |
||||
|
||||
.fade-enter-from, |
||||
.fade-leave-to { |
||||
transform: scale(0); |
||||
} |
||||
|
||||
|
||||
.video-wrapper { |
||||
position: relative; |
||||
background: black; |
||||
font-size: 14rem; |
||||
width: 100%; |
||||
height: 100%; |
||||
|
||||
video { |
||||
width: 100%; |
||||
height: 100%; |
||||
/*position: absolute;*/ |
||||
} |
||||
|
||||
.pause { |
||||
width: 100rem; |
||||
height: 100rem; |
||||
opacity: 0.5; |
||||
position: absolute; |
||||
margin: auto; |
||||
left: 0; |
||||
top: 0; |
||||
top: 0; |
||||
bottom: 0; |
||||
right: 0; |
||||
animation: pause-animation 1.1s linear; |
||||
|
||||
@scale: scale(1.2); |
||||
|
||||
@keyframes pause-animation { |
||||
0% { |
||||
opacity: 0; |
||||
transform: scale(2); |
||||
} |
||||
10% { |
||||
opacity: 0.5; |
||||
transform: @scale; |
||||
} |
||||
100% { |
||||
transform: @scale; |
||||
opacity: 0.5; |
||||
} |
||||
} |
||||
} |
||||
|
||||
.float { |
||||
position: absolute; |
||||
left: 0; |
||||
top: 0; |
||||
height: 100%; |
||||
width: 100%; |
||||
|
||||
.normal { |
||||
position: absolute; |
||||
bottom: 0; |
||||
width: 100%; |
||||
transition: all .3s; |
||||
|
||||
.toolbar { |
||||
//width: 40px; |
||||
position: absolute; |
||||
bottom: 0; |
||||
right: 5px; |
||||
color: #fff; |
||||
|
||||
.avatar-ctn { |
||||
position: relative; |
||||
|
||||
.avatar { |
||||
width: 55px; |
||||
height: 55px; |
||||
border-radius: 50%; |
||||
} |
||||
|
||||
.options { |
||||
position: absolute; |
||||
border-radius: 50%; |
||||
margin: auto; |
||||
left: 0; |
||||
right: 0; |
||||
bottom: -5px; |
||||
background: red; |
||||
//background: black; |
||||
width: 18px; |
||||
height: 18px; |
||||
display: flex; |
||||
justify-content: center; |
||||
align-items: center; |
||||
transition: all 1s; |
||||
|
||||
img { |
||||
position: absolute; |
||||
width: 12px; |
||||
height: 12px; |
||||
transition: all 1s; |
||||
} |
||||
|
||||
.yes { |
||||
opacity: 0; |
||||
transform: rotate(-180deg); |
||||
} |
||||
|
||||
&.attention { |
||||
background: white; |
||||
|
||||
.no { |
||||
opacity: 0; |
||||
transform: rotate(180deg); |
||||
} |
||||
|
||||
.yes { |
||||
opacity: 1; |
||||
transform: rotate(0deg); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
.love, .message, .share { |
||||
display: flex; |
||||
flex-direction: column; |
||||
justify-content: center; |
||||
align-items: center; |
||||
|
||||
@width: 35rem; |
||||
|
||||
img { |
||||
width: @width; |
||||
height: @width; |
||||
} |
||||
|
||||
span { |
||||
font-size: 12rem; |
||||
} |
||||
} |
||||
|
||||
.loved { |
||||
background: red; |
||||
} |
||||
|
||||
} |
||||
|
||||
.content { |
||||
color: #fff; |
||||
position: absolute; |
||||
bottom: 0; |
||||
width: 75%; |
||||
//display: flex; |
||||
//flex-direction: column; |
||||
|
||||
.location-wrapper { |
||||
display: flex; |
||||
|
||||
.location { |
||||
margin-bottom: 10rem; |
||||
|
||||
display: flex; |
||||
align-items: center; |
||||
font-size: 12rem; |
||||
padding: 4rem; |
||||
border-radius: 3rem; |
||||
background: @second-btn-color-tran; |
||||
|
||||
.gang { |
||||
height: 8rem; |
||||
width: 1.5px; |
||||
margin: 0 5rem; |
||||
background: gray; |
||||
} |
||||
|
||||
img { |
||||
margin-right: 7rem; |
||||
width: 18rem; |
||||
} |
||||
} |
||||
} |
||||
|
||||
|
||||
.music { |
||||
position: relative; |
||||
width: 60%; |
||||
display: flex; |
||||
align-items: center; |
||||
|
||||
.music-image { |
||||
width: 20px; |
||||
height: 20px; |
||||
margin-top: 3px; |
||||
} |
||||
} |
||||
} |
||||
|
||||
.comment-status { |
||||
display: flex; |
||||
align-items: center; |
||||
|
||||
.comment { |
||||
|
||||
.type-comment { |
||||
display: flex; |
||||
background: rgb(130, 21, 44); |
||||
border-radius: 50px; |
||||
padding: 3px; |
||||
margin-bottom: 20px; |
||||
|
||||
.avatar { |
||||
width: 36px; |
||||
height: 36px; |
||||
border-radius: 50%; |
||||
} |
||||
|
||||
.right { |
||||
margin: 0 10px; |
||||
color: @second-text-color; |
||||
|
||||
.name { |
||||
margin-right: 10px; |
||||
} |
||||
|
||||
.text { |
||||
color: white; |
||||
} |
||||
|
||||
} |
||||
|
||||
} |
||||
|
||||
.loveds { |
||||
} |
||||
|
||||
.type-loved { |
||||
width: 40px; |
||||
height: 40px; |
||||
position: relative; |
||||
margin-bottom: 20px; |
||||
animation: test 1s; |
||||
animation-delay: .5s; |
||||
|
||||
.avatar { |
||||
width: 36px; |
||||
height: 36px; |
||||
border-radius: 50%; |
||||
} |
||||
|
||||
.loved { |
||||
position: absolute; |
||||
bottom: 0; |
||||
left: 20px; |
||||
width: 10px; |
||||
height: 10px; |
||||
background: red; |
||||
padding: 3px; |
||||
border-radius: 50%; |
||||
border: 2px solid white; |
||||
} |
||||
} |
||||
|
||||
@keyframes test { |
||||
from { |
||||
display: block; |
||||
transform: translate3d(0, 0, 0); |
||||
} |
||||
to { |
||||
display: none; |
||||
transform: translate3d(0, -60px, 0); |
||||
} |
||||
|
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
.progress { |
||||
bottom: -1px; |
||||
position: absolute; |
||||
height: 7px; |
||||
width: 100vw; |
||||
background: black; |
||||
|
||||
|
||||
.time { |
||||
position: absolute; |
||||
z-index: 9; |
||||
font-size: 24px; |
||||
bottom: 50px; |
||||
left: 0; |
||||
right: 0; |
||||
color: white; |
||||
text-align: center; |
||||
|
||||
.duration { |
||||
color: darkgray; |
||||
} |
||||
} |
||||
|
||||
&:before { |
||||
z-index: 9; |
||||
content: ' '; |
||||
height: 1.5px; |
||||
width: 100vw; |
||||
background: gray; |
||||
position: absolute; |
||||
top: 0; |
||||
} |
||||
|
||||
.line { |
||||
z-index: 999; |
||||
content: ''; |
||||
position: absolute; |
||||
top: 0; |
||||
height: 1px; |
||||
width: 200vw; |
||||
transform: translate3d(-200vw, 0, 0); |
||||
background: gray; |
||||
} |
||||
|
||||
.point { |
||||
z-index: 10; |
||||
position: absolute; |
||||
left: 10vw; |
||||
top: -1px; |
||||
height: 4px; |
||||
width: 4px; |
||||
border-radius: 50%; |
||||
background: gray; |
||||
} |
||||
} |
||||
|
||||
& .stop { |
||||
&:before { |
||||
height: 3.5px; |
||||
} |
||||
|
||||
.line { |
||||
height: 3px; |
||||
background: white; |
||||
} |
||||
|
||||
.point { |
||||
top: -2px; |
||||
height: 8px; |
||||
width: 8px; |
||||
background: white; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
</style> |
@ -1,322 +0,0 @@
@@ -1,322 +0,0 @@
|
||||
<template> |
||||
<div id="base-slide-wrapper"> |
||||
<div id="base-slide-list" ref="slideList" |
||||
:style="{'flex-direction':'column'}" |
||||
@touchstart="touchStart($event)" |
||||
@touchmove="touchMove($event)" |
||||
@touchend="touchEnd($event)"> |
||||
<slot></slot> |
||||
</div> |
||||
</div> |
||||
</template> |
||||
<script> |
||||
import * as Vue from 'vue' |
||||
import Dom from "../../utils/dom"; |
||||
|
||||
export default { |
||||
name: "SlideColumnVirtualList", |
||||
props: { |
||||
renderSlide: { |
||||
type: Function, |
||||
default: () => { |
||||
return null |
||||
} |
||||
}, |
||||
list: { |
||||
type: Array, |
||||
default: () => { |
||||
return [] |
||||
} |
||||
}, |
||||
defaultVirtualItemTotal: { |
||||
type: Number, |
||||
default: () => 5 |
||||
}, |
||||
activeIndex: { |
||||
type: Number, |
||||
default: () => 0 |
||||
}, |
||||
}, |
||||
data() { |
||||
return { |
||||
wrapperHeight: 0, |
||||
|
||||
startLocationX: 0, |
||||
startLocationY: 0, |
||||
|
||||
moveXDistance: 0, |
||||
moveYDistance: 0, |
||||
|
||||
judgeValue: 10, |
||||
startTime: 0, |
||||
|
||||
currentSlideItemIndex: 0, |
||||
isDrawDown: true, |
||||
isCanDownWiping: false, |
||||
isNeedCheck: true, |
||||
|
||||
slideList: null, |
||||
slideItems: null, |
||||
slideItemsHeights: [], |
||||
appInsMap: new Map() |
||||
} |
||||
}, |
||||
watch: { |
||||
watchList: { |
||||
handler(newVal, oldVal) { |
||||
console.log('watch', newVal.length, oldVal.length) |
||||
let that = this |
||||
if (oldVal.length === 0) { |
||||
let startIndex = 0 |
||||
if (this.currentSlideItemIndex >= this.defaultVirtualItemTotal) { |
||||
startIndex = this.currentSlideItemIndex - (this.defaultVirtualItemTotal - 1) / 2 |
||||
} |
||||
this.list.slice(startIndex, startIndex + 5).map( |
||||
(item, index) => { |
||||
let el = null |
||||
//自动播放,当前条(可能是0,可能是其他),试了下用jq来找元素,然后trigger play事件,要慢点样 |
||||
if (startIndex + index === this.currentSlideItemIndex) { |
||||
el = this.getInsEl(item, startIndex + index, true) |
||||
} else { |
||||
el = this.getInsEl(item, startIndex + index) |
||||
} |
||||
this.slideList.appendChild(el) |
||||
}) |
||||
if (that.currentSlideItemIndex > 2) { |
||||
this.$setCss(this.slideList, 'transform', `translate3d(0px, |
||||
${-this.currentSlideItemIndex * this.wrapperHeight}px, 0px)`) |
||||
$(".video-slide-item").each(function () { |
||||
$(this).css('top', (that.currentSlideItemIndex - 2) * that.wrapperHeight) |
||||
}) |
||||
} |
||||
setTimeout(this.checkChildren, 100) |
||||
} else { |
||||
let endLength = oldVal.length + 3 |
||||
newVal.slice(oldVal.length, endLength).map((item, index) => { |
||||
let el = this.getInsEl(item, oldVal.length + index) |
||||
//这里必须要设置个top值,不然会把前面的条目给覆盖掉 |
||||
$(el).css('top', (that.currentSlideItemIndex - 2) * that.wrapperHeight) |
||||
this.slideList.appendChild(el) |
||||
}) |
||||
this.checkChildren() |
||||
} |
||||
}, |
||||
deep: true |
||||
} |
||||
}, |
||||
computed: { |
||||
//包装下,不然后watch里面监听到的新值和旧值是一样的,麻了 |
||||
watchList() { |
||||
return [...this.list] |
||||
}, |
||||
}, |
||||
mounted: async function () { |
||||
this.checkChildren() |
||||
this.currentSlideItemIndex = this.activeIndex |
||||
this.list.slice(this.currentSlideItemIndex, (this.defaultVirtualItemTotal + 1) / 2).map((item, index) => { |
||||
this.slideList.appendChild(this.getInsEl(item, index)) |
||||
}) |
||||
this.checkChildren() |
||||
}, |
||||
methods: { |
||||
dislike(item) { |
||||
let currentItem = new Dom(`.video-slide-item-${this.currentSlideItemIndex}`) |
||||
let replaceItem = this.getInsEl(item, this.currentSlideItemIndex, true) |
||||
new Dom(replaceItem).css('top', currentItem.css('top')) |
||||
$(currentItem.els[0]).replaceWith(replaceItem) |
||||
}, |
||||
getInsEl(item, index, play = false) { |
||||
// console.log('index',index,play) |
||||
let slideVNode = this.renderSlide(item, index, play) |
||||
const app = Vue.createApp({ |
||||
render() { |
||||
return slideVNode |
||||
} |
||||
}) |
||||
const parent = document.createElement('div') |
||||
const ins = app.mount(parent) |
||||
this.appInsMap.set(index, app) |
||||
// this.appInsMap.set(index, ins) |
||||
return ins.$el |
||||
}, |
||||
checkChildren() { |
||||
this.slideList = this.$refs.slideList |
||||
this.slideItems = this.slideList.children |
||||
this.wrapperHeight = this.$getCss(this.slideList, 'height') |
||||
}, |
||||
touchStart(e) { |
||||
this.$setCss(this.slideList, 'transition-duration', `0ms`) |
||||
this.showIndicator && this.$setCss(this.indicatorRef, 'transition-duration', `0ms`) |
||||
this.toolbarStyleTransitionDuration = 0 |
||||
this.startLocationX = e.touches[0].pageX |
||||
this.startLocationY = e.touches[0].pageY |
||||
this.startTime = Date.now() |
||||
|
||||
let that = this |
||||
let items = $(".video-slide-item") |
||||
if (items.length > this.defaultVirtualItemTotal) { |
||||
let middle = (this.defaultVirtualItemTotal - 1) / 2 |
||||
let removeNum = 0 |
||||
items.each(function (index) { |
||||
if ($(this).data('index') === that.currentSlideItemIndex) { |
||||
console.log('start-index', index) |
||||
if (index !== middle) { |
||||
removeNum = index - middle |
||||
} |
||||
} |
||||
}) |
||||
items.each(function (index) { |
||||
$(this).css('top', (that.currentSlideItemIndex - 2) * that.wrapperHeight) |
||||
}) |
||||
for (let i = 0; i < removeNum; i++) { |
||||
$(items[i]).remove() |
||||
} |
||||
} |
||||
}, |
||||
touchMove(e) { |
||||
this.moveXDistance = e.touches[0].pageX - this.startLocationX |
||||
this.moveYDistance = e.touches[0].pageY - this.startLocationY |
||||
|
||||
this.isDrawDown = this.moveYDistance < 0 |
||||
|
||||
this.checkDirection() |
||||
|
||||
//me页面,需要获取向下滑动的时候 |
||||
if (!this.isDrawDown) { |
||||
this.$attrs['onFirst'] && this.$emit('first', this.moveYDistance) |
||||
} |
||||
|
||||
//todo 太卡了,后面考虑用原生js来写 |
||||
// this.$attrs['onMove'] && this.$emit('move', { |
||||
// x: {distance: this.moveXDistance, isDrawRight: this.isDrawRight}, |
||||
// y: {distance: this.lastMoveYDistance, isDrawDown: this.isDrawDown}, |
||||
// }) |
||||
|
||||
if (this.isCanDownWiping) { |
||||
if (this.currentSlideItemIndex === 0 && !this.isDrawDown) return; //在第一个item,并且想往下划。 |
||||
if (this.currentSlideItemIndex === this.list.length - 1 && this.isDrawDown) return |
||||
// console.log('this.isCanDownWiping') |
||||
this.$stopPropagation(e) |
||||
this.$setCss(this.slideList, 'transform', `translate3d(0px, ${-this.getHeight(this.currentSlideItemIndex) + |
||||
this.moveYDistance + |
||||
(this.isDrawDown ? this.judgeValue : -this.judgeValue) |
||||
}px, 0px)`) |
||||
} |
||||
}, |
||||
touchEnd(e) { |
||||
if (this.isCanDownWiping) { |
||||
if (this.currentSlideItemIndex === 0 && !this.isDrawDown) return |
||||
if (this.currentSlideItemIndex === this.list.length - 1 && this.isDrawDown) return this.$attrs['onEnd'] && this.$emit('end') |
||||
|
||||
e && this.$stopPropagation(e) |
||||
|
||||
this.$setCss(this.slideList, 'transition-duration', `300ms`) |
||||
let endTime = Date.now() |
||||
let gapTime = endTime - this.startTime |
||||
|
||||
if (Math.abs(this.moveYDistance) < 20) gapTime = 1000 |
||||
if (Math.abs(this.moveYDistance) > (this.wrapperHeight / 3)) gapTime = 100 |
||||
if (gapTime < 150) { |
||||
if (this.isDrawDown) { |
||||
this.currentSlideItemIndex += 1 |
||||
} else { |
||||
this.currentSlideItemIndex -= 1 |
||||
} |
||||
// console.log('gapTime', this.currentSlideItemIndex) |
||||
|
||||
// console.log(this.slideItems.length) |
||||
let that = this |
||||
if (this.isDrawDown) { |
||||
let addItemIndex = this.currentSlideItemIndex + 2 |
||||
if (this.slideItems.length < this.defaultVirtualItemTotal) { |
||||
let res = $(`#base-slide-list .video-slide-item[data-index=${addItemIndex}]`) |
||||
if (res.length === 0) { |
||||
this.slideList.appendChild(this.getInsEl(this.list[addItemIndex], addItemIndex)) |
||||
} |
||||
} |
||||
if (this.slideItems.length === this.defaultVirtualItemTotal |
||||
&& this.currentSlideItemIndex >= (this.defaultVirtualItemTotal + 1) / 2 |
||||
&& this.currentSlideItemIndex <= this.list.length - 3 |
||||
) { |
||||
let res = $(`#base-slide-list .video-slide-item[data-index=${addItemIndex}]`) |
||||
// console.log(videos) |
||||
if (res.length === 0) { |
||||
this.slideList.appendChild(this.getInsEl(this.list[addItemIndex], addItemIndex)) |
||||
this.appInsMap.get($("#base-slide-list .video-slide-item:first").data('index')).unmount() |
||||
// $("#base-slide-list .base-slide-item:first").remove() |
||||
$(".video-slide-item").each(function () { |
||||
$(this).css('top', (that.currentSlideItemIndex - 2) * that.wrapperHeight) |
||||
}) |
||||
} |
||||
} |
||||
if (this.slideItems.length > this.defaultVirtualItemTotal) { |
||||
this.appInsMap.get($("#base-slide-list .video-slide-item:first").data('index')).unmount() |
||||
$(".video-slide-item").each(function () { |
||||
$(this).css('top', (that.currentSlideItemIndex - 2) * that.wrapperHeight) |
||||
}) |
||||
} |
||||
} else { |
||||
if (this.currentSlideItemIndex > 1 && this.currentSlideItemIndex <= this.list.length - 4) { |
||||
let addItemIndex = this.currentSlideItemIndex - 2 |
||||
this.slideList.prepend(this.getInsEl(this.list[addItemIndex], addItemIndex)) |
||||
this.appInsMap.get($("#base-slide-list .video-slide-item:last").data('index')).unmount() |
||||
// $("#base-slide-list .base-slide-item:last").remove() |
||||
$(".video-slide-item").each(function () { |
||||
$(this).css('top', (that.currentSlideItemIndex - 2) * that.wrapperHeight) |
||||
}) |
||||
} |
||||
} |
||||
this.checkChildren() |
||||
} |
||||
this.$setCss(this.slideList, 'transform', `translate3d(0px, ${-this.getHeight(this.currentSlideItemIndex)}px, 0px)`) |
||||
} |
||||
this.resetConfig() |
||||
this.$attrs['onUpdate:activeIndex'] && this.$emit('update:active-index', this.currentSlideItemIndex) |
||||
}, |
||||
resetConfig() { |
||||
this.isCanDownWiping = false |
||||
this.isNeedCheck = true |
||||
this.moveXDistance = 0 |
||||
this.moveYDistance = 0 |
||||
}, |
||||
getHeight(index) { |
||||
return this.wrapperHeight * index |
||||
}, |
||||
checkDirection() { |
||||
if (!this.isNeedCheck) return |
||||
if (Math.abs(this.moveXDistance) > this.judgeValue || Math.abs(this.moveYDistance) > this.judgeValue) { |
||||
let angle = (Math.abs(this.moveXDistance) * 10) / (Math.abs(this.moveYDistance) * 10) |
||||
if (angle > 1) { |
||||
this.isCanDownWiping = false |
||||
this.isCanRightWiping = true |
||||
// console.log('横划') |
||||
} else { |
||||
this.isCanDownWiping = true |
||||
this.isCanRightWiping = false |
||||
// console.log('竖划') |
||||
} |
||||
// console.log(angle) |
||||
return this.isNeedCheck = false |
||||
} |
||||
return this.isNeedCheck = true |
||||
} |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
<style scoped lang="less"> |
||||
@import "../../assets/less/index"; |
||||
|
||||
#base-slide-wrapper { |
||||
width: 100%; |
||||
height: 100%; |
||||
overflow: hidden; |
||||
|
||||
#base-slide-list { |
||||
display: flex; |
||||
height: 100%; |
||||
width: 100%; |
||||
position: relative; |
||||
} |
||||
} |
||||
</style> |
@ -1,184 +0,0 @@
@@ -1,184 +0,0 @@
|
||||
<script setup lang="jsx"> |
||||
import {computed, onMounted, reactive, ref, watch} from "vue"; |
||||
import GM from '../../utils' |
||||
import { |
||||
getSlideDistance, |
||||
slideInit, |
||||
slideReset, |
||||
slideTouchEnd, |
||||
slideTouchMove, |
||||
slideTouchStart |
||||
} from "./common"; |
||||
import {SlideType} from "../../utils/const_var"; |
||||
import SlideItem from "@/pages/slideHooks/SlideItem.vue"; |
||||
import TestItem from "@/pages/slideComponent/TestItem.vue"; |
||||
|
||||
const props = defineProps({ |
||||
index: { |
||||
type: Number, |
||||
default: () => { |
||||
return 0 |
||||
} |
||||
}, |
||||
position: { |
||||
type: Object, |
||||
default: () => { |
||||
return {} |
||||
} |
||||
}, |
||||
render: { |
||||
type: Function, |
||||
default: () => { |
||||
return null |
||||
} |
||||
}, |
||||
list: { |
||||
type: Array, |
||||
default: () => { |
||||
return [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15] |
||||
} |
||||
}, |
||||
virtualTotal: { |
||||
type: Number, |
||||
default: () => 5 |
||||
}, |
||||
name: { |
||||
type: String, |
||||
default: () => '' |
||||
}, |
||||
}) |
||||
const emit = defineEmits(['update:index', 'loadMore']) |
||||
|
||||
const judgeValue = 20 |
||||
const wrapperEl = ref(null) |
||||
const state = reactive({ |
||||
name: 'SlideVertical', |
||||
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}, |
||||
cs: [], |
||||
styleTop: 0, |
||||
components: { |
||||
one: <SlideItem class=" gray"> |
||||
<div class="big">热点0</div> |
||||
</SlideItem>, |
||||
two: <SlideItem class=" gray"> |
||||
<div class="big">热点1</div> |
||||
</SlideItem>, |
||||
three: <SlideItem class=" gray"> |
||||
<div class="big">热点2</div> |
||||
</SlideItem>, |
||||
four: <SlideItem class=" gray"> |
||||
<div class="big">热点3</div> |
||||
</SlideItem>, |
||||
five: <SlideItem class=" gray"> |
||||
<div class="big">热点4</div> |
||||
</SlideItem>, |
||||
} |
||||
}) |
||||
|
||||
function r(item, index = -1) { |
||||
let node = props.render(item, state.localIndex, index === 0, props.position) |
||||
return <SlideItem class="slideItemClassName"> |
||||
{node} |
||||
</SlideItem> |
||||
} |
||||
|
||||
watch( |
||||
() => props.index, |
||||
(newVal) => { |
||||
if (state.localIndex !== newVal) { |
||||
state.localIndex = newVal |
||||
GM.$setCss(wrapperEl.value, 'transition-duration', `300ms`) |
||||
GM.$setCss(wrapperEl.value, 'transform', `translate3d(0,${getSlideDistance(state, SlideType.VERTICAL)}px, 0)`) |
||||
} |
||||
} |
||||
) |
||||
|
||||
watch( |
||||
() => props.list, |
||||
(newVal) => { |
||||
console.log('list', newVal) |
||||
let half = (props.virtualTotal + 1) / 2 |
||||
if (state.localIndex >= half) { |
||||
state.cs = newVal.slice(state.localIndex - 2, state.localIndex + 3).map((v, i) => r(v)) |
||||
state.styleTop = (state.localIndex - 2) * state.wrapper.height |
||||
} |
||||
} |
||||
) |
||||
|
||||
onMounted(() => { |
||||
slideInit(wrapperEl.value, state, SlideType.VERTICAL) |
||||
state.cs = props.list.slice(0, props.virtualTotal).map((v, i) => r(v, i)) |
||||
}) |
||||
|
||||
function touchStart(e) { |
||||
slideTouchStart(e, wrapperEl.value, state) |
||||
} |
||||
|
||||
function touchMove(e) { |
||||
slideTouchMove(e, wrapperEl.value, state, judgeValue, canNext, null, SlideType.VERTICAL) |
||||
} |
||||
|
||||
function touchEnd(e) { |
||||
slideTouchEnd(e, state, canNext, (isNext) => { |
||||
console.log('state.localIndex', state.localIndex) |
||||
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) { |
||||
console.log('loadMore') |
||||
emit('loadMore') |
||||
} |
||||
if (state.localIndex >= half && state.localIndex <= props.list.length - half) { |
||||
console.log('push') |
||||
// state.cs = props.list.slice(state.localIndex - 2, state.localIndex + 3).map((v, i) => r(v)) |
||||
state.cs.shift() |
||||
state.cs.push(r(props.list[state.localIndex + (half - 1)])) |
||||
state.styleTop = (state.localIndex - 2) * state.wrapper.height |
||||
} |
||||
} else { |
||||
//这里先加个1,保持if表达式一致 |
||||
let judgeIndex = state.localIndex + 1 |
||||
if (judgeIndex >= half && judgeIndex <= props.list.length - half) { |
||||
console.log('unshift') |
||||
state.cs.unshift(r(props.list[state.localIndex - (half - 1)])) |
||||
state.cs.pop() |
||||
state.styleTop = (state.localIndex - 2) * state.wrapper.height |
||||
} |
||||
} |
||||
} |
||||
if (state.localIndex < half) { |
||||
console.log('--') |
||||
state.styleTop = 0 |
||||
} |
||||
|
||||
}, null, SlideType.VERTICAL) |
||||
slideReset(wrapperEl.value, state, SlideType.VERTICAL, emit) |
||||
} |
||||
|
||||
|
||||
function canNext(isNext) { |
||||
return !((state.localIndex === 0 && !isNext) || (state.localIndex === props.list.length - 1 && isNext)); |
||||
} |
||||
</script> |
||||
|
||||
<template> |
||||
<div class="slide v"> |
||||
<div class="slide-list flex-direction-column" |
||||
ref="wrapperEl" |
||||
@touchstart="touchStart" |
||||
@touchmove="touchMove" |
||||
@touchend="touchEnd" |
||||
> |
||||
<component :style='{top: state.styleTop + "px"}' :is="v" v-for="(v,i) in state.cs" :key="v.id"/> |
||||
</div> |
||||
</div> |
||||
</template> |
||||
|
||||
测试用<component :is的方法是否可行。结论,可行。但是不实用。当list数据更新时,<component的表现形式为,不销毁组件,只更新props。 |
||||
|
||||
感觉会导致已经加载到的video,重新加载。但未经测试 |
@ -1,166 +0,0 @@
@@ -1,166 +0,0 @@
|
||||
<template> |
||||
<VInfinite |
||||
:style="{background: 'black'}" |
||||
name="main" |
||||
v-model:index="state.itemIndex" |
||||
:render="render" |
||||
:list="state.recommendVideos" |
||||
:position="{ |
||||
baseIndex:0, |
||||
navIndex:5, |
||||
}" |
||||
@loadMore="loadMore" |
||||
@refresh="() => getData(true)" |
||||
> |
||||
</VInfinite> |
||||
</template> |
||||
|
||||
<script setup lang="jsx"> |
||||
import VInfinite from '../../components/slide/SlideVerticalInfinite.vue' |
||||
import SlideAlbum from "../../components/slide/SlideAlbum"; |
||||
import SlideUser from "../../components/slide/SlideUser"; |
||||
import BVideo from "../../components/slide/BVideo"; |
||||
import {computed, onMounted, onUnmounted, reactive, ref} from "vue"; |
||||
import bus, {EVENT_KEY} from "../../utils/bus"; |
||||
import {useNav} from "@/utils/hooks/useNav"; |
||||
import Utils from "@/utils"; |
||||
import api from "@/api"; |
||||
import {useStore} from "vuex"; |
||||
|
||||
const nav = useNav() |
||||
|
||||
function stop(e) { |
||||
e.stopPropagation() |
||||
} |
||||
|
||||
const store = useStore() |
||||
const friends = computed(() => store.state.friends) |
||||
const bodyHeight = computed(() => store.state.bodyHeight) |
||||
const bodyWidth = computed(() => store.state.bodyWidth) |
||||
|
||||
const subTypeRef = ref(null) |
||||
const state = reactive({ |
||||
baseIndex: 0, |
||||
navIndex: 4, |
||||
itemIndex: 0, |
||||
test: '', |
||||
recommendVideos: [ |
||||
// { |
||||
// type: 'img', |
||||
// src: `http://douyin.ttentau.top/0.mp4?vframe/jpg/offset/0/w/${document.body.clientWidth}` |
||||
// }, |
||||
{ |
||||
type: 'imgs', |
||||
src: `http://douyin.ttentau.top/0.mp4?vframe/jpg/offset/0/w/${document.body.clientWidth}` |
||||
}, |
||||
{ |
||||
type: 'user', |
||||
src: `http://douyin.ttentau.top/0.mp4?vframe/jpg/offset/0/w/${document.body.clientWidth}` |
||||
}, |
||||
], |
||||
|
||||
isSharing: false, |
||||
canMove: true, |
||||
loading: false, |
||||
isUp: false, |
||||
|
||||
shareType: -1, |
||||
|
||||
showPlayFeedback: false, |
||||
showShareDuoshan: false, |
||||
showShareDialog: false, |
||||
showShare2WeChatZone: false, |
||||
showDouyinCode: false, |
||||
showFollowSetting: false, |
||||
showFollowSetting2: false, |
||||
showBlockDialog: false, |
||||
showChangeNote: false, |
||||
shareToFriend: false, |
||||
|
||||
commentVisible: false, |
||||
fullScreen: false, |
||||
subType: -1, |
||||
//用于改变zindex的层级到上层,反正比slide高就行。不然摸不到subType. |
||||
subTypeIsTop: false, |
||||
totalSize: 0, |
||||
pageSize: 10, |
||||
pageNo: 0, |
||||
}) |
||||
|
||||
async function getData(refresh = false) { |
||||
// if (process.env.NODE_ENV !== 'development') { |
||||
// state.totalSize = 11 |
||||
// return state.recommendVideos = resource.videos |
||||
// } |
||||
if (state.loading) return |
||||
state.loading = true |
||||
let res = await api.videos.recommended({pageNo: refresh ? 0 : state.pageNo, pageSize: state.pageSize}) |
||||
console.log('loadMore-', 'refresh', refresh, res) |
||||
state.loading = false |
||||
if (res.code === 200) { |
||||
state.totalSize = res.data.total |
||||
if (refresh) { |
||||
state.recommendVideos = [] |
||||
} |
||||
state.recommendVideos = state.recommendVideos.concat(res.data.list) |
||||
} else { |
||||
state.pageNo-- |
||||
} |
||||
} |
||||
|
||||
function loadMore() { |
||||
if (!state.loading) { |
||||
state.pageNo++ |
||||
getData() |
||||
} |
||||
} |
||||
|
||||
function dislike() { |
||||
// this.$refs.virtualList.dislike(this.videos[10]) |
||||
// this.videos[this.videoIndex] = this.videos[10] |
||||
// this.$notice('操作成功,将减少此类视频的推荐') |
||||
} |
||||
|
||||
function end() { |
||||
// this.$notice('暂时没有更多了') |
||||
} |
||||
|
||||
function closeComments() { |
||||
bus.emit(EVENT_KEY.CLOSE_COMMENTS) |
||||
} |
||||
|
||||
function render(item, itemIndex, play, position) { |
||||
// console.log('item', item) |
||||
let node |
||||
if (item.type === 'img') { |
||||
node = <img src={item.src} style="height:100%;"/> |
||||
} |
||||
if (item.type === 'imgs') { |
||||
node = <SlideAlbum/> |
||||
} |
||||
if (item.type === 'user') { |
||||
node = <SlideUser/> |
||||
} |
||||
if (item.type === 'send-video') { |
||||
node = <video src={item.src} style="height:100%;"/> |
||||
} |
||||
if (item.type === 'recommend-video') { |
||||
node = <BVideo |
||||
isPlay={false} |
||||
item={item} |
||||
position={{...position, itemIndex}} |
||||
onGoMusic={e => nav('/home/music')} |
||||
onShowShare={e => state.isSharing = true} |
||||
onGoUserInfo={e => state.baseIndex = 1} |
||||
/> |
||||
} |
||||
return node |
||||
} |
||||
|
||||
</script> |
||||
|
||||
<style scoped lang="less"> |
||||
@import "@/assets/less/index"; |
||||
|
||||
|
||||
</style> |
@ -1,147 +0,0 @@
@@ -1,147 +0,0 @@
|
||||
<template> |
||||
<div id="TestImg"> |
||||
<img ref="img" src="../../assets/img/poster/0.jpg" alt="" |
||||
@touchstart="start" |
||||
@touchmove="move" |
||||
@touchend="end" |
||||
> |
||||
</div> |
||||
</template> |
||||
|
||||
<script> |
||||
import globalMethods from "../../utils"; |
||||
|
||||
export default { |
||||
name: "TestImg", |
||||
data() { |
||||
return { |
||||
result: { |
||||
width: 393, |
||||
height: 699 |
||||
}, |
||||
x: 0, |
||||
y: 0, |
||||
scale: 1, |
||||
point1: {x: 0, y: 0}, |
||||
point2: {x: 0, y: 0}, |
||||
lastPoint1: {x: 0, y: 0}, |
||||
lastPoint2: {x: 0, y: 0}, |
||||
lastCenter: {x: 0, y: 0}, |
||||
} |
||||
}, |
||||
created() { |
||||
}, |
||||
mounted() { |
||||
}, |
||||
methods: { |
||||
getCenter(a, b) { |
||||
const x = (a.x + b.x) / 2; |
||||
const y = (a.y + b.y) / 2; |
||||
return {x, y} |
||||
}, |
||||
getDistance(start, stop) { |
||||
return Math.hypot(stop.x - start.x, stop.y - start.y); |
||||
}, |
||||
handlePointers(e, type) { |
||||
for (let i = 0; i < this.touches.length; i++) { |
||||
if (this.touches[i].pointerId === e.pointerId) { |
||||
if (type === 'update') { |
||||
this.touches[i] = e; |
||||
} else if (type === 'delete') { |
||||
this.touches.splice(i, 1); |
||||
} |
||||
} |
||||
} |
||||
}, |
||||
start(e) { |
||||
// console.log('start') |
||||
if (e.touches && e.touches.length === 1) { |
||||
} else { |
||||
this.isTwo = true |
||||
this.$refs.img.style['transition-duration'] = '0ms'; |
||||
|
||||
let events = e.touches[0]; |
||||
let events2 = e.touches[1]; |
||||
|
||||
this.lastPoint1 = this.point1 = {x: events.pageX, y: events.pageY}; |
||||
this.lastPoint2 = this.point2 = {x: events2.pageX, y: events2.pageY}; |
||||
this.lastCenter = this.getCenter(this.lastPoint1, this.lastPoint2) |
||||
// console.log('lastPoint1', this.lastPoint1) |
||||
} |
||||
}, |
||||
move(e) { |
||||
if (e.touches && e.touches.length === 1) { |
||||
} else { |
||||
this.isTwo = true |
||||
e.preventDefault(); |
||||
|
||||
let current1 = {x: e.touches[0].pageX, y: e.touches[0].pageY} |
||||
let current2 = {x: e.touches[1].pageX, y: e.touches[1].pageY} |
||||
|
||||
// 双指缩放比例计算 |
||||
let ratio = this.getDistance(current1, current2) / this.getDistance(this.lastPoint1, this.lastPoint2); |
||||
|
||||
|
||||
this.scale = this.scale * ratio |
||||
console.log('ratio',ratio) |
||||
console.log('scale',this.scale) |
||||
|
||||
// 计算当前双指中心点坐标 |
||||
let center = this.getCenter(current1, current2) |
||||
// console.log('center', center) |
||||
|
||||
// 计算图片中心偏移量,默认transform-origin: 50% 50% |
||||
// 如果transform-origin: 30% 40%,那origin.x = (ratio - 1) * result.width * 0.3 |
||||
// origin.y = (ratio - 1) * result.height * 0.4 |
||||
// 如果通过修改宽高或使用transform缩放,但将transform-origin设置为左上角时。 |
||||
// 可以不用计算origin,因为(ratio - 1) * result.width * 0 = 0 |
||||
const origin = { |
||||
x: (ratio - 1) * this.result.width * 0.5, |
||||
y: (ratio - 1) * this.result.height * 0.5 |
||||
}; |
||||
// 计算偏移量,认真思考一下为什么要这样计算(带入特定的值计算一下) |
||||
this.x -= (ratio - 1) * (center.x - this.x) - origin.x - (center.x - this.lastCenter.x); |
||||
this.y -= (ratio - 1) * (center.y - this.y) - origin.y - (center.y - this.lastCenter.y); |
||||
|
||||
// console.log('this.x', this.x) |
||||
// console.log('this.y', this.y) |
||||
|
||||
// 图像应用缩放效果 |
||||
this.$refs.img.style.transform = `translate3d(${this.x}px,${this.y}px,0) scale(${this.scale})`; |
||||
|
||||
this.lastCenter = {x: center.x, y: center.y}; |
||||
this.lastPoint1 = {x: current1.x, y: current1.y}; |
||||
this.lastPoint2 = {x: current2.x, y: current2.y}; |
||||
} |
||||
}, |
||||
end(e) { |
||||
// console.log('end', e) |
||||
// 图像应用缩放效果 |
||||
this.scale = 1 |
||||
this.x = this.y = 0 |
||||
this.$refs.img.style['transition-duration'] = '300ms'; |
||||
|
||||
this.$refs.img.style.transform = `translate3d(0px,0px,0) scale(1)`; |
||||
// this.point1 = {x: e.touches[0].pageX, y: e.touches[0].pageY} |
||||
} |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
<style scoped lang="less"> |
||||
|
||||
</style> |
||||
|
||||
<style scoped lang="less"> |
||||
#TestImg { |
||||
height: 100%; |
||||
width: 100%; |
||||
display: flex; |
||||
justify-content: center; |
||||
align-items: center; |
||||
} |
||||
|
||||
img { |
||||
width: 100%; |
||||
} |
||||
</style> |
@ -1,404 +0,0 @@
@@ -1,404 +0,0 @@
|
||||
<template> |
||||
<div id="TestSlide"> |
||||
<SlideRowList |
||||
v-if="true" |
||||
name="baseSlide" |
||||
v-model:active-index="baseActiveIndex"> |
||||
<SlideItem> |
||||
<SlideRowList |
||||
style="height: calc(100vh - 5rem);" |
||||
v-model:active-index="activeIndex"> |
||||
<SlideItem> |
||||
<div id="TestSlide1" style="height: calc(100vh - 5rem);"></div> |
||||
</SlideItem> |
||||
<SlideItem> |
||||
<div id="TestSlide2" style="height: calc(100vh - 5rem);"></div> |
||||
</SlideItem> |
||||
</SlideRowList> |
||||
<Footer v-bind:init-tab="1"/> |
||||
</SlideItem> |
||||
<SlideItem> |
||||
<div id="TestSlide3" style="overflow: auto;height: 100vh;"> |
||||
<p style="height: 50px;" v-for="item in 20">1</p> |
||||
</div> |
||||
</SlideItem> |
||||
</SlideRowList> |
||||
<!-- <div id="TestSlide1" style="height: calc(100% - 5rem);"></div>--> |
||||
</div> |
||||
</template> |
||||
<script lang="jsx"> |
||||
import Slide from "./slide.jsx"; |
||||
import SlideVideo from "../../components/slide/SlideVideo"; |
||||
import SlideUser from "../../components/slide/SlideUser"; |
||||
import SlideImgs from "../../components/slide/SlideAlbum"; |
||||
import resource from "../../assets/data/resource.js"; |
||||
import CONST_VAR from "../../utils/const_var"; |
||||
import Dom from "../../utils/dom"; |
||||
|
||||
export default { |
||||
name: "TestSlide", |
||||
components: {}, |
||||
props: { |
||||
modelValue: false |
||||
}, |
||||
data() { |
||||
return { |
||||
totalSize: 52, |
||||
pageSize: 30, |
||||
pageNo: 0, |
||||
baseActiveIndex: 0, |
||||
activeIndex: 0, |
||||
lastClickTime: 0, |
||||
isDbClick: false, |
||||
dbClickTimer: null, |
||||
} |
||||
}, |
||||
computed: {}, |
||||
watch: {}, |
||||
created() { |
||||
}, |
||||
mounted() { |
||||
let list = resource.videos |
||||
list = list.concat(resource.videos) |
||||
list = list.slice(0, 5) |
||||
list.map(v => v.type = 'recommend-video') |
||||
if (true) { |
||||
// if (false) { |
||||
list.map(v => { |
||||
v.type = 'recommend-video' |
||||
}) |
||||
// list.unshift({ |
||||
// type: 'img', |
||||
// src: `http://douyin.ttentau.top/0.mp4?vframe/jpg/offset/0/w/${document.body.clientWidth}` |
||||
// }) |
||||
// list.unshift({ |
||||
// type: 'user-imgs', |
||||
// src: `http://douyin.ttentau.top/0.mp4?vframe/jpg/offset/0/w/${document.body.clientWidth}` |
||||
// }) |
||||
// list.unshift({ |
||||
// type: 'send-video', |
||||
// src: `http://douyin.ttentau.top/0.mp4` |
||||
// }) |
||||
// list.unshift({ |
||||
// type: 'user', |
||||
// "id": "224e9a00-ffa0-4bc1-bb07-c318c7b02fa5", |
||||
// "avatar": new URL('../../assets/img/icon/avatar/1.png', import.meta.url).href, |
||||
// "name": "何以为家", |
||||
// "realName": "王小二", |
||||
// "sex": "", |
||||
// "age": 25, |
||||
// "idCard": null, |
||||
// "phone": "", |
||||
// "address": null, |
||||
// "wechat": "", |
||||
// "password": null, |
||||
// "lastLoginTime": "1629993515", |
||||
// "createTime": "1630035089", |
||||
// "isDelete": 0, |
||||
// "account": "234", |
||||
// "pinyin": "H", |
||||
// "select": false, |
||||
// videos: [ |
||||
// { |
||||
// "id": "c99d325c-741e-4722-9f39-82bb423a5989", |
||||
// "cover": "https://p26.douyinpic.com/img/tos-cn-i-0004/9cf624ff4e054c8f997295473dce49cc~c5_300x400.jpeg?from=4257465056_large", |
||||
// "dynamic_cover": "https://p3.douyinpic.com/obj/tos-cn-i-0004/9cf624ff4e054c8f997295473dce49cc?from=4257465056_large", |
||||
// "origin_cover": "https://p11.douyinpic.com/tos-cn-p-0015/d9e9b6dc783f4c13a8e5cc08df788a02_1627117997~tplv-dy-360p.jpeg?from=4257465056&s=&se=false&sh=&sc=&l=202108301444360102111780824E10113C&biz_tag=feed_cover", |
||||
// "video": "https://api.amemv.com/aweme/v1/play/?video_id=v0300fg10000c3ttjarc77ucs14tfra0&line=1&file_id=8b05a5dfb9644865a605ba13cc25f0fe&sign=07cddb6e3292bb2f4478d1f9e6187b33&is_play_url=1&source=PackSourceEnum_PUBLISH", |
||||
// "video_data_size": 10754240, |
||||
// "duration": 151720, |
||||
// "desc": "实验意外爆炸,在场的人全晕了过去,再次醒来却发现身处诡异世界#我的观影报告 #抖音电影", |
||||
// "allow_download": 0, |
||||
// "allow_duet": 0, |
||||
// "allow_react": 0, |
||||
// "allow_music": 1, |
||||
// "allow_douplus": 1, |
||||
// "allow_share": 1, |
||||
// "digg_count": 156, |
||||
// "comment_count": 7, |
||||
// "download_count": 0, |
||||
// "play_count": 0, |
||||
// "share_count": 0, |
||||
// "forward_count": 0, |
||||
// "collect_count": 0, |
||||
// "sort": 9, |
||||
// "is_top": 0, |
||||
// "city": "350100", |
||||
// "musicId": "0da2663b-6bff-425f-a93e-9106b4889c99", |
||||
// "create_time": "1630391758", |
||||
// "creator_id": "54884802577", |
||||
// "status": 1, |
||||
// "topics": [ |
||||
// { |
||||
// "id": "41821c6d-e14f-47f1-a391-f0b1f42afbe1", |
||||
// "name": "抖音电影", |
||||
// "creator_id": "54884802577", |
||||
// "create_time": "1630391758", |
||||
// "status": 1 |
||||
// }, |
||||
// { |
||||
// "id": "c10178eb-441f-4dc7-93b6-eaae1b6248cc", |
||||
// "name": "我的观影报告", |
||||
// "creator_id": "54884802577", |
||||
// "create_time": "1630391758", |
||||
// "status": 1 |
||||
// } |
||||
// ], |
||||
// "music": { |
||||
// "id": "8ce2cb26-4772-4c7b-91d9-a2580c667c21", |
||||
// "cover": "https://p3.douyinpic.com/aweme/100x100/85f000239e43c3c985b5.jpeg?from=116350172", |
||||
// "mp3": "https://sf6-cdn-tos.douyinstatic.com/obj/ies-music/6995889105167076132.mp3", |
||||
// "title": "@喵嗷污说电影创作的原声", |
||||
// "creator_id": "54884802577", |
||||
// "create_time": "1630391758", |
||||
// "status": 1 |
||||
// } |
||||
// }, |
||||
// { |
||||
// "id": "cf26d20e-577f-4e08-ae0e-77eac2f1adbb", |
||||
// "cover": "https://p3.douyinpic.com/img/tos-cn-i-0004/a62aa299fd1f48e28a783deec2cdccbe~c5_300x400.jpeg?from=4257465056_large", |
||||
// "dynamic_cover": "https://p3.douyinpic.com/obj/tos-cn-i-0004/a62aa299fd1f48e28a783deec2cdccbe?from=4257465056_large", |
||||
// "origin_cover": "https://p3.douyinpic.com/tos-cn-p-0015/15826b4e6ad54f0bbdb3bd8af024604c_1627117999~tplv-dy-360p.jpeg?from=4257465056&s=&se=false&sh=&sc=&l=202108301444360102111780824E10113C&biz_tag=feed_cover", |
||||
// "video": "https://api.amemv.com/aweme/v1/play/?video_id=v0300fg10000c3ttjarc77u4a4ivddk0&line=1&file_id=f8c9a32ba7bc4976b66db0437dd65d1b&sign=c96c9adfacde30c007555eee23f0f214&is_play_url=1&source=PackSourceEnum_PUBLISH", |
||||
// "video_data_size": 16850048, |
||||
// "duration": 216320, |
||||
// "desc": "实验意外爆炸,在场的人全晕了过去,再次醒来却发现身处诡异世界#我的观影报告 #抖音电影 ", |
||||
// "allow_download": 0, |
||||
// "allow_duet": 0, |
||||
// "allow_react": 0, |
||||
// "allow_music": 1, |
||||
// "allow_douplus": 1, |
||||
// "allow_share": 1, |
||||
// "digg_count": 72, |
||||
// "comment_count": 4, |
||||
// "download_count": 0, |
||||
// "play_count": 0, |
||||
// "share_count": 1, |
||||
// "forward_count": 0, |
||||
// "collect_count": 0, |
||||
// "sort": 10, |
||||
// "is_top": 0, |
||||
// "city": "350100", |
||||
// "musicId": "28944e2e-3bcb-4173-92d2-eb9a263aa826", |
||||
// "create_time": "1630391758", |
||||
// "creator_id": "54884802577", |
||||
// "status": 1, |
||||
// "topics": [ |
||||
// { |
||||
// "id": "41821c6d-e14f-47f1-a391-f0b1f42afbe1", |
||||
// "name": "抖音电影", |
||||
// "creator_id": "54884802577", |
||||
// "create_time": "1630391758", |
||||
// "status": 1 |
||||
// }, |
||||
// { |
||||
// "id": "c10178eb-441f-4dc7-93b6-eaae1b6248cc", |
||||
// "name": "我的观影报告", |
||||
// "creator_id": "54884802577", |
||||
// "create_time": "1630391758", |
||||
// "status": 1 |
||||
// } |
||||
// ], |
||||
// "music": { |
||||
// "id": "8ce2cb26-4772-4c7b-91d9-a2580c667c21", |
||||
// "cover": "https://p3.douyinpic.com/aweme/100x100/85f000239e43c3c985b5.jpeg?from=116350172", |
||||
// "mp3": "https://sf6-cdn-tos.douyinstatic.com/obj/ies-music/6995889105167076132.mp3", |
||||
// "title": "@喵嗷污说电影创作的原声", |
||||
// "creator_id": "54884802577", |
||||
// "create_time": "1630391758", |
||||
// "status": 1 |
||||
// } |
||||
// }, |
||||
// { |
||||
// "id": "f3fd9b59-6ff5-4301-ac18-a4b8cbf35982", |
||||
// "cover": "https://p9.douyinpic.com/img/tos-cn-i-0004/890ebdf8a5c84abcae18c6e00c4165a0~c5_300x400.jpeg?from=4257465056_large", |
||||
// "dynamic_cover": "https://p3.douyinpic.com/obj/tos-cn-i-0004/890ebdf8a5c84abcae18c6e00c4165a0?from=4257465056_large", |
||||
// "origin_cover": "https://p26.douyinpic.com/tos-cn-p-0015/0d7948340d3543bda3357a9496e0cb86_1627117998~tplv-dy-360p.jpeg?from=4257465056&s=&se=false&sh=&sc=&l=202108301444360102111780824E10113C&biz_tag=feed_cover", |
||||
// "video": "https://api.amemv.com/aweme/v1/play/?video_id=v0200fg10000c3ttjarc77ucsm96q040&line=1&file_id=ccc7eccfa0d746febdcf7436841281a8&sign=a4fc8eb995aad2acdcc622371ecc9c7c&is_play_url=1&source=PackSourceEnum_PUBLISH", |
||||
// "video_data_size": 23547094, |
||||
// "duration": 230880, |
||||
// "desc": "实验意外爆炸,在场的人全晕了过去,再次醒来却发现身处诡异世界#我的观影报告 #抖音电影 ", |
||||
// "allow_download": 0, |
||||
// "allow_duet": 0, |
||||
// "allow_react": 0, |
||||
// "allow_music": 1, |
||||
// "allow_douplus": 1, |
||||
// "allow_share": 1, |
||||
// "digg_count": 93, |
||||
// "comment_count": 3, |
||||
// "download_count": 0, |
||||
// "play_count": 0, |
||||
// "share_count": 0, |
||||
// "forward_count": 0, |
||||
// "collect_count": 1, |
||||
// "sort": 11, |
||||
// "is_top": 0, |
||||
// "city": "350100", |
||||
// "musicId": "6fe6bbfc-d393-45f2-a2c8-432422c5904a", |
||||
// "create_time": "1630391758", |
||||
// "creator_id": "54884802577", |
||||
// "status": 1, |
||||
// "topics": [ |
||||
// { |
||||
// "id": "41821c6d-e14f-47f1-a391-f0b1f42afbe1", |
||||
// "name": "抖音电影", |
||||
// "creator_id": "54884802577", |
||||
// "create_time": "1630391758", |
||||
// "status": 1 |
||||
// }, |
||||
// { |
||||
// "id": "c10178eb-441f-4dc7-93b6-eaae1b6248cc", |
||||
// "name": "我的观影报告", |
||||
// "creator_id": "54884802577", |
||||
// "create_time": "1630391758", |
||||
// "status": 1 |
||||
// } |
||||
// ], |
||||
// "music": { |
||||
// "id": "8ce2cb26-4772-4c7b-91d9-a2580c667c21", |
||||
// "cover": "https://p3.douyinpic.com/aweme/100x100/85f000239e43c3c985b5.jpeg?from=116350172", |
||||
// "mp3": "https://sf6-cdn-tos.douyinstatic.com/obj/ies-music/6995889105167076132.mp3", |
||||
// "title": "@喵嗷污说电影创作的原声", |
||||
// "creator_id": "54884802577", |
||||
// "create_time": "1630391758", |
||||
// "status": 1 |
||||
// } |
||||
// }, |
||||
// ] |
||||
// }) |
||||
} |
||||
|
||||
// console.log('length', list.length) |
||||
let config = { |
||||
render: (item, itemIndex, play) => { |
||||
let html |
||||
if (item.type === 'recommend-video') { |
||||
html = <SlideVideo |
||||
isPlay={play} |
||||
video={item} |
||||
index={itemIndex} |
||||
onShowComments={e => this.isCommenting = true} |
||||
onShowShare={e => this.isSharing = true} |
||||
onGoUserInfo={e => this.baseActiveIndex = 1} |
||||
onGoMusic={e => this.$nav('/home/music')} |
||||
v-model={[this.videos[itemIndex], 'video']} |
||||
/> |
||||
} |
||||
if (item.type === 'img') { |
||||
html = <img src={item.src} style="height:100%;"/> |
||||
} |
||||
if (item.type === 'imgs') { |
||||
html = <SlideImgs /> |
||||
} |
||||
if (item.type === 'send-video') { |
||||
html = <video src={item.src} style="height:100%;"/> |
||||
} |
||||
if (item.type === 'user') { |
||||
html = <SlideUser onClose={this.t} modelValue={item}/> |
||||
} |
||||
return html |
||||
}, |
||||
list, |
||||
index: 0, |
||||
request: async q => { |
||||
let res = await this.$api.videos.recommended(q) |
||||
if (res.code === 200) { |
||||
res.data.list.map(v => v.type = 'recommend-video') |
||||
return {code: 200, data: res.data} |
||||
} |
||||
return {code: 500} |
||||
} |
||||
}; |
||||
let slide = new Slide('#TestSlide1', config) |
||||
let slide2 = new Slide('#TestSlide2', config) |
||||
|
||||
// let layout = document.querySelector('#TestSlide') |
||||
// let checkTime = 300 |
||||
// let setIsDbClickFalse = () => { |
||||
// this.dbClickTimer = setTimeout(() => { |
||||
// this.isDbClick = false |
||||
// }, checkTime); |
||||
// } |
||||
// layout.addEventListener('click', e => { |
||||
// if (this.isDbClick) { |
||||
// this.dbClick(e) |
||||
// clearTimeout(this.dbClickTimer); |
||||
// setIsDbClickFalse() |
||||
// return |
||||
// } |
||||
// let nowTime = new Date().getTime(); |
||||
// if (nowTime - this.lastClickTime < checkTime) { |
||||
// this.dbClick(e) |
||||
// this.isDbClick = true |
||||
// setIsDbClickFalse() |
||||
// } |
||||
// this.lastClickTime = nowTime; |
||||
// }, true) |
||||
}, |
||||
methods: { |
||||
t() { |
||||
console.log('t', this.totalSize) |
||||
}, |
||||
dbClick(e) { |
||||
let id = 'a' + Date.now() |
||||
let elWidth = 80 |
||||
let rotate = this.$randomNum(0, 1) |
||||
let template = `<img class="${rotate ? 'left love-dbclick' : 'right love-dbclick'}" id="${id}" src="src/assets/img/icon/loved.svg">` |
||||
let el = new Dom().create(template) |
||||
el.css({top: e.y - elWidth - 50, left: e.x - elWidth / 2,}) |
||||
new Dom('#TestSlide').append(el) |
||||
setTimeout(() => { |
||||
new Dom(`#${id}`).remove() |
||||
}, 1000) |
||||
}, |
||||
async getData() { |
||||
let res = await this.$api.videos.recommended({pageNo: this.pageNo, pageSize: this.pageSize}) |
||||
console.log(res) |
||||
}, |
||||
} |
||||
} |
||||
|
||||
if (import.meta.hot) { |
||||
} |
||||
</script> |
||||
<style scoped> |
||||
.slide-wrapper { |
||||
width: 100%; |
||||
height: 100%; |
||||
overflow: hidden; |
||||
} |
||||
|
||||
.slide-list { |
||||
display: flex; |
||||
flex-direction: column; |
||||
height: 100%; |
||||
width: 100%; |
||||
position: relative; |
||||
} |
||||
|
||||
.slide-item { |
||||
min-width: 100%; |
||||
min-height: 100%; |
||||
position: relative; |
||||
} |
||||
|
||||
</style> |
||||
|
||||
<style lang="less"> |
||||
@import "@/assets/less/index"; |
||||
|
||||
#TestSlide { |
||||
position: fixed; |
||||
left: 0; |
||||
right: 0; |
||||
bottom: 0; |
||||
top: 0; |
||||
overflow: auto; |
||||
color: white; |
||||
font-size: 1.4rem; |
||||
|
||||
.content { |
||||
padding-top: 6rem; |
||||
} |
||||
} |
||||
</style> |
@ -1,111 +0,0 @@
@@ -1,111 +0,0 @@
|
||||
<template> |
||||
<div class="slide"> |
||||
<div class="slide-list" |
||||
ref="wrapperEl" |
||||
@touchstart="touchStart" |
||||
@touchmove="touchMove" |
||||
@touchend="touchEnd" |
||||
> |
||||
<slot></slot> |
||||
</div> |
||||
</div> |
||||
|
||||
</template> |
||||
<script lang="jsx" setup> |
||||
import {onMounted, reactive, ref, watch} from "vue"; |
||||
import GM from "@/utils"; |
||||
import { |
||||
getSlideDistance, |
||||
slideInit, |
||||
slideReset, |
||||
slideTouchEnd, |
||||
slideTouchMove, |
||||
slideTouchStart |
||||
} from "@/components/slide/common"; |
||||
import {SlideType} from "@/utils/const_var"; |
||||
|
||||
const emit = defineEmits(['update:index']) |
||||
|
||||
const props = defineProps({ |
||||
index: { |
||||
type: Number, |
||||
default: () => { |
||||
return 0 |
||||
} |
||||
}, |
||||
name: { |
||||
type: String, |
||||
default: () => '' |
||||
}, |
||||
}) |
||||
const judgeValue = 20 |
||||
const wrapperEl = ref(null) |
||||
const state = reactive({ |
||||
name: props.name, |
||||
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}, |
||||
|
||||
|
||||
}) |
||||
|
||||
watch( |
||||
() => props.index, |
||||
(newVal) => { |
||||
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(() => { |
||||
slideInit(wrapperEl.value, state, SlideType.HORIZONTAL) |
||||
}) |
||||
|
||||
function touchStart(e) { |
||||
slideTouchStart(e, wrapperEl.value, state) |
||||
} |
||||
|
||||
function touchMove(e) { |
||||
slideTouchMove(e, wrapperEl.value, state, judgeValue, canNext, null, SlideType.HORIZONTAL) |
||||
} |
||||
|
||||
function touchEnd(e) { |
||||
slideTouchEnd(e, state, canNext, () => { |
||||
|
||||
}) |
||||
slideReset(wrapperEl.value, state, SlideType.HORIZONTAL, emit) |
||||
} |
||||
|
||||
|
||||
function canNext(isNext) { |
||||
return !((state.localIndex === 0 && !isNext) || (state.localIndex === state.wrapper.childrenLength - 1 && isNext)); |
||||
} |
||||
</script> |
||||
<style lang="less" scoped> |
||||
.page { |
||||
font-size: 14rem; |
||||
color: white; |
||||
} |
||||
|
||||
.swiper { |
||||
height: 50vh; |
||||
//width: 100%; |
||||
color: white; |
||||
font-size: 14rem; |
||||
|
||||
.swiper-slide { |
||||
width: 100vw; |
||||
} |
||||
|
||||
.aa { |
||||
height: 50vh; |
||||
width: 100vw; |
||||
} |
||||
} |
||||
</style> |
@ -1,278 +0,0 @@
@@ -1,278 +0,0 @@
|
||||
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')) |
||||
// console.log('height',this.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, this.index === start + i) |
||||
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) { |
||||
if (this.index > 1) { |
||||
this.css(this.slideList, 'transform', `translate3d(0px, ${this.getHeight()}px, 0px)`) |
||||
|
||||
this.slideList.childNodes.forEach(v => { |
||||
//(this.total - 1 - this.index) > 1 ? 0 : this.total - 1 - this.index) , 如果当前是最后两条,那么要多减去N个height |
||||
console.log('((this.total - 1 - this.index) > 1 ? 0 : this.total - 1 - this.index)', |
||||
(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 <div class={`slide-item slide-item-${index}`}>{slideVNode}</div> |
||||
} |
||||
}) |
||||
const ins = app.mount(document.createElement('div')) |
||||
this.appInsMap.set(index, app) |
||||
|
||||
return ins.$el |
||||
} |
||||
|
||||
touchstart(e) { |
||||
// console.log('start') |
||||
this.startLocationX = e.touches[0].pageX |
||||
this.startLocationY = e.touches[0].pageY |
||||
this.moveXDistance = 0 |
||||
this.moveYDistance = 0 |
||||
|
||||
this.startTime = Date.now() |
||||
// console.log('touchstart', this.startTime) |
||||
this.css(this.slideList, 'transition-duration', '0ms') |
||||
} |
||||
|
||||
touchmove(e) { |
||||
// console.log('move') |
||||
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() { |
||||
// console.log('end') |
||||
//如果向下划,并且加载中,并且还是已有数据的最后一条 |
||||
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) { |
||||
let stopPreviousNodeFn = (index) => { |
||||
setTimeout(() => { |
||||
let previousNode = this.slideList.querySelector(`.slide-item-${index} .video-wrapper`) |
||||
previousNode && previousNode.dispatchEvent(new Event('stop')) |
||||
}, 300) |
||||
} |
||||
//index要传过去,不然是修改后的值 |
||||
stopPreviousNodeFn(this.index) |
||||
|
||||
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) |
||||
} |
||||
} |
||||
} |
||||
|
||||
let nextNode = this.slideList.querySelector(`.slide-item-${this.index} .video-wrapper`) |
||||
nextNode && nextNode.dispatchEvent(new Event('play')) |
||||
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') |
||||
}) |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,11 @@
@@ -0,0 +1,11 @@
|
||||
import {defineStore} from "pinia"; |
||||
|
||||
export const useBaseStore = defineStore('base', { |
||||
state: () => { |
||||
return { |
||||
routeData: null |
||||
} |
||||
}, |
||||
getters: {}, |
||||
actions: {}, |
||||
}) |
Loading…
Reference in new issue