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

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

Vue:


初始界面


<template>
  <div class="box jz">
    <div>
      <img src="../assets/MV.png" alt="">
    </div>
    <mu-button fab  color="primary" @click="go" class="go" >
      <mu-icon value="arrow_right_alt" color="white" size="34"></mu-icon>
    </mu-button>
  </div>
</template>
<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>
<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界面


<template>
  <div class="box">
    <v-carousel :cycle="false" :show-arrows="false" hide-delimiters   @change="f(index1)" v-model="index1" class="view" height="100vh">
      <v-carousel-item v-for="(item,index) in list" :key="index" class="item" @touchstart="cl">
        <div class="inner">
          <video :src="url"  autoplay="autoplay"  v-if="playIndex==index"></video>
          <div class="foot">
            <div class="in">
              <p class="user">@ {{item.artistName}}</p>
              <p class="name">{{item.name}}</p>
            </div>
            <div class="pl">
              <mu-icon value="speaker_notes" @click="openBotttomSheet" color="white" size="32"></mu-icon>
            </div>
          </div>
        </div>
      </v-carousel-item>
    </v-carousel>
    <mu-bottom-sheet :open.sync="open" class="sheet">
      <mu-load-more @refresh="refresh" :refreshing="refreshing" :loading="loading" @load="load">
        <div v-for="(item,index) in plist" :key='index' class="ovf pll">
          <div class="pl-l"><img :src="item.user.avatarUrl" alt=""></div>
          <div class="pl-r">
            <div class="name1">{{item.user.nickname}}</div>
            <div class="con">{{item.content}}</div>
          </div>
        </div>
      </mu-load-more>
    </mu-bottom-sheet>
  </div>
</template>
<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>
<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


<!--pages/video/video.wxml-->
<view class="container">
  <view class="page-body">
    <view class="page-section page-section-spacing swiper">
      <swiper   current="{{current}}" bindchange='onSlideChangeEnd'
        autoplay="{{autoplay}}" circular="{{circular}}" vertical="{{vertical}}"
        interval="{{interval}}" duration="{{duration}}" previous-margin="{{previousMargin}}px" next-margin="{{nextMargin}}px"  >
        <block wx:for="{{vlists}}"   wx:key="index" wx:for-item="item">
          <swiper-item   bindtouchstart="touchStart">
              <view class="swiper-item "   >
                  <view><image src="https://www.maomin.club/data/pl.png" class="pl" bindtap='showFrame' id='i{{index}}'></image></view>
                  <video src="{{url}}"  wx:if='{{playIndex==index}}' autoplay="{{true}}"  controls='{{controls}}'  id="{{index}}"  ></video>
                  <view wx:if="{{index==0}}"  class="start" bindtap="cli">点我一下,精彩MV马上开始</view>
                  <view class="foot">
                            <view class="songer">@ {{item.artistName}}</view>
                            <view class="name">{{item.name}}</view>  
                  </view>
                    <view wx:if='{{flag}}'>
                    <view class='wrap {{wrapAnimate}}' style='background:rgba(0,0,0,{{bgOpacity}});'></view>
                    <view catchtap='hideFrame' class='frame-wrapper {{frameAnimate}}'>
                      <view catchtap='catchNone' class='frame' >
                           <scroll-view class="title-wrapper" scroll-y style="width: 100%"  bindscrolltolower='down1'  id='s{{index}}'>
                          <view   wx:for="{{plist}}"    wx:key="index" wx:for-item="item" class="plitem">
                            <view class="plitem_l">
                              <image src="{{item.user.avatarUrl}}"></image>
                            </view>
                            <view  class="plitem_r">
                              <view class="username">{{item.user.nickname}}</view>
                              <view class="content"> {{item.content}}</view>
                            </view>
                          </view>
                          </scroll-view>
                      </view>
                    </view>
                  </view>
             </view>
          </swiper-item>
        </block>
      </swiper>
    </view>
  </view>
</view>


wxss


