关于个人项目(臻美MV【仿抖音App】)滑动切换视频的分析(前端角度)

桃扇骨 2021-10-01 03:08 384阅读 0赞

我们知道你天天刷抖音的时候可以上滑切换视频,互不影响。那么我们站在前端的角度能否可以实现这种效果呢?
这是我的个人项目:臻美MV

下面我是用Vue写的,现在我把它开源。
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

Vue:

初始界面

  1. <template>
  2. <div class="box jz">
  3. <div>
  4. <img src="../assets/MV.png" alt="">
  5. </div>
  6. <mu-button fab color="primary" @click="go" class="go" >
  7. <mu-icon value="arrow_right_alt" color="white" size="34"></mu-icon>
  8. </mu-button>
  9. </div>
  10. </template>
  11. <script> export default { name: 'index', data () { return { msg: '' } }, methods: { go () { this.$router.push({ name: 'mv', params: { id: this.$store.state.id } }) } }, mounted () { this.$axios.get(['/top/mv?limit=5']) .then((response) => { // success let num = Math.floor(Math.random() * 5 + 1) localStorage.setItem('i', num) console.log(response.data.data) localStorage.setItem('list', JSON.stringify(response.data.data[num])) this.$store.state.id = response.data.data[num].id }) .catch((error) => { // error console.log(error) }) } } </script>
  12. <style scoped> @keyframes my{ from{ opacity: 0;} to{ opacity: 1;} } .go{ margin-top:60px ;} .jz{ animation:my 0.3s;} .box{ text-align: center;} .box img{ width: 25%;margin-top:25vh;} </style>

MV界面

  1. <template>
  2. <div class="box">
  3. <v-carousel :cycle="false" :show-arrows="false" hide-delimiters @change="f(index1)" v-model="index1" class="view" height="100vh">
  4. <v-carousel-item v-for="(item,index) in list" :key="index" class="item" @touchstart="cl">
  5. <div class="inner">
  6. <video :src="url" autoplay="autoplay" v-if="playIndex==index"></video>
  7. <div class="foot">
  8. <div class="in">
  9. <p class="user">@ {
  10. {item.artistName}}</p>
  11. <p class="name">{
  12. {item.name}}</p>
  13. </div>
  14. <div class="pl">
  15. <mu-icon value="speaker_notes" @click="openBotttomSheet" color="white" size="32"></mu-icon>
  16. </div>
  17. </div>
  18. </div>
  19. </v-carousel-item>
  20. </v-carousel>
  21. <mu-bottom-sheet :open.sync="open" class="sheet">
  22. <mu-load-more @refresh="refresh" :refreshing="refreshing" :loading="loading" @load="load">
  23. <div v-for="(item,index) in plist" :key='index' class="ovf pll">
  24. <div class="pl-l"><img :src="item.user.avatarUrl" alt=""></div>
  25. <div class="pl-r">
  26. <div class="name1">{
  27. {item.user.nickname}}</div>
  28. <div class="con">{
  29. {item.content}}</div>
  30. </div>
  31. </div>
  32. </mu-load-more>
  33. </mu-bottom-sheet>
  34. </div>
  35. </template>
  36. <script>import { mapGetters} from 'vuex' export default { name: 'mv', data () { return { name: '', user: '', index1: '', url: '', list: [], num: 1, playIndex: null, open: false, idd: '', plist: '', num1: 0, refreshing: false, loading: false } }, methods: { //下滑重新加载更多评论 refresh () { this.refreshing = true this.$refs.container.scrollTop = 0 setTimeout(() => { this.refreshing = false this.$axios.get(['/comment/mv?id=' + this.idd]) //这里的url我隐藏了。谢谢理解 .then((response) => { // success console.log(response.data.comments) this.plist = response.data.comments }) .catch((error) => { // error console.log(error) }) }, 2000) }, //下滑加载更多评论 load () { this.loading = true setTimeout(() => { this.loading = false this.num1 += 10 this.$axios.get(['/comment/mv?id=' + this.idd + '&limit=' + this.num1]) //这里的url我隐藏了。谢谢理解 .then((response) => { // success console.log(response.data.comments) this.plist = response.data.comments }) .catch((error) => { // error console.log(error) }) }, 2000) }, //右滑切换视频 f (index) { console.log(index) let id = this.list[index].id this.idd = id this.$axios.get(['/mv/url?id=' + id])//这里的url我隐藏了。谢谢理解 .then((response) => { // success console.log(response.data.data.url) this.url = response.data.data.url this.playIndex = index }) .catch((error) => { // error console.log(error) }) }, //关闭评论 closeBottomSheet () { this.open = false }, //打开评论 openBotttomSheet () { this.open = true console.log(this.idd) this.$axios.get(['/comment/mv?id=' + this.idd])//这里的url我隐藏了。谢谢理解 .then((response) => { // success console.log(response.data.comments) this.plist = response.data.comments }) .catch((error) => { // error console.log(error) }) }, //数据自动增加点击增加 cl () { this.num++ this.$axios.get(['/top/mv?limit=' + this.num])//这里的url我隐藏了。谢谢理解 .then((response) => { // success console.log(response.data.data) this.list = response.data.data }) .catch((error) => { // error console.log(error) }) } }, computed: { ...mapGetters({ getid: 'getid' }) }, //初始化数据 mounted () { const list = localStorage.getItem('list') this.idd = this.$route.params.id this.list.push(JSON.parse(list)) console.log(this.list) this.index1 = localStorage.getItem('list') this.$axios.get(['/mv/url?id=' + this.$route.params.id])//这里的url我隐藏了。谢谢理解 .then((response) => { // success this.playIndex = localStorage.getItem('i') console.log(response.data.data.url) this.url = response.data.data.url }) .catch((error) => { // error console.log(error) }) } } </script>
  37. <style scoped> html,body{ position: relative;} .box{ width: 100%;height: 100vh;background: #333;} .sheet{ height: 70vh;overflow: auto;border-top-right-radius:0.2rem;border-top-left-radius:0.2rem;z-index: 1000} .inner{ width: 100%;height: 100vh;position: relative;} .in{ width: 80%;float: left;} .pl{ width: 10%;float: right;margin-top:0.2rem ;} .foot{ width: 90%;position: absolute;bottom: 14vh;left:5% ;} .name{ color: #fff;font-size: 20px;font-weight: bold;} .user{ color: #fff;margin-bottom:0.3rem;font-size: 16px;} video{ width: 100%;height:auto;margin-top:35vh;} .item{ width: 100%;height: 100vh;} .view{ width: 100%;height: 100vh !important;} .pll{ width: 95%;margin: 10px auto;overflow: hidden;padding: 10px 0;} .pl:last-child{ border:none;} .pl-l{ width: 20%;float: left;} .pl-l img{ width: 60%;border-radius: 50%;} .pl-r{ width: 78%;float: left;} .name1{ font-size:14px;color: #666;font-weight: bold;margin-bottom: 5px;} .con{ width: 100%;line-height: 24px;color: #333333;font-size:16px;} </style>

我还有微信小程序,这里我也分享给大家。

微信小程序:

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

MV界面

wxml

  1. <!--pages/video/video.wxml-->
  2. <view class="container">
  3. <view class="page-body">
  4. <view class="page-section page-section-spacing swiper">
  5. <swiper current="{ {current}}" bindchange='onSlideChangeEnd' autoplay="{ {autoplay}}" circular="{ {circular}}" vertical="{ {vertical}}" interval="{ {interval}}" duration="{ {duration}}" previous-margin="{ {previousMargin}}px" next-margin="{ {nextMargin}}px" >
  6. <block wx:for="{ {vlists}}" wx:key="index" wx:for-item="item">
  7. <swiper-item bindtouchstart="touchStart">
  8. <view class="swiper-item " >
  9. <view><image src="https://www.maomin.club/data/pl.png" class="pl" bindtap='showFrame' id='i{ {index}}'></image></view>
  10. <video src="{ {url}}" wx:if='{ {playIndex==index}}' autoplay="{ {true}}" controls='{ {controls}}' id="{ {index}}" ></video>
  11. <view wx:if="{ {index==0}}" class="start" bindtap="cli">点我一下,精彩MV马上开始</view>
  12. <view class="foot">
  13. <view class="songer">@ {
  14. {item.artistName}}</view>
  15. <view class="name">{
  16. {item.name}}</view>
  17. </view>
  18. <view wx:if='{ {flag}}'>
  19. <view class='wrap { {wrapAnimate}}' style='background:rgba(0,0,0,{ { bgOpacity}});'></view>
  20. <view catchtap='hideFrame' class='frame-wrapper { {frameAnimate}}'>
  21. <view catchtap='catchNone' class='frame' >
  22. <scroll-view class="title-wrapper" scroll-y style="width: 100%" bindscrolltolower='down1' id='s{ {index}}'>
  23. <view wx:for="{ {plist}}" wx:key="index" wx:for-item="item" class="plitem">
  24. <view class="plitem_l">
  25. <image src="{ {item.user.avatarUrl}}"></image>
  26. </view>
  27. <view class="plitem_r">
  28. <view class="username">{
  29. {item.user.nickname}}</view>
  30. <view class="content"> {
  31. {item.content}}</view>
  32. </view>
  33. </view>
  34. </scroll-view>
  35. </view>
  36. </view>
  37. </view>
  38. </view>
  39. </swiper-item>
  40. </block>
  41. </swiper>
  42. </view>
  43. </view>
  44. </view>

wxss

  1. /* pages/video/video.wxss */
  2. page { height: 100%; font-size: 32rpx; background: #000; }
  3. swiper{ height:100vh; }
  4. .swiper-item{ display: block; height:100%; position: relative; align-items: center; justify-content: center; font-size: 36rpx; }
  5. .cover{ width: 100%; height:100vh ; }
  6. .name{ color: #fff; font-size: 34rpx; font-weight: bold; }
  7. video{ width: 100%; height: 100vh; }
  8. .start{ color: #fff; text-align: center; line-height: 65vh; }
  9. .songer{ color: #fff; font-size: 26rpx; margin-bottom:25rpx; }
  10. .pl{ width:10%; height:74rpx; float: right;position: absolute;right: 4%;bottom:13vh;z-index: 100; }
  11. .foot{ width: 70%; position: absolute; bottom: 10%; left: 5%; }
  12. .wrapAnimate{ animation: wrapAnimate 0.5s ease-in-out forwards}
  13. @keyframes wrapAnimate{
  14. 0%{ }
  15. 100%{ background:rgba(0,0,0,0.35);}
  16. }
  17. .wrapAnimateOut{ animation: wrapAnimateOut 0.4s ease-in-out forwards}
  18. @keyframes wrapAnimateOut{
  19. 0%{ background:rgba(0,0,0,0.35);}
  20. 100%{ background:rgba(0,0,0,0);}
  21. }
  22. .frameAnimate{ animation: frameAnimate 0.5s ease forwards;}
  23. @keyframes frameAnimate{
  24. 0%{ }
  25. 100%{ opacity: 1;top:0vh;}
  26. }
  27. .frameAnimateOut{ animation: frameAnimateOut 0.4s ease forwards;}
  28. @keyframes frameAnimateOut{
  29. 0%{ opacity: 1;top:0vh;}
  30. 100%{ opacity: 0;top:100vh;}
  31. }
  32. .frame-wrapper{ position: fixed;height:100vh;width:100vw;z-index: 2;top: 50vh;}
  33. .frame{ background: #fff; position: absolute;bottom: 0;width: 88.2vw;padding: 5.9vw 5.9vw 0;border-top-left-radius: 20rpx;border-top-right-radius: 20rpx;z-index: 3;}
  34. .title-wrapper { justify-content: space-between; font-size: 4.9vw; color: #4a4a4a; margin-bottom: 5.9vw; height: 70vh; }
  35. .title-wrapper>image{ width:3.2vw;height:3.2vw;padding:0 5vw;margin-right:-5vw;}
  36. .flex{ display: flex;align-items: center;}
  37. .wrap{ position: fixed;z-index: 1;top: 0;left: 0;right: 0;bottom: 0;}
  38. ::-webkit-scrollbar { width: 0; height: 0; color: transparent; }
  39. .plitem{ overflow: hidden; margin:45rpx 0; }
  40. .plitem_l{ width: 12%; float: left; }
  41. .plitem_l image{ width: 100%; height: 78rpx; border-radius:50%; margin-top:10rpx; }
  42. .plitem_r{ width: 84%; float: right; }
  43. .username{ font-size: 28rpx; color: #666; margin-bottom:18rpx; }
  44. .content{ font-size: 28rpx; color: #333; }

js

  1. Page({
  2. data: {
  3. vlists:'',
  4. vertical: true,
  5. autoplay: false,
  6. controls:false,
  7. circular: false,
  8. interval: 2000,
  9. duration: 1000,
  10. previousMargin: 0,
  11. nextMargin: 0 ,
  12. num:1,
  13. iid:'',
  14. url:'',
  15. current:0,
  16. playIndex: null,
  17. flag: false,
  18. wrapAnimate: 'wrapAnimate',
  19. bgOpacity: 0,
  20. frameAnimate: 'frameAnimate',
  21. plist:'',
  22. con:1
  23. },
  24. // 点击
  25. cli:function () {
  26. var that = this
  27. wx.request({
  28. url: '/mv/url?id=' + that.data.vlists[0].id, //这里的url我隐藏了。谢谢理解
  29. header: {
  30. 'content-type': 'application/json' // 默认值
  31. },
  32. success(res) {
  33. that.setData({
  34. url: res.data.data.url
  35. })
  36. that.setData({
  37. playIndex: 0
  38. })
  39. }
  40. })
  41. },
  42. // 滚动到底部
  43. down1:function (e) {
  44. var that = this
  45. console.log(e.currentTarget.id)
  46. var i = e.currentTarget.id;
  47. var b = i.substr(1, 1)
  48. that.data.con+=20
  49. console.log(that.data.con)
  50. wx.request({
  51. url: '/comment/mv?id=' + that.data.vlists[b].id + '&limit=' + that.data.con,//这里的url我隐藏了。谢谢理解
  52. header: {
  53. 'content-type': 'application/json' // 默认值
  54. },
  55. success(res) {
  56. console.log(res.data.comments)
  57. that.setData({
  58. plist: res.data.comments
  59. })
  60. }
  61. })
  62. },
  63. // 弹窗
  64. showFrame(e) {
  65. const that = this;
  66. console.log(e.currentTarget.id)
  67. var i = e.currentTarget.id;
  68. var b=i.substr(1,1)
  69. console.log(b)
  70. wx.request({
  71. url: '/comment/mv?id=' + that.data.vlists[b].id + '&offset=' + that.data.con,//这里的url我隐藏了。谢谢理解
  72. header: {
  73. 'content-type': 'application/json' // 默认值
  74. },
  75. success(res) {
  76. console.log(res.data.comments)
  77. that.setData({
  78. plist: res.data.comments
  79. })
  80. }
  81. })
  82. this.setData({
  83. flag: true,
  84. wrapAnimate: 'wrapAnimate',
  85. frameAnimate: 'frameAnimate'
  86. });
  87. },
  88. hideFrame() {
  89. const that = this;
  90. that.setData({
  91. wrapAnimate: 'wrapAnimateOut',
  92. frameAnimate: 'frameAnimateOut'
  93. });
  94. setTimeout(() => {
  95. that.setData({
  96. flag: false
  97. })
  98. }, 400)
  99. },
  100. catchNone() {
  101. //阻止冒泡
  102. },
  103. _showEvent() {
  104. this.triggerEvent("showEvent");
  105. },
  106. _hideEvent() {
  107. this.triggerEvent("hideEvent");
  108. },
  109. // 滑动事件:
  110. onSlideChangeEnd: function (e) {
  111. var that = this
  112. console.log('本页:'+e.detail.current)
  113. wx.request({
  114. url: '/mv/url?id=' + that.data.vlists[e.detail.current].id, //这里的url我隐藏了。谢谢理解
  115. header: {
  116. 'content-type': 'application/json' // 默认值
  117. },
  118. success(res) {
  119. that.setData({
  120. playIndex: e.detail.current
  121. })
  122. that.setData({
  123. url: res.data.data.url
  124. })
  125. }
  126. })
  127. },
  128. // 触摸开始事件
  129. touchStart: function (e) {
  130. var that = this
  131. that.data.num++
  132. wx.request({
  133. url: '/top/mv?limit=' + that.data.num, //这里的url我隐藏了。谢谢理解
  134. header: {
  135. 'content-type': 'application/json' // 默认值
  136. },
  137. success(res) {
  138. console.log(res.data.data)
  139. that.setData({
  140. vlists: res.data.data
  141. })
  142. }
  143. })
  144. },
  145. // 加载
  146. onLoad:function () {
  147. var that = this
  148. wx.request({
  149. url: '/top/mv?limit=1', //这里的url我隐藏了。谢谢理解
  150. header: {
  151. 'content-type': 'application/json' // 默认值
  152. },
  153. success(res) {
  154. console.log(res.data.data)
  155. that.setData({
  156. vlists: res.data.data
  157. })
  158. }
  159. })
  160. }
  161. })

如果有疑问请在下面评论吧,我会及时回复你的。

发表评论

表情:
评论列表 (有 0 条评论,384人围观)

还没有评论,来说两句吧...

相关阅读

    相关 vue+swiper仿视频滑动

    解释一番,本文章只是做了一个仿抖音的滑动效果,并没有添加其他效果,例如头部的类似关注与推荐功能、广告、右侧按钮等等,这些功能可按项目需求添加,在这里我就不多bb了。 先看下