微信小程序模仿抖音,全屏播放且有流畅的动画

效果图

有上翻的动画,有商品展示,有分享,以及跳到视频库。之前网上找了很多,说swiper不能套video,现在已经可以了,第二种是把封面平铺下来,滚动的是封面那种,效果不好

demo.gif

思路如下:

首先用一个cover-view来控制上下滚空,监听touch事件,让swiper的index+1或者-1
swiper包裹这video,swiper不能自动滚动,但是必须要设置衔接的属性。

代码如下:

<view class="video-contain">
  <!-- 自定义头部 -->
  <cover-view class="nav-myself" style="height:{{navigationBarHeight+'px'}}">
    <cover-view class="status-title" style="height:{{statusBarHeight+'px'}}"></cover-view>
    <cover-view class="title-content">
      <cover-view class="back" bindtap="goback" wx:if="{{showBack}}"><cover-image src="/image/shop/back.png"></cover-image></cover-view>
    </cover-view>
  </cover-view>
  <!-- 滑动遮罩 -->
  <cover-view class="touch-cover" bindtouchstart="touchStart" bindtouchend="touchEnd" bindtap='changeStatus'>
  </cover-view>
  <!-- 暂无数据 -->
  <view wx:if="{{!lists.length&&!isLoading}}" >
    <no-data type="common" txt="暂无数据"></no-data>
  </view>
   <swiper vertical="true" skip-hidden-item-layout="{{true}}"  loop="{{true}}" current="{{swiperCurrent}}" 
   circular="{{true}}">
      <swiper-item wx:for="{{lists}}" wx:key="{{index}}" wx:if="{{lists.length>0}}">
        <video src="{{item.product.img_video.video[0].url}}" objectFit="contain" controls="{{false}}"  loop="{{true}}" id="video{{item.id}}"  data-id="{{item.id}}" custom-cache="{{false}}"></video>
      </swiper-item> 
    </swiper>
    <!-- 右侧工具 -->
    <cover-view class="tools" wx:if="{{lists.length}}">
        <cover-image src="https://img.kemanyun.com/qianhuituan/2019-09-20/69_1568963723_UXAJkemkT3.png" class="home-icon right-icon" bindtap='goList'></cover-image>
      <button open-type="share" class="share-button">
          <cover-image class="share-icon right-icon" src="https://img.kemanyun.com/qianhuituan/2019-09-20/69_1568963641_Fkf2m79fFb.png"></cover-image>
      </button>
    </cover-view>
    <!-- 暂停按钮 -->
    <cover-image class="play-btn" wx:if="{{!playState}}" bindtap='changeStatus' src="https://img.kemanyun.com/qianhuituan/2019-10-11/69_1570790815_yUO3cyjQTB.png"></cover-image>
    <!-- 商品信息 -->
    <cover-view class="video-goods" bindtap='goDeatil' wx:if="{{lists.length>0}}">
      <cover-image src="{{lists[swiperCurrent].product.img_video.img[0]}}"></cover-image>
      <cover-view class="goods-right">
        <cover-view class="title">{{lists[swiperCurrent].product.title}}</cover-view>
        <cover-view class="goods-price">
          <cover-view class="price">
            <cover-view style="color:rgb({{themeColor.mainColor}});font-size:36rpx">¥{{lists[swiperCurrent].product.price}}
            </cover-view>
            <cover-view class="market-price" style='width:100%'>
            <cover-view class="market-box">
              ¥{{lists[swiperCurrent].product.market_price + ' '}}<cover-view class="hidden-box">一</cover-view>
              <cover-view class="line"></cover-view>
            </cover-view>
            </cover-view>
          </cover-view>
          <cover-view class="goBuy" style='background:rgb({{themeColor.mainColor}})'>{{video_buy_button_title}}</cover-view>
      </cover-view>
    </cover-view> 
  </cover-view>

      <!-- 轮播购买记录 -->
    <view class="buy-info" wx:if="{{video_goods_buy_data==1}}">
      <swiper indicator-dots="{{false}}" autoplay="true"  vertical circular style="height:270rpx">
        <block>
          <swiper-item wx:for="{{lists[swiperCurrent].trade_users}}" wx:key="img">
            <view class="item-li" wx:for="{{item}}" wx:for-item="oneItem" wx:for-index="idx" wx:key="idx">
              <image src="{{oneItem.headimgurl}}" ></image>
              <text>{{numberUtil.strLong(oneItem.name,3)}}{{video_buy_success_hint}}</text>
            </view>
          </swiper-item>
        </block>
      </swiper>
    </view> 