/* pages/video/video.wxss */
page { height: 100%; font-size: 32rpx; background: #000; }
swiper{ height:100vh; }
.swiper-item{ display: block; height:100%; position: relative; align-items: center; justify-content: center; font-size: 36rpx; }
.cover{ width: 100%; height:100vh ; }
.name{ color: #fff; font-size: 34rpx; font-weight: bold; }
video{ width: 100%; height: 100vh; }
.start{ color: #fff; text-align: center; line-height: 65vh; }
.songer{ color: #fff; font-size: 26rpx; margin-bottom:25rpx; }
.pl{ width:10%; height:74rpx; float: right;position: absolute;right: 4%;bottom:13vh;z-index: 100; }
.foot{width: 70%; position: absolute; bottom: 10%; left: 5%; }
.wrapAnimate{animation: wrapAnimate 0.5s ease-in-out forwards}
@keyframes wrapAnimate{
  0%{}
  100%{background:rgba(0,0,0,0.35);}
}
.wrapAnimateOut{animation: wrapAnimateOut 0.4s ease-in-out forwards}
@keyframes wrapAnimateOut{
  0%{background:rgba(0,0,0,0.35);}
  100%{background:rgba(0,0,0,0);}
}
.frameAnimate{animation: frameAnimate 0.5s ease forwards;}
@keyframes frameAnimate{
  0%{}
  100%{opacity: 1;top:0vh;}
}
.frameAnimateOut{animation: frameAnimateOut 0.4s ease forwards;}
@keyframes frameAnimateOut{
  0%{opacity: 1;top:0vh;}
  100%{opacity: 0;top:100vh;}
}
.frame-wrapper{position: fixed;height:100vh;width:100vw;z-index: 2;top: 50vh;}
.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;}
.title-wrapper { justify-content: space-between; font-size: 4.9vw; color: #4a4a4a; margin-bottom: 5.9vw; height: 70vh; }
.title-wrapper>image{width:3.2vw;height:3.2vw;padding:0 5vw;margin-right:-5vw;}
.flex{display: flex;align-items: center;}
.wrap{position: fixed;z-index: 1;top: 0;left: 0;right: 0;bottom: 0;}
::-webkit-scrollbar { width: 0; height: 0; color: transparent; }
.plitem{ overflow: hidden; margin:45rpx 0; }
.plitem_l{ width: 12%; float: left; }
.plitem_l image{ width: 100%; height: 78rpx; border-radius:50%; margin-top:10rpx; }
.plitem_r{ width: 84%; float: right; }
.username{ font-size: 28rpx; color: #666; margin-bottom:18rpx; }
.content{ font-size: 28rpx; color: #333; }


js


Page({
  data: {
    vlists:'',
    vertical: true,
    autoplay: false,
    controls:false,
    circular: false,
    interval: 2000,
    duration: 1000,
    previousMargin: 0,
    nextMargin: 0 ,
    num:1,
    iid:'',
    url:'',
    current:0,
    playIndex: null,
    flag: false,
    wrapAnimate: 'wrapAnimate',
    bgOpacity: 0,
    frameAnimate: 'frameAnimate',
    plist:'',
    con:1
  },
  // 点击
  cli:function () {
    var that = this
              wx.request({
                url: '/mv/url?id=' + that.data.vlists[0].id, //这里的url我隐藏了。谢谢理解
                header: {
                  'content-type': 'application/json' // 默认值
                },
                success(res) {
                  that.setData({
                    url: res.data.data.url
                  })
                  that.setData({
                        playIndex: 0
                      })
                }
              })
  },
  // 滚动到底部
  down1:function (e) {
    var that = this
    console.log(e.currentTarget.id)
    var i = e.currentTarget.id;
    var b = i.substr(1, 1)
    that.data.con+=20
     console.log(that.data.con)
     wx.request({
       url: '/comment/mv?id=' + that.data.vlists[b].id + '&limit=' + that.data.con,//这里的url我隐藏了。谢谢理解
       header: {
         'content-type': 'application/json' // 默认值
       },
       success(res) {
         console.log(res.data.comments)
         that.setData({
           plist: res.data.comments
         })
       }
     })
  },
  // 弹窗
  showFrame(e) {
    const that = this;
    console.log(e.currentTarget.id)
    var i = e.currentTarget.id;
    var b=i.substr(1,1)
    console.log(b)
              wx.request({
                url: '/comment/mv?id=' + that.data.vlists[b].id + '&offset=' + that.data.con,//这里的url我隐藏了。谢谢理解
                header: {
                  'content-type': 'application/json' // 默认值
                },
                success(res) {
                  console.log(res.data.comments)
                  that.setData({
                    plist: res.data.comments
                  })
                }
              })
      this.setData({
        flag: true,
        wrapAnimate: 'wrapAnimate',
        frameAnimate: 'frameAnimate'
      });
    },
    hideFrame() {
      const that = this;
      that.setData({
        wrapAnimate: 'wrapAnimateOut',
        frameAnimate: 'frameAnimateOut'
      });
      setTimeout(() => {
        that.setData({
          flag: false
        })
      }, 400)
    },
    catchNone() {
      //阻止冒泡
    },
    _showEvent() {
      this.triggerEvent("showEvent");
    },
    _hideEvent() {
      this.triggerEvent("hideEvent");
    },
  // 滑动事件:
  onSlideChangeEnd: function (e) {
     var that = this
      console.log('本页:'+e.detail.current)
          wx.request({
            url: '/mv/url?id=' + that.data.vlists[e.detail.current].id, //这里的url我隐藏了。谢谢理解
            header: {
              'content-type': 'application/json' // 默认值
            },
            success(res) {
              that.setData({
                playIndex: e.detail.current
              })
              that.setData({
                  url: res.data.data.url
                })
            }
          })
  },
  // 触摸开始事件  
  touchStart: function (e) {
 var that = this
 that.data.num++
 wx.request({
   url: '/top/mv?limit=' + that.data.num, //这里的url我隐藏了。谢谢理解
   header: {
     'content-type': 'application/json' // 默认值
   },
   success(res) {
     console.log(res.data.data)
     that.setData({
       vlists: res.data.data
     })
   }
 })
    },
  // 加载
  onLoad:function () {
    var that = this
    wx.request({
      url: '/top/mv?limit=1', //这里的url我隐藏了。谢谢理解
      header: {
        'content-type': 'application/json' // 默认值
      },
      success(res) {
        console.log(res.data.data)
           that.setData({
           vlists: res.data.data
        })
      }
      })
  }
})



相关文章
|
16天前
|
XML Java 数据库
安卓项目:app注册/登录界面设计
本文介绍了如何设计一个Android应用的注册/登录界面,包括布局文件的创建、登录和注册逻辑的实现,以及运行效果的展示。
66 0
安卓项目:app注册/登录界面设计
|
5天前
|
移动开发 小程序 数据可视化
基于npm CLI脚手架的uniapp项目创建、运行与打包全攻略(微信小程序、H5、APP全覆盖)
基于npm CLI脚手架的uniapp项目创建、运行与打包全攻略(微信小程序、H5、APP全覆盖)
55 3
|
28天前
|
开发工具
uniapp, 短剧视频类App实现参考,支持滑动播放,仿抖音 仿陌陌 短视频 无限滑动播放 视频流
阿里云点播服务web播放器sdk,短剧视频类App实现参考。仿抖音 仿陌陌 短视频 无限滑动播放 视频流。无uniapp video 原生组件的层级、遮挡、覆盖问题,适合与不同功能视图组合使用,实现丰富的应用功能。
uniapp, 短剧视频类App实现参考,支持滑动播放,仿抖音 仿陌陌 短视频 无限滑动播放 视频流
|
13天前
|
前端开发 JavaScript 编译器
不走弯路,纯前端如何把图片导出成视频!
【10月更文挑战第3天】不走弯路,纯前端如何把图片导出成视频!
32 3
|
10天前
|
前端开发 JavaScript Java
导出excel的两个方式:前端vue+XLSX 导出excel,vue+后端POI 导出excel,并进行分析、比较
这篇文章介绍了使用前端Vue框架结合XLSX库和后端结合Apache POI库导出Excel文件的两种方法,并对比分析了它们的优缺点。
148 0
|
11天前
|
缓存 开发框架 移动开发
uni-app:下载使用uni&创建项目&和小程序链接&数据缓存&小程序打包 (一)
uni-app 是一个跨平台的开发框架,它允许开发者使用 Vue.js 来构建应用程序,并能够同时发布到多个平台,如微信小程序、支付宝小程序、H5、App(通过DCloud的打包服务)等。uni-app 的目标是通过统一的代码库,简化多平台开发过程,提高开发效率。 在这一部分中,我们将逐步介绍如何下载和使用uni-app、创建一个新的项目、如何将项目链接到小程序,以及实现数据缓存的基本方法。
|
2月前
|
小程序 前端开发 Java
SpringBoot+uniapp+uview打造H5+小程序+APP入门学习的聊天小项目
JavaDog Chat v1.0.0 是一款基于 SpringBoot、MybatisPlus 和 uniapp 的简易聊天软件,兼容 H5、小程序和 APP,提供丰富的注释和简洁代码,适合初学者。主要功能包括登录注册、消息发送、好友管理及群组交流。
86 0
SpringBoot+uniapp+uview打造H5+小程序+APP入门学习的聊天小项目
|
1月前
|
安全
【Azure App Service】App service无法使用的情况分析
App Service集成子网后,如果子网网段中的剩余IP地址非常少的情况下,会在App Service实例升级时( 先加入新实例,然后在移除老实例 )。新加入的实例不能被分配到正确的内网IP地址,无法成功的访问内网资源。 解决方法就是为App Service增加子网地址, 最少需要/26 子网网段地址。
|
2月前
【Azure Function App】本地运行的Function发布到Azure上无法运行的错误分析
【Azure Function App】本地运行的Function发布到Azure上无法运行的错误分析
|
2月前
|
前端开发 大数据 数据库
🔥大数据洪流下的决战:JSF 表格组件如何做到毫秒级响应?揭秘背后的性能魔法!💪
【8月更文挑战第31天】在 Web 应用中,表格组件常用于展示和操作数据,但在大数据量下性能会成瓶颈。本文介绍在 JavaServer Faces(JSF)中优化表格组件的方法,包括数据处理、分页及懒加载等技术。通过后端分页或懒加载按需加载数据,减少不必要的数据加载和优化数据库查询,并利用缓存机制减少数据库访问次数,从而提高表格组件的响应速度和整体性能。掌握这些最佳实践对开发高性能 JSF 应用至关重要。
58 0