手把手教你用前端实现短视频App(滑动切换)

2021-12-01 09:27:25 浏览数 (1)

前言

平常在玩短视频App时,喜欢看的视频可以多看一会,不喜欢看的视频直接往上一划,这个功能一直深受用户喜爱。今天我们不妨实现一个这样功能的App。

功能

  1. 上下滑动切换视频
  2. 可查看对应视频下的评论

示例

下面我挑出了几张代表性的图片,供大家参考。

下载链接

以上是我自己做的一款App的示例图,如果感兴趣的小伙伴,大家可以下载到手机上体验。目前只是开发了安卓版本。

代码语言:javascript复制
链接: https://pan.baidu.com/s/1dbgx9eht54qh-Ig04w2JAg 
提取码: 96ta 
复制这段内容后打开百度网盘手机App,操作更方便哦

核心代码

代码语言:javascript复制
<template>
  <div class="home">
    <van-swipe
      style="height: 100vh"
      vertical
      @change="onChange"
      :show-indicators="false"
    >
      <van-swipe-item>
        <div class="main" v-if="isshow">
          <video-player
            v-if="playerOptions.sources[0].src"
            class="video-player vjs-custom-skin"
            ref="videoPlayer"
            :playsinline="true"
            :options="playerOptions"
          ></video-player>
          <div class="footbox">
            <div class="foot">
              <p class="user">@ {{ artistName }}</p>
              <p class="name">{{ name }}</p>
            </div>
            <div class="pl" @click="openPl">
              <van-icon name="chat" size="30" />
            </div>
          </div>
        </div>
      </van-swipe-item>
      <van-swipe-item>
        <div class="main" v-if="!isshow">
          <video-player
            v-if="playerOptions.sources[0].src"
            class="video-player vjs-custom-skin"
            ref="videoPlayer"
            :playsinline="true"
            :options="playerOptions"
          ></video-player>
          <div class="footbox">
            <div class="foot">
              <p class="user">@ {{ artistName }}</p>
              <p class="name">{{ name }}</p>
            </div>
            <div class="pl" @click="openPl">
              <van-icon name="chat" size="30" />
            </div>
          </div>
        </div>
      </van-swipe-item>
    </van-swipe>
    <van-action-sheet v-model="show" class="sheet">
      <van-list
        v-model="loading"
        @load="onLoad"
        :offset="1"
        :immediate-check="false"
      >
        <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>
      </van-list>
    </van-action-sheet>
  </div>
</template>

<script>
import { list, mv, pl } from "@request/api";
import "../video/index";
import { videoPlayer } from "vue-video-player";
export default {
  name: "home",
  data() {
    return {
      list: "",
      id: "",
      show: false,
      dataLength: 1,
      inx: 0,
      artistName: "",
      name: "",
      isshow: true,
      plist: "",
      loading: false,
      page: 10,
      playerOptions: {
        autoplay: true, //如果true,浏览器准备好时开始回放。
        muted: false, // 默认情况下将会消除任何音频。
        loop: false, // 导致视频一结束就重新开始。
        preload: "auto", // 建议浏览器在<video>加载元素后是否应该开始下载视频数据。auto浏览器选择最佳行为,立即开始加载视频(如果浏览器支持)
        language: "zh-CN",
        poster:"",
        aspectRatio: "16:9", // 将播放器置于流畅模式,并在计算播放器的动态大小时使用该值。值应该代表一个比例 - 用冒号分隔的两个数字(例如"16:9"或"4:3")
        fluid: true, // 当true时,Video.js player将拥有流体大小。换句话说,它将按比例缩放以适应其容器。
        sources: [{ type: "video/mp4", src: "" }],
        width: document.documentElement.clientWidth,
        notSupportedMessage: "此视频暂无法播放,请稍后再试", //允许覆盖Video.js无法播放媒体源时显示的默认信息。
        controlBar: {
          timeDivider: true, // 分时
          durationDisplay: true, // 持续时间显示
          remainingTimeDisplay: false, // 剩余时间显示
          fullscreenToggle: false, //全屏按钮
        },
      },
    };
  },
  components: {
    videoPlayer,
  },
  mounted() {
    this.wait();
  },
  methods: {
    // 首次获取url
    async wait() {
      let id = await this.get();
      let res = await mv(id);
      // this.playerOptions.poster = this.poster;
      this.$set(this.playerOptions, "poster", this.poster);
      this.$set(this.playerOptions.sources[0], "src", res.data.url);
    },
    // 加载评论
    onLoad() {
      setTimeout(() => {
        this.page  = 10;
        this.getpl();
        this.loading = false;
      }, 500);
    },
    // 获取数据
    get() {
      return list(this.dataLength).then((res) => {
        console.log(res.data);
        this.urlData = res.data;
        this.poster = this.urlData[this.inx].cover;
        this.id = this.urlData[this.inx].id;
        this.artistName = this.urlData[this.inx].artistName;
        this.name = this.urlData[this.inx].name;
        // return this.id;
        return Promise.resolve(this.id);
        // 等价于
        // return new Promise((resolve) => {
        //   resolve(this.id)
        // })
      });
    },
    // 封装静态数据
    getStatic(v) {
      this.id = v[this.inx].id;
      this.artistName = v[this.inx].artistName;
      this.name = v[this.inx].name;
      this.poster = v[this.inx].cover;
      return this.id;
    },
    // 获取评论
    getpl() {
      pl(this.id, this.page).then((res) => {
        if (document.querySelector(".van-action-sheet__content")) {
          document.querySelector(".van-action-sheet__content").scrollTop = 0;
        }
        this.plist = res.comments;
      });
    },
    // 滑动触发
    async onChange() {
      console.log(this.inx);
      this.isshow = !this.isshow;
      this.page = 10;
      this.getpl();
      if (this.inx < this.urlData.length - 1) {
        this.inx  = 1;
        let id = this.getStatic(this.urlData);
        let res = await mv(id);
        this.$set(this.playerOptions.sources[0], "src", res.data.url);
        this.$set(this.playerOptions, "poster", this.poster);
        // this.playerOptions.poster = this.poster;
      } else {
        this.inx = 0;
        this.dataLength  = 1;
        this.wait();
      }
    },
    // 打开评论列表
    openPl() {
      this.show = true;
      this.getpl();
    },
  },
};
</script>
<style lang="less" scoped>
.main {
  height: 100%;
}
.footbox {
  position: relative;
  bottom: 0;
  width: 95%;
  margin: 0 auto;
  display: flex;
  justify-content: space-between;
  align-items: center;
}
.user {
  font-size: 14px;
}
.user,
.name {
  text-overflow: ellipsis;
  overflow: hidden;
  white-space: nowrap;
}
.foot {
  width: 80%;
  color: #fff;
  font-size: 16px;
  z-index: 10001;
}
.pl {
  width: 10%;
  color: #fff;
  font-size: 16px;
  z-index: 10001;
}
.sheet {
  height: 50vh;
  .pll {
    overflow: hidden;
    padding: 10px;
    border-bottom: 1px solid #f4f4f4;
    .pl-l {
      width: 20%;
      float: left;
    }
    .pl-l img {
      width: 55%;
      border-radius: 50%;
      animation: all ds 0.5s;
    }
    @keyframes ds {
      from {
        opacity: 0;
      }
      to {
        opacity: 1;
      }
    }
    .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: 14px;
    }
  }
}
</style>

完整代码库

恭请各位大佬指正。

代码语言:javascript复制
https://github.com/maomincoding/zm_mv2

0 人点赞