</view>
/* pages/Main/myVideo/index.wxss */
page{
  width: 100%;
  height: 100%;
}
.video-contain{
  width: 100%;
  height: 100%;
}
swiper{
  height: 100%;
  width: 100%;
}
swiper video{
  width: 100%;
  height: 100%;
}
.touch-cover{
  width: 100%;
  height: 100%;
  position: fixed;
  top: 0px;
  left: 0px;
  z-index: 9;
  display: flex;
  justify-content: center;
  align-items: center;
  flex-direction: column;
  background-image: url('https://img.kemanyun.com/qianhuituan/2019-09-20/69_1568967223_obtnfpoWy3.png');
  background-size:100% 100%;
}
.nav-myself{
  position: fixed;
  top: 0;
  width: 100%;
  display: flex;
  flex-direction: column;
  z-index: 999;
}
.title-content{
  display: flex;
  flex:1;
  align-items: center;
}
.title{
  display: inline-block;
  white-space:nowrap;
  overflow:hidden;
  text-overflow:ellipsis;
}
.back{
  position: absolute;
  left: 30rpx;
  width: 19rpx;
  height: 32rpx;
}
.back image{
  width: 100%;
  height: 100%;
}
.tools {
  position: fixed;
  right: 5rpx;
  top: 500rpx;
  display: flex;
  flex-direction: column;
  width: 125rpx;
  z-index: 9999;
}
.share-icon {
  width: 73rpx;
  height: 70rpx;
  /* background-image: url(https://img.kemanyun.com/qianhuituan/2019-09-20/69_1568963641_Fkf2m79fFb.png);
  background-size: 100% 100%; */
}
.right-icon {
  margin: 20rpx 20rpx 28rpx 0;
}
.home-icon{
  width: 75rpx;
  height: 75rpx;
  /* background-image: url(https://img.kemanyun.com/qianhuituan/2019-09-20/69_1568963723_UXAJkemkT3.png);
  background-size: 100% 100%; */
}
.like-icon{
  width: 74rpx;
  height: 64rpx;
}
.text {
  color: white;
  width: 100rpx;
  text-align: center;
  margin: 0rpx 20rpx;
}
.share-button{
  background-color: transparent !important;
  margin:20rpx 20rpx 28rpx 0;
  padding:0 !important;
}
.video-goods{
  width: 578rpx;
  height: 186rpx;
  border-radius: 8rpx;
  background: #fff;
  position: fixed;
  bottom: 50rpx;
  left: 20rpx;
  padding: 0 15rpx;
  display: flex;
  align-items: center;
  z-index: 9999;
}
.video-goods cover-image{
  height: 156rpx;
  width: 156rpx;
  border-radius: 8rpx;
  margin-right: 20rpx;
}
.goods-right{
  font-size: 28rpx;
  color: #1a1a1a;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  flex: 1;
  height: 156rpx;
}
.goods-price{
  display: flex;
  justify-content: space-between;
  align-items: center;
}
.price{
  flex:1;
}
.goBuy{
  height: 52rpx;
  padding: 0 30rpx;
  border-radius: 26rpx;
  color: #fff;
  font-size: 24rpx;
  line-height: 52rpx;
  display: inline-block;
}
.buy-info{
  position: fixed;
  bottom:280rpx;
  left: 20rpx;
  min-width: 320rpx;
  z-index: 9999;
}
.buy-info .item-li{
  height: 68rpx;
  padding: 0 10rpx;
  border-radius: 34rpx;
  background-color: rgba(0,0,0,.4);
  color: #fff;
  font-size: 24rpx;
  display: flex;
  align-items: center;
  margin-bottom:20rpx;
}
.buy-info .item-li image{
  width: 60rpx;
  height: 60rpx;
  border-radius: 50%;
  margin-right: 20rpx;
}
.play-btn {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%,-50%);
  height: 128rpx;
  width: 128rpx;
  /* background-image: url(https://img.kemanyun.com/qianhuituan/2019-10-11/69_1570790815_yUO3cyjQTB.png);
  background-size: 100% 100%; */
  z-index: 200;
}
.market-price{
  color:#ccc;
  margin-top: 5rpx;
  flex:1;
  width: 100%;
}
.market-box{
  position: relative;
  display: inline-block;
  font-size:28rpx;
}
.line{
  width: 100%;
  position: absolute;
  height: 1px;
  background-color: #ccc;
  top: 16rpx;
}
.hidden-box{
  width: 2rpx;
  background: #fff;
  color: #fff;
  display: inline-block;
  height: 100%;
  vertical-align: middle;
  overflow: hidden;
}
// pages/Main/myVideo/index.js
var app = getApp()
var videoObj = ''
Page({
  data: {
    p: 1, // 分页
    newId: 0, // 下一个要播放视频的id
    oldId: 0, // 当前播放的视频id
    isEnd: false, // 是否已经全部请求完毕
    lists: [], // 视频数组
    showBack: false, // 是否展示返回按钮
    isLoading: false, // 是否加载中,供暂无数据出来
    swiperCurrent: 0, // 控制swiper的current
    playState: true // 暂停的开关
  },
  onLoad: function (options) {
    // 获取头部高度, 全屏播放要兼容齐刘海各种手机型号的高度
    wx.getSystemInfo({
      success: res => {
        const navigationBarHeight = res.statusBarHeight + 44
        const statusBarHeight = res.statusBarHeight
        const windowHeight = res.windowHeight
        this.setData({
          navigationBarHeight,
          statusBarHeight
        })
      }
    })
  },
  onShow: function () {
      this.selfReset() //重置方法
      this.getData() // 获取视频接
  },
  // 重置
  selfReset() {
    this.setData({
      p: 1,
      newId: 0,
      oldId: 0,
      lists: [],
      isEnd: false,
      swiperCurrent: 0,
      playState: true
    })
  },
  getData() {
    if (this.data.isLoading || this.data.isEnd) {
      return
    }
    wx.showLoading({
      title: '加载中'
    })
    this.setData({
      isLoading: true
    })
    let sendData = {}
    sendData.page = this.data.p++
    const postData = {
        page: sendData.page
    } // 视频播放接口传的参数
// 替换成自己封装的api请求的方法
    wx.request('get', `获取视频的接口`,
      postData, res => {
        const data = res.data
        if (data.status_code == 200) {
          wx.hideLoading()
          const lists = data.data.list
          if (sendData.page == 1) {
            this.setData({
              lists: lists,
              newId: lists.length>0?lists[this.data.swiperCurrent || 0].id:0,
              total_pages: data.meta.pagination.total_pages,
              video_buy_button_title: data.data.video_buy_button_title,
              video_buy_success_hint: data.data.video_buy_success_hint,
              video_goods_buy_data: data.data.video_goods_buy_data,
              video_list_title: data.data.video_list_title
            })
            // 增加浏览量
            if (this.data.lists.length>0) {
              videoObj = wx.createVideoContext('video' + this.data.newId)
              videoObj.play()
              this.addBrowse()
            }
          } else {
            this.setData({
              lists: this.data.lists.concat(lists)
            })
          }
          if (lists.length < 1) {
            this.setData({
              isEnd: true
            })
          }
          this.setData({
            isLoading: false
          })
        }
      })
  },
  // 设置swiper的current,tag==1下滑+1,tag==-1上滑-1
  playActive(tag) {
    var lists = this.data.lists
    var currentId = 0
    var swiperCurrent = 0
    for (var i in lists) {
      // 如果找到当前播放的视频,那么要判断下一个是加1还是重头播放
      if (lists[i]['id'] == this.data.newId) {
        if (tag == 1) {
          if (i < lists.length - 1) {
            i = parseInt(i) + 1
            currentId = lists[i]['id']
            swiperCurrent = parseInt(this.data.swiperCurrent) + 1
          } else {
            //播放到最后了
            currentId = lists[0]['id']
            swiperCurrent = 0
          }
        }
        if (tag == -1) {
          if (i != 0) {
            i = parseInt(i) - 1
            currentId = lists[i]['id']
            swiperCurrent = parseInt(this.data.swiperCurrent) - 1
          } else {
            //播放到第一个了,要把swiper的current变成数组最后一个,才能循环无缝
            currentId = lists[lists.length - 1]['id']
            swiperCurrent = lists.length - 1
          }
        }
      }
    }
    this.setData({
      swiperCurrent: swiperCurrent,
      oldId: this.data.newId,
      newId: currentId,
      playState: true
    })
    var scale = this.data.swiperCurrent % 10
    wx.createVideoContext('video' + this.data.oldId).pause()
    // wx.createVideoContext('video' + this.data.newId).play()
    videoObj = wx.createVideoContext('video' + this.data.newId)
    videoObj.play()
    // 增加浏览量
    this.addBrowse()
    // 如果还剩下5条去请求下一页
    if (scale == 5) {
      this.getData()
    }
  },
  // 点击暂停
  changeStatus() {
    if (this.data.lists.length != 0) {
      let playState = !this.data.playState
      if (playState) {
        // wx.createVideoContext('video' + this.data.newId).play()
        videoObj = wx.createVideoContext('video' + this.data.newId)
        videoObj.play()
      } else {
        wx.createVideoContext('video' + this.data.newId).pause()
      }
      this.setData({
        playState: playState
      })
    }
  },
  // 浏览量
  addBrowse() {
    wx.request('post', 获取浏览量的接口)
  },
  // 跳转视频列表页面
  goList() {
    wx.navigateTo({
      url: '../list/list',
    })
  },
  // 商品分享
  onShareAppMessage() {
    const data = this.data.lists[this.data.swiperCurrent]
    const product = this.data.lists[this.data.swiperCurrent].product
    return {
      title: product.title,
      imageUrl: product.img_video.img[0],
      path:''
  },
  // 立即购买
  goDeatil() {
    let uri = ''
    const data = this.data.lists[this.data.swiperCurrent]
    const product = this.data.lists[this.data.swiperCurrent].product
    wx.navigateTo({
      url: uri
    })
  },
  // 返回
  goback() {
    var pages = getCurrentPages()
    if (pages.length > 1) {
      wx.navigateBack({
        delta: 1
      })
    } else {
      wx.switchTab({
        url: '/pages/Main/index/index'
      })
    }
  },
  // 开始滑动事件
  touchStart(e) {
    this.setData({
      startY: e.touches[0]['clientY'],
      moveY: 0
    })
  },
  // 结束滑动事件
  touchEnd(e) {
    var that = this
    that.setData({
      endY: e.changedTouches[0]['clientY']
    })
    var moveY = that.data.startY - that.data.endY
    if (moveY > 0) {
      // 手指上划,查看下一个
      var sensitive = Math.abs(moveY)
      if (sensitive > 50) {
        that.playActive(1)
      }
    } else if (moveY < 0) {
      // 手指下划,查看上一个
      var sensitive = Math.abs(moveY)
      if (sensitive > 50) {
        that.playActive(-1)
      }
    }
  }
})

js里面onshow的那个地方是我们点击tabbar的时候不需要再次调接口,做的处理,如果不是tabbar,可以直接再onload里面调后端数据

注意的是,在开发中,只要不是本地的视频,掉了接口,然后ios的前两个视频就是黑屏,后来加上了custom-cache="{{false}}">就解决了

注意的另一个兼容的是,只有cover-view才能覆盖再原生video之上,像轮播购买记录的那个地方,cover-view不能包swiper,所有有的手机是被视频遮住的

如果不妨到tabbar里的话,就是全屏播放了,头部自定义,然后左右两边也没有黑色没撑满了

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,482评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,377评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,762评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,273评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,289评论 5 373
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,046评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,351评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,988评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,476评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,948评论 2 324
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,064评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,712评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,261评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,264评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,486评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,511评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,802评论 2 345