微信小程序抖音实战-支持播放小视频

我是小探花 2018-06-26 18:10:18 ⋅ 874 阅读

之前的案例只支持图片的播放,经过粉丝的反馈说视频弄不出来,本节内容就教大家怎么做

首先看下效果图

页面布局

主要思路就是使用小程序提供的cover-view 和 cover-image 标签来做,在视频上面做绝对定位

这边有个坑大家要注意: 视频属性的最好都用 {{}} 扩起来,只是写false 或者 true 是不起效果的

<view class="page-body" animation="{{animationData}}">
  <video id='myVideo' src='{{subject.coverUrl}}' controls="{{show}}" show-center-play-btn='{{show}}' show-play-btn='{{show}}' class='canvas' show-progress='{{show}}' loop='{{true}}' objectFit='cover' autoplay='{{autoplay}}' enable-progress-gesture='{{show}}' bindtouchstart='touchstart'
    bindtouchmove='touchmove' bindtouchend='touchend' bindtouchcancel='touchcancel' bindtap='play' bindtimeupdate='timeupdate' bindplay='bindPlay' bindpause='bindPause' bindended='bindEnded'>


    <!-- 右侧区域  -->
    <cover-view class="tools">
      <cover-view>
        <cover-image class="userinfo-avatar" src="{{subject.avatarUrl}}"></cover-image>
      </cover-view>
      <cover-view>
        <cover-image wx:if="{{subject.like}}" class="icon right-icon" src='/image/like_red.png' bindtap='like'>喜欢</cover-image>
        <cover-image wx:if="{{!subject.like}}" class="icon right-icon" src='/image/like.png' bindtap='like'>喜欢</cover-image>
        <cover-view class="text" bindtap='like'>{{subject.likes}}</cover-view>
      </cover-view>
      <cover-view>
        <cover-image class="icon right-icon" src='/image/talk.png' bindtap='talk'>评论</cover-image>
        <cover-view class="text" bindtap='apply'>{{subject.talks}}</cover-view>
      </cover-view>
      <cover-view>
        <cover-image class="icon right-icon" src='/image/share.png' bindtap='share'>分享</cover-image>
        <cover-view class="text">{{subject.shares}}</cover-view>
      </cover-view>
    </cover-view>

    <!-- 暂停播放按钮  -->
    <cover-view class='icon-box' bindtap='play'>
      <cover-image class="icon-play" src='/image/play.png' hidden='{{isPlay}}'></cover-image>
    </cover-view>

    <!-- 进度条  -->
    <cover-view class="jindu-back"></cover-view>
    <cover-view class="jindu" style='width: {{percent}}%'></cover-view>

    <!-- 描述  -->
    <cover-view class="desc">
      <cover-view>
        <cover-view class="desc-text">{{subject.title}}</cover-view>
      </cover-view>
    </cover-view>

    <!-- 底部 -->
    <cover-view class="footer">
      <cover-view class='apply'>
        <cover-image class="icon" src='/image/add.png' bindtap='apply'></cover-image>
      </cover-view>
    </cover-view>

  </video>
</view>

样式布局

思路很简单,把视频铺满全屏后,把内部的元素都定位好就可以了

page {
  width100%;
  height100%;
}

.page-body {
  width100%;
  height100%;
}

video {
  width100%;
  height100%;
  margin0;
  padding0;
  border0;
  display: block;
}

/* 播放按钮  */

.icon-box {
  position: relative;
  margin0 auto;
  width200rpx;
  height100%;
}

.icon-play {
  position: absolute;
  display: block;
  top50%;
  transformtranslate(0, -50%);
  width200rpx;
  height200rpx;
}

/* 进度条  */

.jindu {
  position: absolute;
  bottom110rpx;
  height5rpx;
  background-color: white;
}

.jindu-back {
  position: absolute;
  bottom110rpx;
  width100%;
  height5rpx;
  background-color: gray;
}

/* 刷新按钮 */

.reload {
  position: absolute;
  left5rpx;
  top10rpx;
  height100px;
}

