Browse Source

优化slide组件

pull/19/head
zyronon 3 years ago
parent
commit
be78b58377
  1. 84
      src/assets/data/resource.js
  2. 11
      src/components/BVideo.vue
  3. 8
      src/components/Comment.vue
  4. 18
      src/components/dialog/FromBottomDialog.vue
  5. 3
      src/pages/slide/IndicatorHome.vue
  6. 1541
      src/pages/slide/Slide.vue
  7. 12
      src/pages/slide/SlideHorizontal.vue
  8. 17
      src/pages/slide/SlideVertical.vue
  9. 247
      src/pages/slide/SlideVerticalInfinite.vue
  10. 71
      src/utils/mixin.js

84
src/assets/data/resource.js

@ -1256,6 +1256,90 @@ export default { @@ -1256,6 +1256,90 @@ export default {
"is_private": 1
}
},
{
"id": "0be47c89-f6e8-4d5d-820a-072e75c52fbf",
video: 'http://douyin.ttentau.top/16.mp4',
"video_data_size": 17839401,
"duration": 262127,
"desc": "水刀的原理,为什么被称为最锋利的刀?",
"allow_download": 1,
"allow_duet": 0,
"allow_react": 0,
"allow_music": 1,
"allow_douplus": 1,
"allow_share": 1,
"digg_count": 156000,
"comment_count": 14000,
"download_count": 704,
"play_count": 0,
"share_count": 8982,
"forward_count": 36,
"collect_count": 856,
"sort": 252,
"is_top": 0,
city: '',
address: '',
"musicId": "11e5f2b6-3ecc-4f6c-ae95-609313b9e22a",
"create_time": "1630423745",
"creator_id": "54884802577",
"status": 1,
"topics": [
{
"id": "6e2d8f8b-a7f3-43ca-861c-882abd9eeaf6",
"name": "科普",
"creator_id": "93864497380",
"create_time": "1630423585",
"status": 1
},
{
"id": "6e2d8f8b-a7f3-43ca-861c-882abd9eeaf6",
"name": "水刀",
"creator_id": "93864497380",
"create_time": "1630423585",
"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
},
"author": {
school: {
name: '',
department: '',
joinTime: '',
education: '',
displayType: '',
},
"id": "7",
"unique_id_modify_time": "1630393144",
"unique_id": "",
"favoriting_count": 143,
"avatar": new URL('../img/icon/avatar/25.png', import.meta.url).href,
"city": "",
"province": '',
"country": "",
"birthday": "",
"cover": "https://p3.douyinpic.com/obj/c8510002be9a3a61aad2?from=116350172",
"following_count": 78,
"follower_count": 123900,
"aweme_count": 2090000,
"nickname": "有你科技",
certification: '成都文化旅游发展股份有限公司西岭雪山运营分公司',
"phone": "",
"sex": "",
"last_login_time": "1630423555",
"create_time": "1630423555",
"status": 1,
"desc": `#窗含西岭千秋雪#\n千秋西岭,成都之颠,海拔5364米\n日出云海,阴阳界奇观`,
"is_private": 1
}
},
],
my: [
{

11
src/components/BVideo.vue

@ -10,7 +10,7 @@ @@ -10,7 +10,7 @@
<p> 您的浏览器不支持 video 标签</p>
</video>
<img src="../assets/img/icon/play-white.png" class="pause" v-if="!isPlaying">
<div class="float" @click="togglePlayVideo">
<div class="float" :style="{opacity: isUp?0:1}" @click="togglePlayVideo">
<div :style="{opacity:isMove ? 0:1}" class="normal">
<div class="toolbar mb1r">
<div class="avatar-ctn mb4r">
@ -159,6 +159,12 @@ export default { @@ -159,6 +159,12 @@ export default {
default: () => {
return false
}
},
isUp: {
type: Boolean,
default: () => {
return false
}
}
},
computed: {
@ -192,6 +198,7 @@ export default { @@ -192,6 +198,7 @@ export default {
test: [1, 2],
lVideo: this.video,
videoScreenHeight: 0,
videoPoster: `?vframe/jpg/offset/0/w/${document.body.clientWidth}`
}
},
@ -203,10 +210,12 @@ export default { @@ -203,10 +210,12 @@ export default {
let video = this.$refs.video
video.currentTime = 0
let fun = e => {
this.loading = false
this.currentTime = Math.ceil(e.target.currentTime)
this.pageX = this.currentTime * this.step
}
video.addEventListener('loadedmetadata', e => {
this.videoScreenHeight = video.videoHeight / (video.videoWidth / this.width)
this.duration = video.duration
if (this.duration > 60) {
// if (this.duration > 6) {

8
src/components/Comment.vue

@ -5,7 +5,7 @@ @@ -5,7 +5,7 @@
@cancel="cancel"
:show-heng-gang="false"
maskMode="light"
height="70vh"
:height="height"
mode="white">
<template v-slot:header>
<div class="title">
@ -135,6 +135,10 @@ export default { @@ -135,6 +135,10 @@ export default {
type: String,
default: null
},
height: {
type: String,
default: '70vh'
},
},
computed: {
...mapState(['friends'])
@ -348,7 +352,7 @@ export default { @@ -348,7 +352,7 @@ export default {
.comment {
width: 100%;
height: 70vh;
height: v-bind(height);
background: #fff;
z-index: 5;
border-radius: 1rem 1rem 0 0;

18
src/components/dialog/FromBottomDialog.vue

@ -87,7 +87,7 @@ export default { @@ -87,7 +87,7 @@ export default {
let mask = new Dom('.Mask').replaceClass('fade-in', 'fade-out')
setTimeout(() => {
mask.remove()
}, 300)
}, 250)
}
},
},
@ -105,7 +105,7 @@ export default { @@ -105,7 +105,7 @@ export default {
},
methods: {
beforeEnter(el) {
this.$setCss(el, 'transition-duration', `200ms`)
this.$setCss(el, 'transition-duration', `250ms`)
this.$setCss(el, 'transform', `translate3d(0,${this.height},0)`)
},
enter(el, done) {
@ -116,19 +116,19 @@ export default { @@ -116,19 +116,19 @@ export default {
// this.$setCss(el, 'transition-duration', `0ms`)
this.$setCss(el, 'transform', `none`)
done()
}, 200)
}, 250)
},
afterEnter() {
},
beforeLeave(el) {
this.$setCss(el, 'transition-duration', `200ms`)
this.$setCss(el, 'transition-duration', `250ms`)
this.$setCss(el, 'transform', `translate3d(0,0,0)`)
},
leave(el, done) {
//ref
let maxHeight = new Dom('.FromBottomDialog').css('max-height')
this.$setCss(el, 'transform', `translate3d(0,${maxHeight},0)`)
setTimeout(done, 200)
setTimeout(done, 250)
},
afterLeave() {
},
@ -151,25 +151,23 @@ export default { @@ -151,25 +151,23 @@ export default {
}
},
end(e) {
//
if (Date.now() - this.startTime < 150 && Math.abs(this.moveYDistance) < 30) {
return
}
//
if (this.$refs.dialog.scrollTop !== 0) return
let clientHeight = this.$refs.dialog.clientHeight
this.$setCss(this.$refs.dialog, 'transition-duration', `300ms`)
this.$setCss(this.$refs.dialog, 'transition-duration', `250ms`)
if (Math.abs(this.moveYDistance) > clientHeight / 2) {
this.$setCss(this.$refs.dialog, 'transform', `translate3d(0,${clientHeight}px,0)`)
setTimeout(this.hide, 300)
setTimeout(this.hide, 250)
} else {
this.$setCss(this.$refs.dialog, 'transform', `translate3d(0,0,0)`)
setTimeout(() => {
this.$setCss(this.$refs.dialog, 'transform', 'none')
// this.$setCss(this.$refs.dialog, 'transition-duration', `0ms`)
}, 300)
}, 250)
}
this.moveYDistance = 0
}

3
src/pages/slide/IndicatorHome.vue

@ -24,7 +24,7 @@ @@ -24,7 +24,7 @@
</div>
<div class="indicator" ref="indicator"></div>
</div>
<img v-hide="!loading" src="../../assets/img/icon/search-light.png"
<img v-hide="loading" src="../../assets/img/icon/search-light.png"
class="search"
@click="$nav('/home/search')"
style="margin-top: .5rem;">
@ -199,6 +199,7 @@ export default { @@ -199,6 +199,7 @@ export default {
width: 100%;
color: white;
height: @height;
transition: all .3s;
.notice {
opacity: 0;

1541
src/pages/slide/Slide.vue

File diff suppressed because it is too large Load Diff

12
src/pages/slide/SlideHorizontal.vue

@ -26,6 +26,7 @@ export default { @@ -26,6 +26,7 @@ export default {
startY: 0,
needCheck: true,
next: false,
startTime: null
}
},
computed: {
@ -50,6 +51,7 @@ export default { @@ -50,6 +51,7 @@ export default {
this.$setCss(this.wrapper, 'transition-duration', `0ms`)
this.startX = e.touches[0].pageX
this.startY = e.touches[0].pageY
this.startTime = Date.now()
},
touchMove(e) {
this.moveX = e.touches[0].pageX - this.startX
@ -83,10 +85,13 @@ export default { @@ -83,10 +85,13 @@ export default {
},
touchEnd(e) {
let isRight = this.moveX < 0
let next = true
if ((this.lIndex === 0 && !isRight) || (this.lIndex === this.total - 1 && isRight)) next = false
if ((this.lIndex === 0 && !isRight) || (this.lIndex === this.total - 1 && isRight)) this.next = false
if (Math.abs(this.moveX) > (this.wrapperWidth / 4) && next) {
let endTime = Date.now()
let gapTime = endTime - this.startTime
if (Math.abs(this.moveX) < 20) gapTime = 1000
if (Math.abs(this.moveX) > (this.wrapperWidth / 4)) gapTime = 100
if (gapTime < 150 && this.next) {
if (isRight) {
this.lIndex++
} else {
@ -105,6 +110,7 @@ export default { @@ -105,6 +110,7 @@ export default {
this.moveX = 0
this.next = false
this.needCheck = true
this.startTime = null
bus.emit(this.name + '-end', this.lIndex)
},
getDistance() {

17
src/pages/slide/SlideVertical.vue

@ -22,10 +22,11 @@ export default { @@ -22,10 +22,11 @@ export default {
startY: 0,
needCheck: true,
next: false,
startTime: null
}
},
computed: {
...mapState(['judgeValue','homeRefresh'])
...mapState(['judgeValue', 'homeRefresh'])
},
mounted() {
this.wrapper = this.$refs.wrapper
@ -38,6 +39,7 @@ export default { @@ -38,6 +39,7 @@ export default {
this.$setCss(this.wrapper, 'transition-duration', `0ms`)
this.startX = e.touches[0].pageX
this.startY = e.touches[0].pageY
this.startTime = Date.now()
},
touchMove(e) {
this.moveX = e.touches[0].pageX - this.startX
@ -49,10 +51,7 @@ export default { @@ -49,10 +51,7 @@ export default {
if (this.index === 0 && !isDown && this.next) {
bus.emit(this.name + '-moveY', this.moveY)
}
if ((this.index === 0 && !isDown) || (this.index === this.total - 1 && isDown)) return
if (this.next) {
this.$stopPropagation(e)
this.$setCss(this.wrapper, 'transform',
@ -81,10 +80,13 @@ export default { @@ -81,10 +80,13 @@ export default {
if (this.index === 0 && !isDown && this.moveY > (this.homeRefresh + this.judgeValue)) {
bus.emit(this.name + '-loading')
}
let next = true
if ((this.index === 0 && !isDown) || (this.index === this.total - 1 && isDown)) next = false
if ((this.index === 0 && !isDown) || (this.index === this.total - 1 && isDown)) this.next = false
if (Math.abs(this.moveY) > (this.wrapperHeight / 4) && next) {
let endTime = Date.now()
let gapTime = endTime - this.startTime
if (Math.abs(this.moveY) < 20) gapTime = 1000
if (Math.abs(this.moveY) > (this.wrapperHeight / 4)) gapTime = 100
if (gapTime < 150 && this.next) {
if (isDown) {
this.index++
} else {
@ -100,6 +102,7 @@ export default { @@ -100,6 +102,7 @@ export default {
reset() {
this.moveX = 0
this.next = false
this.startTime = null
this.needCheck = true
bus.emit(this.name + '-end', this.index)
},

247
src/pages/slide/SlideVerticalInfinite.vue

@ -0,0 +1,247 @@ @@ -0,0 +1,247 @@
<script lang="jsx">
import bus from "../../utils/bus";
import {mapState} from "vuex";
import * as Vue from "vue";
export default {
props: {
name: {
type: String,
default: () => ''
},
renderSlide: {
type: Function,
default: () => {
return null
}
},
list: {
type: Array,
default: () => {
return []
}
},
virtualTotal: {
type: Number,
default: () => 5
},
index: {
type: Number,
default: () => 0
},
},
data() {
return {
wrapper: null,
total: 0,
lIndex: 0,
wrapperWidth: 0,
wrapperHeight: 0,
moveX: 0,
moveY: 0,
startX: 0,
startY: 0,
needCheck: true,
next: false,
startTime: null,
appInsMap: new Map()
}
},
computed: {
...mapState(['judgeValue', 'homeRefresh'])
},
mounted() {
this.lIndex = this.index
this.checkChildren()
this.insertContent()
this.total = this.wrapper.children.length
},
methods: {
checkChildren() {
this.wrapper = this.$refs.wrapper
this.wrapperWidth = this.$getCss(this.wrapper, 'width')
this.wrapperHeight = this.$getCss(this.wrapper, 'height')
},
insertContent() {
let start = 0
let that = this
if (this.lIndex >= (this.virtualTotal - 1) / 2) {
start = this.lIndex - (this.virtualTotal - 1) / 2
}
let end = start + 5
if (end >= this.list.length) {
end = this.list.length
start = end - 5
}
console.log('start', start)
console.log('end', end)
this.list.slice(start, end).map(
(item, index) => {
let el = null
//0jqtrigger play
el = this.getInsEl(item, start + index, start + index === this.lIndex)
this.wrapper.appendChild(el)
}
)
this.$setCss(this.wrapper, 'transform', `translate3d(0px,
${-this.lIndex * this.wrapperHeight}px, 0px)`)
if (this.lIndex > 2 && this.list.length > 5) {
$(this.wrapper).find(".slide-item").each(function () {
if ((that.list.length - that.lIndex) > 2) {
$(this).css('top', (that.lIndex - 2) * that.wrapperHeight)
} else {
$(this).css('top', start * that.wrapperHeight)
}
})
}
},
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
},
touchStart(e) {
this.$setCss(this.wrapper, 'transition-duration', `0ms`)
this.startX = e.touches[0].pageX
this.startY = e.touches[0].pageY
this.startTime = Date.now()
},
touchMove(e) {
this.moveX = e.touches[0].pageX - this.startX
this.moveY = e.touches[0].pageY - this.startY
let isDown = this.moveY < 0
this.checkDirection(e)
if (this.lIndex === 0 && !isDown && this.next) {
bus.emit(this.name + '-moveY', this.moveY)
}
if ((this.lIndex === 0 && !isDown) || (this.lIndex === this.list.length - 1 && isDown)) return
if (this.next) {
this.$stopPropagation(e)
this.$setCss(this.wrapper, 'transform',
`translate3d(0,${this.getDistance()
+ this.moveY
+ (isDown ? this.judgeValue : -this.judgeValue)
}px, 0)`)
}
},
checkDirection(e) {
if (this.needCheck) {
// this.$stopPropagation(e)
} else {
return
}
if (Math.abs(this.moveX) > this.judgeValue || Math.abs(this.moveY) > this.judgeValue) {
let angle = (Math.abs(this.moveX) * 10) / (Math.abs(this.moveY) * 10)
this.next = angle <= 1;
// console.log(angle)
return this.needCheck = false
}
return this.needCheck = true
},
touchEnd(e) {
let isDown = this.moveY < 0
if (this.lIndex === 0 && !isDown && this.moveY > (this.homeRefresh + this.judgeValue)) {
bus.emit(this.name + '-loading')
}
if ((this.lIndex === 0 && !isDown) || (this.lIndex === this.list.length - 1 && isDown)) this.next = false
let endTime = Date.now()
let gapTime = endTime - this.startTime
if (Math.abs(this.moveY) < 20) gapTime = 1000
if (Math.abs(this.moveY) > (this.wrapperHeight / 4)) gapTime = 100
if (gapTime < 150 && this.next) {
if (isDown) {
this.lIndex++
} else {
this.lIndex--
}
// console.log(this.total)
let that = this
if (isDown) {
let addItemIndex = this.lIndex + 2
let res = $(that.wrapper).find(`.slide-item[data-index=${addItemIndex}]`)
if (this.total < this.virtualTotal) {
if (res.length === 0) {
this.wrapper.appendChild(this.getInsEl(this.list[addItemIndex], addItemIndex))
}
}
if (this.total === this.virtualTotal
&& this.lIndex >= (this.virtualTotal + 1) / 2
&& this.lIndex <= this.list.length - 3
) {
// console.log(videos)
if (res.length === 0) {
this.wrapper.appendChild(this.getInsEl(this.list[addItemIndex], addItemIndex))
this.appInsMap.get($(that.wrapper).find(".slide-item:first").data('index')).unmount()
// $(that.wrapper).find(".base-slide-item:first").remove()
$(that.wrapper).find(".slide-item").each(function () {
$(this).css('top', (that.lIndex - 2) * that.wrapperHeight)
})
}
}
if (this.total > this.virtualTotal) {
this.appInsMap.get($(that.wrapper).find(".slide-item:first").data('index')).unmount()
$(that.wrapper).find(".slide-item").each(function () {
$(this).css('top', (that.lIndex - 2) * that.wrapperHeight)
})
}
} else {
if (this.lIndex > 1 && this.lIndex <= this.list.length - 4) {
let addItemIndex = this.lIndex - 2
this.wrapper.prepend(this.getInsEl(this.list[addItemIndex], addItemIndex))
this.appInsMap.get($(that.wrapper).find(".slide-item:last").data('index')).unmount()
// $(that.wrapper).find(".base-slide-item:last").remove()
$(that.wrapper).find(".slide-item").each(function () {
$(this).css('top', (that.lIndex - 2) * that.wrapperHeight)
})
}
}
}
this.$setCss(this.wrapper, 'transition-duration', `300ms`)
this.$setCss(this.wrapper, 'transform',
`translate3d(0,${this.getDistance()}px, 0)`)
this.reset()
},
reset() {
this.moveX = 0
this.next = false
this.startTime = null
this.needCheck = true
bus.emit(this.name + '-end', this.lIndex)
},
getDistance() {
return -this.lIndex * this.wrapperHeight
},
},
render(createElement, context) {
return (
<div className="slide">
<div className="slide-wrapper"
style="flex-direction: column;"
ref="wrapper"
ontouchstart={this.touchStart.bind(this)}
ontouchmove={this.touchMove.bind(this)}
ontouchend={this.touchEnd.bind(this)}
>
{this.$slots.default && this.$slots.default()}
</div>
</div>
)
}
}
</script>

71
src/utils/mixin.js

@ -14,6 +14,7 @@ import Back from "../components/Back"; @@ -14,6 +14,7 @@ import Back from "../components/Back";
import Loading from "../components/Loading";
import BaseButton from "../components/BaseButton";
import CONST_VAR from "./const_var";
import Dom from "./dom";
export default {
components: {
@ -97,18 +98,78 @@ export default { @@ -97,18 +98,78 @@ export default {
hide: {
beforeMount: function (el, binding, vNode) {
if (binding.value) {
el.style.opacity = 1
} else {
el.style.opacity = 0
} else {
el.style.opacity = 1
}
},
updated: function (el, binding, vNode) {
if (binding.value) {
el.style.opacity = 1
} else {
el.style.opacity = 0
} else {
el.style.opacity = 1
}
}
}
},
love: {
beforeMount: function (el, binding, vNode) {
let isDbClick = false
let clickTimer = null
let dbClickTimer = null
let lastClickTime = null
let dbClick = (e) => {
console.log('dbClick')
let id = 'a' + Date.now()
let elWidth = 80
let rotate = randomNum(0, 1)
let template = `<img class="${rotate ? 'left love-dbclick' : 'right love-dbclick'}" id="${id}" src="${new URL('../assets/img/icon/loved.svg', import.meta.url).href}">`
let el = new Dom().create(template)
el.css({top: e.y - elWidth, left: e.x - elWidth / 2,})
new Dom(`#${binding.value}`).append(el)
setTimeout(() => {
new Dom(`#${id}`).remove()
}, 1000)
}
let randomNum = (minNum, maxNum) => {
switch (arguments.length) {
case 1:
return parseInt(Math.random() * minNum + 1, 10);
case 2:
return parseInt(Math.random() * (maxNum - minNum + 1) + minNum, 10);
default:
return 0;
}
}
let check = (e) => {
let checkTime = 400
if (isDbClick) {
dbClick(e)
clearTimeout(dbClickTimer);
dbClickTimer = setTimeout(() => {
isDbClick = false
}, 400);
return
}
let nowTime = new Date().getTime();
if (nowTime - lastClickTime < checkTime) {
dbClick(e)
lastClickTime = 0;
clickTimer && clearTimeout(clickTimer);
isDbClick = true
dbClickTimer = setTimeout(() => {
isDbClick = false
}, checkTime);
} else {
lastClickTime = nowTime;
clickTimer = setTimeout(() => {
console.log('单击')
}, checkTime);
}
}
el.addEventListener('click', check)
},
},
},
}
Loading…
Cancel
Save