27 changed files with 745 additions and 678 deletions
@ -1,13 +1,235 @@
@@ -1,13 +1,235 @@
|
||||
<template> |
||||
<script setup> |
||||
|
||||
</template> |
||||
import {reactive} from "vue"; |
||||
import BaseMarquee from "../BaseMarquee"; |
||||
|
||||
const {item, index, prefix, isMy, isUp, isPlay} = defineProps({ |
||||
item: { |
||||
type: Object, |
||||
default: () => { |
||||
return {} |
||||
} |
||||
}, |
||||
index: { |
||||
type: Number, |
||||
default: () => { |
||||
return -1 |
||||
} |
||||
}, |
||||
prefix: { |
||||
type: String, |
||||
default: () => { |
||||
return '' |
||||
} |
||||
}, |
||||
isMy: { |
||||
type: Boolean, |
||||
default: () => { |
||||
return false |
||||
} |
||||
}, |
||||
isUp: { |
||||
type: Boolean, |
||||
default: () => { |
||||
return false |
||||
} |
||||
}, |
||||
//用于第一条数据,自动播放,如果都用事件去触发播放,有延迟 |
||||
isPlay: { |
||||
type: Boolean, |
||||
default: () => { |
||||
return true |
||||
} |
||||
}, |
||||
}) |
||||
|
||||
const state = reactive({ |
||||
isAttention: false, |
||||
name: `v-${prefix}-${index}-video`, |
||||
test: [1, 2], |
||||
}) |
||||
|
||||
<script> |
||||
export default { |
||||
name: "ItemDesc" |
||||
} |
||||
</script> |
||||
<template> |
||||
<div class="item-desc"> |
||||
<div class="content ml1r mb2r" v-if="!isMy"> |
||||
<div class="location-wrapper" v-if=" item.city || item.address"> |
||||
<div class="location"> |
||||
<img src="../../assets/img/icon/location.webp" alt=""> |
||||
<span>{{ item.city }}</span> |
||||
<template v-if="item.address"> |
||||
<div class="gang"></div> |
||||
</template> |
||||
<span>{{ item.address }}</span> |
||||
</div> |
||||
</div> |
||||
<div class="name mb1r fb" @click.stop="$emit('goUserInfo')">@{{ item.author.nickname }}</div> |
||||
<div class="description mb1r"> |
||||
{{ item.desc }} |
||||
</div> |
||||
<div class="music" @click.stop="$nav('/music')"> |
||||
<img src="../../assets/img/icon/music.svg" alt="" class="music-image"> |
||||
<BaseMarquee :key="state.name" |
||||
:name="state.name" |
||||
:isPlay="isPlay" |
||||
:text="item.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 state.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> |
||||
</template> |
||||
|
||||
<style scoped lang="less"> |
||||
@import "../../assets/less/color"; |
||||
|
||||
.item-desc { |
||||
|
||||
.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; |
||||
} |
||||
} |
||||
} |
||||
|
||||
|
||||
<style scoped> |
||||
.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); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
</style> |
@ -1,13 +0,0 @@
@@ -1,13 +0,0 @@
|
||||
<template> |
||||
|
||||
</template> |
||||
|
||||
<script> |
||||
export default { |
||||
name: "ItemOptions" |
||||
} |
||||
</script> |
||||
|
||||
<style scoped> |
||||
|
||||
</style> |
@ -0,0 +1,193 @@
@@ -0,0 +1,193 @@
|
||||
<script setup> |
||||
import BaseMusic from "../BaseMusic"; |
||||
import Utils from "../../utils"; |
||||
import {reactive} from "vue"; |
||||
|
||||
const {item, index, prefix, isMy, isUp, isPlay} = defineProps({ |
||||
item: { |
||||
type: Object, |
||||
default: () => { |
||||
return {} |
||||
} |
||||
}, |
||||
index: { |
||||
type: Number, |
||||
default: () => { |
||||
return -1 |
||||
} |
||||
}, |
||||
prefix: { |
||||
type: String, |
||||
default: () => { |
||||
return '' |
||||
} |
||||
}, |
||||
isMy: { |
||||
type: Boolean, |
||||
default: () => { |
||||
return false |
||||
} |
||||
}, |
||||
isUp: { |
||||
type: Boolean, |
||||
default: () => { |
||||
return false |
||||
} |
||||
}, |
||||
//用于第一条数据,自动播放,如果都用事件去触发播放,有延迟 |
||||
isPlay: { |
||||
type: Boolean, |
||||
default: () => { |
||||
return true |
||||
} |
||||
}, |
||||
}) |
||||
|
||||
const state = reactive({ |
||||
isAttention: false, |
||||
name: `v-${prefix}-${index}-video`, |
||||
}) |
||||
|
||||
function loved(e, index) { |
||||
// this.lVideo.isLoved = !this.lVideo.isLoved |
||||
// this.$emit('update:video', this.lVideo) |
||||
} |
||||
|
||||
function attention() { |
||||
// let option = this.$refs['attention-option'] |
||||
// option.classList.add('attention') |
||||
// setTimeout(() => { |
||||
// this.isAttention = true |
||||
// }, 1000) |
||||
} |
||||
|
||||
</script> |
||||
|
||||
<template> |
||||
<div class="toolbar mb1r"> |
||||
<div class="avatar-ctn mb4r"> |
||||
<img class="avatar" :src="item.author.avatar" alt="" |
||||
@click.stop="$emit('goUserInfo')"> |
||||
<transition name="fade"> |
||||
<div v-if="!state.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="!item.isLoved"> |
||||
<img src="../../assets/img/icon/loved.svg" class="love-image" v-if="item.isLoved"> |
||||
</div> |
||||
<span>{{ Utils.formatNumber(item.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>{{ Utils.formatNumber(item.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>{{ Utils.formatNumber(item.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="item.music.cover" |
||||
:key="state.name" |
||||
:name="state.name" |
||||
:isPlay="state.isPlay" |
||||
@click.stop="$emit('goMusic')" |
||||
/> |
||||
</div> |
||||
|
||||
</template> |
||||
|
||||
<style scoped lang="less"> |
||||
.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; |
||||
} |
||||
|
||||
} |
||||
</style> |
@ -1,478 +0,0 @@
@@ -1,478 +0,0 @@
|
||||
<template> |
||||
<div id="SlideImgs"> |
||||
<div class="img-slide-wrapper"> |
||||
<div class="img-slide-list" |
||||
ref="list" |
||||
@touchstart="touchstart" |
||||
@touchmove="touchmove" |
||||
@touchend="touchend"> |
||||
<div class="img-slide-item" v-for="img in modelValue.imgs"> |
||||
<img :ref="setItemRef" :src="img"> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
<div class="progress-bar" v-if="true"> |
||||
<div class="bar" v-for="(img,index) in modelValue.imgs"> |
||||
<div class="progress" |
||||
:style="getProgressWidth(index)"></div> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</template> |
||||
|
||||
<script> |
||||
import enums from "../../utils/enums"; |
||||
import Utils from '../../utils' |
||||
import GM from '../../utils' |
||||
import {mat4} from 'gl-matrix' |
||||
import {cloneDeep} from "lodash"; |
||||
|
||||
let out = new Float32Array([ |
||||
0, 0, 0, 0, |
||||
0, 0, 0, 0, |
||||
0, 0, 0, 0, |
||||
0, 0, 0, 0 |
||||
]) |
||||
let ov = new Float32Array([ |
||||
1, 0, 0, 0, |
||||
0, 1, 0, 0, |
||||
0, 0, 1, 0, |
||||
0, 0, 0, 1, |
||||
]); |
||||
let original = cloneDeep(ov) |
||||
const rectMap = new Map() |
||||
export default { |
||||
name: "SlideImgs", |
||||
components: {}, |
||||
props: { |
||||
modelValue: { |
||||
type: Object, |
||||
default() { |
||||
return { |
||||
type: 'imgs', |
||||
imgs: [ |
||||
new URL('../../assets/img/poster/0.jpg', import.meta.url).href, |
||||
new URL('../../assets/img/poster/1.jpg', import.meta.url).href, |
||||
new URL('../../assets/img/poster/2.jpg', import.meta.url).href, |
||||
new URL('../../assets/img/poster/3.jpg', import.meta.url).href, |
||||
], |
||||
"id": "034ae83b-ca0a-401a-b7c6-cf78361bae7b", |
||||
video: 'http://douyin.ttentau.top/0.mp4', |
||||
"video_data_size": 26829508, |
||||
"duration": 427780, |
||||
"desc": "我不管我们宿舍第一好看", |
||||
"allow_download": 0, |
||||
"allow_duet": 0, |
||||
"allow_react": 0, |
||||
"allow_music": 1, |
||||
"allow_douplus": 1, |
||||
"allow_share": 1, |
||||
"digg_count": 10480000, |
||||
"comment_count": 79000, |
||||
"download_count": 6, |
||||
"play_count": 0, |
||||
"share_count": 119000, |
||||
"forward_count": 0, |
||||
"collect_count": 3, |
||||
"sort": 195, |
||||
"is_top": 0, |
||||
"city": "北京", |
||||
address: '中央戏剧学院', |
||||
"musicId": "2ee213c6-3e3f-4758-ba5a-7f1c955604a4", |
||||
"create_time": "1630423555", |
||||
"creator_id": "93864497380", |
||||
"status": 1, |
||||
"topics": [ |
||||
{ |
||||
"id": "85ceda30-898f-4b57-b891-0e58b3ab99a9", |
||||
"name": "敬礼变装", |
||||
"creator_id": "93864497380", |
||||
"create_time": "1630423555", |
||||
"status": 1 |
||||
}, |
||||
{ |
||||
"id": "85ceda30-898f-4b57-b891-0e58b3ab99a9", |
||||
"name": "宿舍", |
||||
"creator_id": "93864497380", |
||||
"create_time": "1630423555", |
||||
"status": 1 |
||||
} |
||||
], |
||||
"music": { |
||||
"id": "cde50af2-628c-4d28-b9c6-67237a62518e", |
||||
"cover": "https://p29.douyinpic.com/img/tos-cn-avt-0015/f4de202ff2e41b523838a4a767aebd16~c5_100x100.jpeg?from=116350172", |
||||
"mp3": "https://sf3-cdn-tos.douyinstatic.com/obj/ies-music/1658584661080088.mp3", |
||||
"title": "@穷电影创作的原声-小高快起来跳舞", |
||||
"creator_id": "93864497380", |
||||
"create_time": "1630423555", |
||||
"status": 1 |
||||
}, |
||||
"author": { |
||||
"id": "1", |
||||
"unique_id_modify_time": "1630393144", |
||||
"unique_id": "10040050", |
||||
"favoriting_count": 143, |
||||
"avatar": new URL('../../assets/img/icon/avatar/3.png', import.meta.url).href, |
||||
school: { |
||||
name: '中央戏剧学院', |
||||
department: null, |
||||
joinTime: null, |
||||
education: null, |
||||
displayType: enums.DISPLAY_TYPE.ALL, |
||||
}, |
||||
"city": "", |
||||
"province": '北京', |
||||
"country": "", |
||||
"location": "", |
||||
"birthday": "2002-01-01", |
||||
"cover": "https://p3.douyinpic.com/obj/c8510002be9a3a61aad2?from=116350172", |
||||
"following_count": 66, |
||||
"follower_count": 235000, |
||||
"aweme_count": 1796000, |
||||
"nickname": "我是小睿耶", |
||||
certification: '', |
||||
"phone": "", |
||||
"sex": "", |
||||
"last_login_time": "1630423555", |
||||
"create_time": "1630423555", |
||||
"status": 1, |
||||
"desc": `一个普普通通学表演的\n看到的人都能开开心心`, |
||||
"is_private": 0 |
||||
} |
||||
} |
||||
} |
||||
} |
||||
}, |
||||
data() { |
||||
return { |
||||
itemRefs: [], |
||||
baseActiveIndex: 0, |
||||
progress: 0, |
||||
cycleFn: null, |
||||
state: 'play',//stop,custom |
||||
|
||||
index: 0, |
||||
isNext: false, |
||||
isTwo: false, |
||||
last: { |
||||
ratio: 1, |
||||
point1: {x: 0, y: 0}, |
||||
point2: {x: 0, y: 0}, |
||||
}, |
||||
start: { |
||||
point1: {x: 0, y: 0}, |
||||
point2: {x: 0, y: 0}, |
||||
center: {x: 0, y: 0}, |
||||
time: 0 |
||||
}, |
||||
move: {x: 0, y: 0}, |
||||
wrapper: { |
||||
width: 0 |
||||
} |
||||
} |
||||
}, |
||||
created() { |
||||
}, |
||||
watch: { |
||||
state(newVal, oldVal) { |
||||
return |
||||
console.log('newVal', newVal) |
||||
if (newVal === 'play') requestAnimationFrame(this.cycleFn) |
||||
if (newVal === 'stop') cancelAnimationFrame(this.cycleFn) |
||||
if (newVal === 'custom') cancelAnimationFrame(this.cycleFn) |
||||
} |
||||
}, |
||||
mounted() { |
||||
this.cycleFn = () => { |
||||
if (this.state !== 'play') return cancelAnimationFrame(this.cycleFn) |
||||
if (this.progress < this.modelValue.imgs.length * 100) { |
||||
this.progress += .4 |
||||
this.index = parseInt(this.progress / 100) |
||||
if (this.$refs.list) { |
||||
Utils.$setCss(this.$refs.list, 'transition-duration', `300ms`) |
||||
Utils.$setCss(this.$refs.list, 'transform', `translate3d(${this.getSlideDistance()}px, 0px, 0px)`) |
||||
} |
||||
} else { |
||||
this.progress = 0 |
||||
// cancelAnimationFrame(this.cycleFn) |
||||
} |
||||
requestAnimationFrame(this.cycleFn) |
||||
} |
||||
this.wrapper.width = GM.$getCss(this.$refs.list, 'width') |
||||
return |
||||
// requestAnimationFrame(this.cycleFn) |
||||
}, |
||||
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); |
||||
}, |
||||
touchstart(e) { |
||||
console.log('start', e.touches.length) |
||||
if (this.state !== 'custom') { |
||||
this.state = 'stop' |
||||
} |
||||
if (e.touches.length === 1) { |
||||
this.isTwo = false |
||||
Utils.$setCss(this.$refs.list, 'transition-duration', `0ms`) |
||||
this.start = { |
||||
point1: { |
||||
x: e.touches[0].pageX, |
||||
y: e.touches[0].pageY, |
||||
}, |
||||
time: Date.now() |
||||
} |
||||
} else { |
||||
if (this.isTwo) return |
||||
|
||||
this.isTwo = true |
||||
this.itemRefs[this.index].style['transition-duration'] = '0ms'; |
||||
|
||||
this.last.point1 = this.start.point1 = {x: e.touches[0].pageX, y: e.touches[0].pageY}; |
||||
this.last.point2 = this.start.point2 = {x: e.touches[1].pageX, y: e.touches[1].pageY}; |
||||
this.start.center = this.getCenter(this.start.point1, this.start.point2) |
||||
} |
||||
}, |
||||
touchmove(e) { |
||||
console.log('move', e.touches.length) |
||||
if (this.isTwo && e.touches.length === 1) { |
||||
Utils.$stopPropagation(e) |
||||
|
||||
// console.log('单手移动',) |
||||
let current = {x: e.touches[0].pageX, y: e.touches[0].pageY} |
||||
let movementX = current.x - this.last.point1.x |
||||
let movementY = current.y - this.last.point1.y |
||||
console.log(movementX, movementY) |
||||
const t = new Float32Array([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, movementX, movementY, 0, 1,]); |
||||
ov = mat4.multiply(out, t, ov); |
||||
this.itemRefs[this.index].style.transform = `matrix3d(${ov.toString()})`; |
||||
this.last.point1 = current |
||||
} else { |
||||
if (e.touches.length === 1) { |
||||
this.isTwo = false |
||||
this.move.x = e.touches[0].pageX - this.start.point1.x |
||||
this.move.y = e.touches[0].pageY - this.start.point1.y |
||||
this.isNext = this.move.x < 0 |
||||
if (!this.canNext(this.isNext)) return |
||||
Utils.$stopPropagation(e) |
||||
Utils.$setCss(this.$refs.list, 'transform', `translate3d(${this.getSlideDistance() + this.move.x}px, 0px, 0px)`) |
||||
} else { |
||||
Utils.$stopPropagation(e) |
||||
this.isTwo = true |
||||
|
||||
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.start.point1, this.start.point2); |
||||
|
||||
let rect = {x: 0, y: 0} |
||||
if (rectMap.has(this.index)) { |
||||
rect = rectMap.get(this.index) |
||||
} else { |
||||
//getBoundingClientRect在手机上获取不到值 |
||||
let offset = $(this.itemRefs[this.index]).offset() |
||||
rect.x = offset.left |
||||
rect.y = offset.top |
||||
rectMap.set(this.index, rect) |
||||
} |
||||
|
||||
let center = this.getCenter(current1, current2) |
||||
center.x -= rect.x |
||||
center.y -= rect.y |
||||
//用最新的放大倍数ratio除以之前的放大ov[0]倍数,算出本次要累加放大的倍数 |
||||
let zoom = ratio / ov[0] |
||||
const x = center.x * (1 - zoom); |
||||
const y = center.y * (1 - zoom); |
||||
const t = new Float32Array([zoom, 0, 0, 0, 0, zoom, 0, 0, 0, 0, 1, 0, x, y, 0, 1,]); |
||||
//如果zoom是每次都是最后放大倍数,第三个参数用原值(即,矩阵x乘时,都是乘以单位矩阵) |
||||
//如果zoom是累加放大(比如每次都是0.15),第三个参数用ov。这里还是采用累加计算 |
||||
ov = mat4.multiply(out, t, ov); |
||||
|
||||
let dRatio = this.last.ratio - ratio |
||||
//如果本次比例和上次的不超过0.02。那么判定为平移 |
||||
if (Math.abs(dRatio) <= 0.02) { |
||||
let movementX = current1.x - this.last.point1.x |
||||
let movementY = current1.y - this.last.point1.y |
||||
let movement2X = current2.x - this.last.point2.x |
||||
let movement2Y = current2.y - this.last.point2.y |
||||
|
||||
let minX = Math.min(movementX, movement2X) |
||||
let minY = Math.min(movementY, movement2Y) |
||||
const t1 = new Float32Array([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, minX, minY, 0, 1,]); |
||||
ov = mat4.multiply(out, t1, ov); |
||||
} |
||||
|
||||
this.itemRefs[this.index].style.transform = `matrix3d(${ov.toString()})`; |
||||
this.last.point1 = current1 |
||||
this.last.point2 = current2 |
||||
this.last.ratio = ratio |
||||
} |
||||
} |
||||
}, |
||||
touchend(e) { |
||||
console.log('end', e.touches.length, this.isTwo) |
||||
if (this.isTwo && e.touches.length === 1) { |
||||
Utils.$stopPropagation(e) |
||||
|
||||
//双指缩放,但只松开了一只手 |
||||
this.last.point1 = {x: e.touches[0].pageX, y: e.touches[0].pageY} |
||||
} else { |
||||
if (this.isTwo) { |
||||
Utils.$stopPropagation(e) |
||||
|
||||
ov = original |
||||
this.itemRefs[this.index].style['transition-duration'] = '300ms'; |
||||
this.itemRefs[this.index].style.transform = `matrix3d(${ov.toString()})`; |
||||
// if (this.state !== 'custom') { |
||||
// this.state = 'play' |
||||
// } |
||||
// if (e.touches.length) { |
||||
// this.point1 = {x: e.touches[0].pageX, y: e.touches[0].pageY} |
||||
// } |
||||
|
||||
} else { |
||||
if (!this.canNext(this.isNext)) return |
||||
Utils.$stopPropagation(e) |
||||
|
||||
let canSlide = this.wrapper.width / 5 < Math.abs(this.move.x); |
||||
if (Date.now() - this.start.time < 40) canSlide = false |
||||
|
||||
if (canSlide) { |
||||
if (this.isNext) { |
||||
this.index += 1 |
||||
} else { |
||||
this.index -= 1 |
||||
} |
||||
this.state = 'custom' |
||||
this.progress = (this.index + 1) * 100 |
||||
} else { |
||||
if (this.state !== 'custom') { |
||||
this.state = 'play' |
||||
} |
||||
} |
||||
Utils.$setCss(this.$refs.list, 'transition-duration', `300ms`) |
||||
Utils.$setCss(this.$refs.list, 'transform', `translate3d(${this.getSlideDistance()}px, 0px, 0px)`) |
||||
} |
||||
} |
||||
}, |
||||
canNext(isNext) { |
||||
return !((this.index === 0 && !isNext) || (this.index === this.modelValue.imgs.length - 1 && isNext)); |
||||
}, |
||||
getSlideDistance() { |
||||
return -this.index * this.wrapper.width |
||||
}, |
||||
setItemRef(el) { |
||||
if (el) { |
||||
this.itemRefs.push(el) |
||||
} |
||||
}, |
||||
beforeUpdate() { |
||||
this.itemRefs = [] |
||||
}, |
||||
updated() { |
||||
console.log(this.itemRefs) |
||||
}, |
||||
toggle() { |
||||
return |
||||
if (this.state === 'stop') { |
||||
this.state = 'play' |
||||
requestAnimationFrame(this.cycleFn) |
||||
} else { |
||||
this.state = 'stop' |
||||
} |
||||
}, |
||||
getProgressWidth(index) { |
||||
if (this.progress >= (index + 1) * 100) return {width: '100%'} |
||||
return {width: `${this.progress - index * 100 < 0 ? 0 : this.progress - index * 100}%`} |
||||
} |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
<style scoped lang="less"> |
||||
html { |
||||
touch-action: none; |
||||
} |
||||
|
||||
#SlideImgs { |
||||
position: relative; |
||||
background: black; |
||||
width: 100%; |
||||
height: 100%; |
||||
overflow: hidden; |
||||
color: white; |
||||
font-size: 14rem; |
||||
|
||||
.img-slide-wrapper { |
||||
height: 100%; |
||||
width: 100%; |
||||
|
||||
.img-slide-list { |
||||
height: 100%; |
||||
width: 100%; |
||||
display: flex; |
||||
position: relative; |
||||
|
||||
.img-slide-item { |
||||
height: 100%; |
||||
width: 100%; |
||||
flex-shrink: 0; |
||||
display: flex; |
||||
align-items: center; |
||||
justify-content: center; |
||||
|
||||
img { |
||||
transform-origin: 0 0; |
||||
width: 100%; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
|
||||
.progress-bar { |
||||
position: absolute; |
||||
width: 100%; |
||||
bottom: 0; |
||||
display: flex; |
||||
box-sizing: border-box; |
||||
padding: 0 5rem; |
||||
justify-content: space-between; |
||||
|
||||
.bar { |
||||
border-radius: 10rem; |
||||
flex: 1; |
||||
margin: 0 2rem; |
||||
height: 2rem; |
||||
background: gray; |
||||
position: relative; |
||||
|
||||
.progress { |
||||
border-radius: 10rem; |
||||
position: absolute; |
||||
left: 0; |
||||
height: 2rem; |
||||
background: white; |
||||
//width: 100%; |
||||
//animation: start 3s linear; |
||||
|
||||
@keyframes start { |
||||
0% { |
||||
width: 0; |
||||
} |
||||
100% { |
||||
width: 100%; |
||||
} |
||||
} |
||||
} |
||||
|
||||
} |
||||
} |
||||
} |
||||
</style> |
Loading…
Reference in new issue