/* 右侧区域  */
.tools {
  display: flex;
  position: absolute;
  right5rpx;
  top250rpx;
  height300px;
  flex-direction: column;
}

/* 头像  */
.userinfo-avatar {
  width100rpx;
  height100rpx;
  margin20rpx;
  border-radius50%;
}

.icon {
  width100rpx;
  height100rpx;
}

.right-icon {
  margin20rpx 20rpx 0rpx;
}

.text {
  color: white;
  width100rpx;
  text-align: center;
  margin0rpx 20rpx;
}

.desc {
  position: absolute;
  left20rpx;
  bottom158rpx;
  width600rpx;
}

.desc-text {
  color: white;
  white-space: pre-line;
}
/* 底部区域  */
.footer {
  position: absolute;
  bottom10rpx;
  width100%;
}

.apply {
  position: relative;
  margin0 auto;
  width100rpx;
  color#ff2c37;
}

/* 评论区域  */

.talks-layer {
  position: absolute;
  bottom: -100%;
  height0;
  width100%;
  overflow: hidden;
  /* background-color: blue; */
}

.layer-white-space {
  height100%;
  width100%;
  /* background-color: green; */
}

.talks {
  position: absolute;
  height900rpx;
  width100%;
  bottom0rpx;
  background-color#2f2d2e;
  border-top-left-radius5%;
  border-top-right-radius5%;
  /* background-color: red; */
}

.talk-header {
  width100%;
  height70rpx;
  padding-top30rpx;
  text-align: center;
}

.talk-body {
  height700rpx;
}

.talk-footer {
  position: absolute;
  bottom0rpx;
  width100%;
  height100rpx;
  background-color#151515;
}

/* 顶部元素  */

.talk-count {
  font-size0.8rem;
  height40rpx;
  color#6b696a;
}

.talk-close {
  position: absolute;
  top40rpx;
  right40rpx;
  width40rpx;
  height40rpx;
}

/* 中部元素  */

.talk-item {
  display: flex;
  flex-direction: row;
  align-items: flex-start;
  width100%;
  color: white;
}

.talk-item-left {
  display: flex;
  flex-direction: row;
  margin20rpx 30rpx;
}

.talk-item-face {
  width80rpx;
  height80rpx;
  border-radius50%;
}

.talk-item-right {
  width100%;
  border-bottom: solid 1rpx #6a6869;
  margin-right30rpx;
  margin-bottom30rpx;
}

.talk-item-nickname {
  font-size0.7rem;
  color#aaa8a9;
  margin-top20rpx;
  margin-bottom10rpx;
}

.talk-item-content {
  display: block;
  margin-right30rpx;
  width100%;
  white-space: pre-line;
}

.talk-item-time {
  font-size0.7rem;
  color#6a6869;
  margin-bottom20rpx;
}

/* 底部元素  */

.talk-input {
  width100%;
  padding20rpx 40rpx;
  color: white;
  border-top-left-radius5%;
  border-top-right-radius5%;
}

完整 js 代码

const app = getApp()
const api = require('../../utils/api.js')

