关于个人项目(臻美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
        })
      }
      })
  }
})



相关文章
|
28天前
|
JavaScript 前端开发 Docker
前端全栈之路Deno篇(二):几行代码打包后接近100M?别慌,带你掌握Deno2.0的安装到项目构建全流程、剖析构建物并了解其好处
在使用 Deno 构建项目时,生成的可执行文件体积较大,通常接近 100 MB,而 Node.js 构建的项目体积则要小得多。这是由于 Deno 包含了完整的 V8 引擎和运行时,使其能够在目标设备上独立运行,无需额外安装依赖。尽管体积较大,但 Deno 提供了更好的安全性和部署便利性。通过裁剪功能、使用压缩工具等方法,可以优化可执行文件的体积。
110 3
前端全栈之路Deno篇(二):几行代码打包后接近100M?别慌,带你掌握Deno2.0的安装到项目构建全流程、剖析构建物并了解其好处
|
12天前
|
开发框架 监控 .NET
【Azure App Service】部署在App Service上的.NET应用内存消耗不能超过2GB的情况分析
x64 dotnet runtime is not installed on the app service by default. Since we had the app service running in x64, it was proxying the request to a 32 bit dotnet process which was throwing an OutOfMemoryException with requests >100MB. It worked on the IaaS servers because we had the x64 runtime install
|
17天前
|
前端开发 Unix 测试技术
揭秘!前端大牛们如何高效管理项目,确保按时交付高质量作品!
【10月更文挑战第30天】前端开发项目涉及从需求分析到最终交付的多个环节。本文解答了如何制定合理项目计划、提高团队协作效率、确保代码质量和应对项目风险等问题,帮助你学习前端大牛们的项目管理技巧,确保按时交付高质量的作品。
30 2
|
20天前
|
JavaScript 前端开发 开发者
前端框架对比:Vue.js与Angular的优劣分析与选择建议
【10月更文挑战第27天】在前端开发领域,Vue.js和Angular是两个备受瞩目的框架。本文对比了两者的优劣,Vue.js以轻量级和易上手著称,适合快速开发小型到中型项目;Angular则由Google支持,功能全面,适合大型企业级应用。选择时需考虑项目需求、团队熟悉度和长期维护等因素。
28 1
|
21天前
|
JavaScript 前端开发 API
前端框架对比:Vue.js与Angular的优劣分析与选择建议
【10月更文挑战第26天】前端技术的飞速发展让开发者在构建用户界面时有了更多选择。本文对比了Vue.js和Angular两大框架,介绍了它们的特点和优劣,并给出了在实际项目中如何选择的建议。Vue.js轻量级、易上手,适合小型项目;Angular结构化、功能强大,适合大型项目。
17 1
|
27天前
|
JavaScript 前端开发 算法
前端优化之超大数组更新:深入分析Vue/React/Svelte的更新渲染策略
本文对比了 Vue、React 和 Svelte 在数组渲染方面的实现方式和优缺点,探讨了它们与直接操作 DOM 的差异及 Web Components 的实现方式。Vue 通过响应式系统自动管理数据变化,React 利用虚拟 DOM 和 `diffing` 算法优化更新,Svelte 通过编译时优化提升性能。文章还介绍了数组更新的优化策略,如使用 `key`、分片渲染、虚拟滚动等,帮助开发者在处理大型数组时提升性能。总结指出,选择合适的框架应根据项目复杂度和性能需求来决定。
|
1月前
|
移动开发 小程序 数据可视化
基于npm CLI脚手架的uniapp项目创建、运行与打包全攻略(微信小程序、H5、APP全覆盖)
基于npm CLI脚手架的uniapp项目创建、运行与打包全攻略(微信小程序、H5、APP全覆盖)
226 3
|
29天前
|
缓存 前端开发 JavaScript
前端架构思考:代码复用带来的隐形耦合,可能让大模型造轮子是更好的选择-从 CDN 依赖包被删导致个站打不开到数年前因11 行代码导致上千项目崩溃谈谈npm黑洞 - 统计下你的项目有多少个依赖吧!
最近,我的个人网站因免费CDN上的Vue.js包路径变更导致无法访问,引发了我对前端依赖管理的深刻反思。文章探讨了NPM依赖陷阱、开源库所有权与维护压力、NPM生态问题,并提出减少不必要的依赖、重视模块设计等建议,以提升前端项目的稳定性和可控性。通过“left_pad”事件及个人经历,强调了依赖管理的重要性和让大模型代替人造轮子的潜在收益
|
1月前
|
前端开发 JavaScript 开发工具
前端代码规范和质量是确保项目可维护性、可读性和可扩展性的关键(三)
前端代码规范和质量是确保项目可维护性、可读性和可扩展性的关键(三)
36 0
|
1月前
|
Web App开发 前端开发 JavaScript
前端代码规范和质量是确保项目可维护性、可读性和可扩展性的关键(二)
前端代码规范和质量是确保项目可维护性、可读性和可扩展性的关键(二)
49 0
下一篇
无影云桌面