15 changed files with 968 additions and 167 deletions
@ -0,0 +1,464 @@
@@ -0,0 +1,464 @@
|
||||
<template> |
||||
<div id="SlideImgs"> |
||||
<div class="img-slide-wrapper"> |
||||
<div class="img-slide-list" |
||||
ref="list" |
||||
@touchstart="start" |
||||
@touchmove="move" |
||||
@touchend="end"> |
||||
<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 globalMethods from '../../utils' |
||||
//TODO 放大功能待完善 |
||||
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 |
||||
|
||||
startX: 0, |
||||
startY: 0, |
||||
|
||||
moveX: 0, |
||||
moveY: 0, |
||||
width: document.body.clientWidth, |
||||
startTime: 0, |
||||
index: 0, |
||||
isDrawRight: false, |
||||
isDrawDown: false, |
||||
isTwo: false, |
||||
store: { |
||||
scale: 1 |
||||
}, |
||||
result: { |
||||
width: 414, |
||||
height: 737 |
||||
}, |
||||
x: 0, |
||||
y: 79, |
||||
scale: 1, |
||||
maxScale: 3, |
||||
minScale: 0.5, |
||||
point1: {x: 0, y: 0}, |
||||
point2: {x: 0, y: 0}, |
||||
diff: {x: 0, y: 0}, |
||||
lastPointermove: {x: 0, y: 0}, |
||||
lastPoint1: {x: 0, y: 0}, |
||||
lastPoint2: {x: 0, y: 0}, |
||||
lastCenter: {x: 0, y: 0}, |
||||
a: {}, |
||||
b: {}, |
||||
} |
||||
}, |
||||
created() { |
||||
this.width = document.body.clientWidth |
||||
}, |
||||
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) { |
||||
globalMethods.$setCss(this.$refs.list, 'transition-duration', `300ms`) |
||||
globalMethods.$setCss(this.$refs.list, 'transform', |
||||
`translate3d(${-this.getWidth(this.index)}px, 0px, 0px)`) |
||||
} |
||||
} else { |
||||
this.progress = 0 |
||||
// cancelAnimationFrame(this.cycleFn) |
||||
} |
||||
requestAnimationFrame(this.cycleFn) |
||||
} |
||||
// requestAnimationFrame(this.cycleFn) |
||||
}, |
||||
methods: { |
||||
getCenter(a, b) { |
||||
const x = (a.x + b.x) / 2; |
||||
const y = (a.y + b.y) / 2; |
||||
return {x, y} |
||||
}, |
||||
start(e) { |
||||
console.log('start') |
||||
if (this.state !== 'custom') { |
||||
this.state = 'stop' |
||||
} |
||||
if (e.touches && e.touches.length === 1) { |
||||
this.isTwo = false |
||||
globalMethods.$setCss(this.$refs.list, 'transition-duration', `0ms`) |
||||
this.startX = e.touches[0].pageX |
||||
this.startY = e.touches[0].pageY |
||||
this.startTime = Date.now() |
||||
} else { |
||||
this.isTwo = true |
||||
this.itemRefs[this.index].style['transition-duration'] = '0ms'; |
||||
|
||||
let events = e.touches[0]; |
||||
let events2 = e.touches[1]; |
||||
|
||||
if (e.cancelable) { |
||||
e.preventDefault(); |
||||
} |
||||
|
||||
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) |
||||
} |
||||
}, |
||||
move(e) { |
||||
if (e.touches && e.touches.length === 1) { |
||||
this.isTwo = false |
||||
|
||||
this.moveX = e.touches[0].pageX - this.startX |
||||
this.moveY = e.touches[0].pageY - this.startY |
||||
|
||||
this.isDrawRight = this.moveX < 0 |
||||
this.isDrawDown = this.moveY < 0 |
||||
|
||||
if (this.index === 0 && !this.isDrawRight) return |
||||
if (this.index === this.modelValue.imgs.length - 1 && this.isDrawRight) return |
||||
|
||||
globalMethods.$setCss(this.$refs.list, 'transform', |
||||
`translate3d(${-this.getWidth(this.index) + |
||||
this.moveX}px, 0px, 0px)`) |
||||
} else { |
||||
this.isTwo = true |
||||
if (e.cancelable) { |
||||
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 getDistance = function (start, stop) { |
||||
return Math.hypot(stop.x - start.x, stop.y - start.y); |
||||
}; |
||||
|
||||
// 双指缩放比例计算 |
||||
let ratio = getDistance(current1, current2) / getDistance(this.lastPoint1, this.lastPoint2); |
||||
|
||||
|
||||
// 计算当前双指中心点坐标 |
||||
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.itemRefs[this.index].style.transform = |
||||
`translate3d(${this.x}px,${this.y}px,0) scale(${this.store.scale * ratio})`; |
||||
|
||||
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.touches) |
||||
if (this.isTwo) { |
||||
this.store.scale = 1 |
||||
this.itemRefs[this.index].style['transition-duration'] = '300ms'; |
||||
this.itemRefs[this.index].style.transform = `translate3d(0,0,0) scale(1)`; |
||||
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.index === 0 && !this.isDrawRight) return |
||||
if (this.index === this.modelValue.imgs.length - 1 && this.isDrawRight) return |
||||
|
||||
let canSlide = this.width / 5 < Math.abs(this.moveX); |
||||
if (Date.now() - this.startTime < 40) canSlide = false |
||||
|
||||
if (canSlide) { |
||||
if (this.isDrawRight) { |
||||
this.index += 1 |
||||
} else { |
||||
this.index -= 1 |
||||
} |
||||
this.state = 'custom' |
||||
this.progress = (this.index + 1) * 100 |
||||
} else { |
||||
if (this.state !== 'custom') { |
||||
this.state = 'play' |
||||
} |
||||
} |
||||
globalMethods.$setCss(this.$refs.list, 'transition-duration', `300ms`) |
||||
globalMethods.$setCss(this.$refs.list, 'transform', |
||||
`translate3d(${-this.getWidth(this.index)}px, 0px, 0px)`) |
||||
} |
||||
}, |
||||
getWidth(index) { |
||||
return index * this.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%; |
||||
height: calc(100vh - 5rem); |
||||
overflow: auto; |
||||
color: white; |
||||
font-size: 1.4rem; |
||||
display: flex; |
||||
align-items: center; |
||||
justify-content: center; |
||||
|
||||
.img-slide-wrapper { |
||||
width: 100vw; |
||||
overflow: hidden; |
||||
|
||||
.img-slide-list { |
||||
width: 100vw; |
||||
display: flex; |
||||
|
||||
.img-slide-item { |
||||
min-width: 100vw; |
||||
display: flex; |
||||
align-items: center; |
||||
justify-content: center; |
||||
|
||||
img { |
||||
//transform-origin: left top; |
||||
width: 100vw; |
||||
//position: absolute; |
||||
//height: 100%; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
.content { |
||||
width: 100vw; |
||||
|
||||
.base-slide-item { |
||||
display: flex; |
||||
align-items: center; |
||||
justify-content: center; |
||||
} |
||||
|
||||
img { |
||||
width: 100vw; |
||||
} |
||||
|
||||
} |
||||
|
||||
.progress-bar { |
||||
position: absolute; |
||||
width: 100vw; |
||||
bottom: 0; |
||||
display: flex; |
||||
box-sizing: border-box; |
||||
padding: 0 .5rem; |
||||
justify-content: space-between; |
||||
|
||||
.bar { |
||||
border-radius: 1rem; |
||||
flex: 1; |
||||
margin: 0 .2rem; |
||||
height: .2rem; |
||||
background: gray; |
||||
position: relative; |
||||
|
||||
.progress { |
||||
border-radius: 1rem; |
||||
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