var start;
Page({

  /**
   * 页面的初始数据
   */

  data: {
    showfalse,
    autoplaytrue,
    videoContextnull,
    percent0,
    isPlaytrue,
    count0,
    pages0,
    page1,
    subject: {},
    current0,
    subjectList: [],
    userInfo: {},
    hasUserInfofalse,
    inputTalk'',
    talks: [],
    talksPage1,
    talksPages-1,
    canIUse: wx.canIUse('button.open-type.getUserInfo'),
    talksAnimationData: {}
  },

  changeSubjectfunction (current{
    current = current || 0;
    var list = this.data.subjectList;
    if (list.length <= current) {
      return;
    }
    this.setData({
      current: current,
      subject: list[current]
    })
    // 自动加载
    var diff = list.length - current;
    if (diff <= 5) {
      this.loadData(this.data.page + 1);
    }
  },
  likefunction (e{
    // 验证用户信息
    if (!this.checkUserInfo()) return;
    var subject = this.data.subject;
    subject.like = true;
    subject.likes = subject.likes + 1;
    this.setData({
      subject: subject
    })
    api.like({
      subjectId: subject.subjectId
    })
  },
  applyfunction (e{
    wx.showToast({
      icon'none',
      title"暂时还不能发布视频呦",
    })
  },
  checkUserInfofunction ({
    if (!app.globalData.userInfo) {
      wx.showModal({
        content'请先授权访问你的基本信息',
        successfunction (res{
          if (res.confirm) {
            wx.navigateTo({
              url'getuserinfo',
            })
          }
        }
      })
      return false;
    }
    return true;
  },
  talkfunction (e{
    wx.showToast({
      icon'none',
      title"敬请期待",
    })
  },
  sharefunction (e{
    wx.showToast({
      icon'none',
      title"敬请期待",
    })
  },

  loadDatafunction (page, success{
    var that = this;
    this.setData({
      page: page
    })
    api.getRecommendList({
      data: {
        page: page,
        rows5,
        type'video'
      },
      successfunction (res{
        var list = res.content;
        if (list){
          var listData = [];
          for (var i = 0; i < that.data.subjectList.length; i++) {
            listData.push(that.data.subjectList[i])
          }
          for (var i = 0; i < list.length; i++) {
            listData.push(list[i])
          }
          that.setData({
            count: res.count,
            page: page,
            pages: res.pages,
            subjectList: listData
          })
          if (success) {
            success();
          }
        }
      }
    })
  },

  /**
   * 生命周期函数--监听页面加载
   */

  onLoad: function (options{
    var that = this;
    // 登录回调
    if (app.globalData.isLogin) {
      that.loadData(1, that.changeSubject)
    } else {
      app.onLogin = function ({
        that.loadData(1, that.changeSubject)
      }
    }

    // 获取用户信息
    if (app.globalData.userInfo) {
      this.setData({
        userInfo: app.globalData.userInfo,
        hasUserInfotrue
      })
    } else if (this.data.canIUse) {
      // 由于 getUserInfo 是网络请求,可能会在 Page.onLoad 之后才返回
      // 所以此处加入 callback 以防止这种情况
      app.onUserInfo = userInfo => {
        that.setData({
          userInfo: userInfo,
          hasUserInfotrue
        })
      }
    }
  },

  /**
   * 生命周期函数--监听页面初次渲染完成
   */

  onReady: function ({
    // 初始化播放器上下文
    this.videoContext = wx.createVideoContext('myVideo')

    wx.setNavigationBarTitle({
      title"微抖音",
    })
  },

// 视频播放时间更新
  timeupdate: function (e{
    var val = e.detail.currentTime;
    var max = e.detail.duration;
    var percent = Math.round(val / max * 10000) / 100;
    this.setData({ percent: percent })
  },

// 点击播放按钮
  play: function ({
    if (this.data.isPlay) {
      this.videoContext.pause();
    } else {
      this.videoContext.play()
    }
  },

// 开始播放
  bindPlay: function ({
    this.setData({ isPlaytrue })
  },

// 暂停播放
  bindPause: function ({
    this.setData({ isPlayfalse })
  },

// 播放完毕
  bindEnded: function ({
    this.setData({ isPlayfalse })
  },

// 播放上一个抖音
  pre: function ({
    this.changeSubject(this.data.current - 1);
  },

// 播放下一个抖音
  next: function ({
    this.changeSubject(this.data.current + 1);
  },

// 下面主要模仿滑动事件
  touchstart: function (e{
    start = e.changedTouches[0];
    // console.log("touchstart ", e.changedTouches[0])
  },

  touchmovefunction (e{
    // console.log("touchmove ", e.changedTouches[0])
  },

  touchendfunction (e{
    // console.log("touchend ", e.changedTouches[0])
    this.getDirect(start, e.changedTouches[0]);
  },

  touchcancelfunction (e{
    // console.log("touchcancel ", e.changedTouches[0])
    this.getDirect(start, e.changedTouches[0]);
  },

  // 计算滑动方向
  getDirect(start, end) {
    var X = end.pageX - start.pageX,
      Y = end.pageY - start.pageY;
    if (Math.abs(X) > Math.abs(Y) && X > 0) {
      console.log("left 2 right");
    }
    else if (Math.abs(X) > Math.abs(Y) && X < 0) {
      console.log("right 2 left");
    }
    else if (Math.abs(Y) > Math.abs(X) && Y > 0) {
      console.log("top 2 bottom");
      this.pre()
    }
    else if (Math.abs(Y) > Math.abs(X) && Y < 0) {
      console.log("bottom 2 top");
      this.next()
    }
  }
})

更多内容请到官网https://100boot.cn的这里下载

更多精彩内容

微信小程序电商实战-入门篇
微信小程序电商实战-首页(上)
微信小程序电商实战-首页(下)
微信小程序电商实战-商品详情(上)
微信小程序电商实战-商品详情加入购物车(下)
微信小程序电商实战-商品列表流式布局
微信小程序实战篇:基于wxcharts.js绘制移动报表
微信小程序实战篇:实现抖音评论效果
微信小程序抖音实战-首页(上)
微信小程序实战篇:抖音案例使用手册
微信小程序抖音实战-首页(下)
微信小程序营销之大转盘(二)

关注我们

IT实战联盟是集产品、UI设计、前后端、架构、大数据和AI人工智能等为一体的实战交流服务平台!联盟嘉宾都为各互联网公司项目的核心成员,联盟主旨是“让实战更简单”,欢迎来撩~~~



全部评论: 0

    我有话说:

    程序实战-首页(上)

    你也可以用程序编写一个

    程序实战-视频弹幕

    如果你去只是为了看视频就少了一大乐趣,评论区才是最有趣的地方,边看视频边看评论的弹幕是不是更有意思

    程序实战-首页(下)

    程序首页动态数据获取

    程序实战篇:实现评论效果

    我们在写程序的时候经常会遇到弹出层的效果而现有官网提供的跳转方法多数是不支持参数传递的。本文教大家做一个评论效果的程序......

    程序电商实战-首页(下)

    上一篇:程序电商实战-首页(上)好了,上一期我们把首页搜索、导航栏和广告轮播给做完了,那么接下来会继续

    程序电商实战-首页(上)

    上一篇:程序电商实战-入门篇 嗨,大家好!经过近两周的精心准备终于开始程序电商实战之路喽。那么最终会做成什么样呢?好了,不啰嗦了 我们先看首页长什么样吧!   首页效果图

    程序实现商品数量加减

    这是一个用程序原生代码实现的数量加减demo,主要是用于商品购物车或者商品详情修改数量使用

    程序实战篇:基于wxcharts.js绘制移动报表

    程序图表插件(wx-charts)是基于canvas绘制,体积巧,支持图表类型饼图、线图、柱状图 ......

    程序实战篇:商品属性联动选择(案例)

    本期的程序实战篇来做一个电商网站经常用到的-商品属性联动选择的效果,素材参考了一点点

    程序电商实战-商品详情(上)

    先看一下今天要实现程序商品详情页吧!

    程序商城(七):动态API实现商品分类

    程序商品分类页面布局并且调用动态API获取数据并加载

    程序实战篇:如何解决https域名问题 ?

    开发自己的程序绕不开https问题,为了能在程序中调用我们自己的API服务请打开看一看吧!!!

    程序电商实战-购物车(上)

    好久没更新程序电商实战的文章了,是因为最近一直做整体架构调整,一些准备工作也是比较耗费时间的。在这几天将会有新版的 程序电商教程推出.......

    程序电商实战-商品列表流式布局

    今天给大家分享一下程序中商品列表的流式布局方式,根据文章内容操作就可以看到效果哦~~~

    程序电商实战-入门篇

    程序开发工具有新版本更新啦!开发体验更好了,接下来一起为电商程序做一下准备前期准备工作~~

    程序实战篇:程序之页面数据传递

    我们在写程序的时候经常会遇到子页面向主页面回传数据或者普通页面跳转到tabBar 页面携带数......

    程序商城(九):授权并实现个人中心页面页面

    实现商城的授权并获取用户信息和个人中心页面布局