gemer cheung %!s(int64=4) %!d(string=hai) anos
achega
e1fc5a4d9d

+ 19 - 0
app.js

@@ -0,0 +1,19 @@
+// app.js
+App({
+  onLaunch() {
+    // 展示本地存储能力
+    const logs = wx.getStorageSync('logs') || []
+    logs.unshift(Date.now())
+    wx.setStorageSync('logs', logs)
+
+    // 登录
+    wx.login({
+      success: res => {
+        // 发送 res.code 到后台换取 openId, sessionKey, unionId
+      }
+    })
+  },
+  globalData: {
+    userInfo: null
+  }
+})

+ 14 - 0
app.json

@@ -0,0 +1,14 @@
+{
+  "pages":[
+    "pages/index/index",
+    "pages/logs/logs"
+  ],
+  "window":{
+    "backgroundTextStyle":"light",
+    "navigationBarBackgroundColor": "#fff",
+    "navigationBarTitleText": "Weixin",
+    "navigationBarTextStyle":"black"
+  },
+  "style": "v2",
+  "sitemapLocation": "sitemap.json"
+}

+ 4 - 0
app.wxss

@@ -0,0 +1,4 @@
+/**app.wxss**/
+page {
+  background: rgba(0, 0, 0, 0)
+}

+ 315 - 0
components/video-swiper/index.js

@@ -0,0 +1,315 @@
+Component({
+  options: {
+    addGlobalClass: true,
+    // 指定所有 _ 开头的数据字段为纯数据字段
+    pureDataPattern: /^_/
+  },
+  properties: {
+    duration: {
+      type: Number,
+      value: 500
+    },
+    easingFunction: {
+      type: String,
+      value: 'default'
+    },
+    loop: {
+      type: Boolean,
+      value: true
+    },
+    targetPlayIndex: {
+      type: Number,
+      value: -1,
+      observer: function (newVal) {
+        if (newVal >= 0) {
+          this.targetPlay(newVal)
+        }
+      }
+    },
+    // {id, url, objectFit}
+    videoList: {
+      type: Array,
+      value: [],
+      observer: function (newVal = []) {
+        this._videoListChanged(newVal)
+      }
+    }
+  },
+  data: {
+    nextQueue: [], // 待播放列表
+    prevQueue: [], // 已播放列表
+    curQueue: [], // 当前播放列表
+    circular: false, // 是否循环
+    _last: 0, // 上一次显示
+    _maxCount: -1,
+    _dataIdx: 0,
+    initIndex: 0,
+    playerIdx: 0,
+    playing: false,
+    _change: -1,
+    _invalidUp: 0,
+    _invalidDown: 0,
+    _videoContexts: []
+  },
+  lifetimes: {
+    attached() {
+      this.data._videoContexts = [
+        wx.createVideoContext('video_0', this),
+        wx.createVideoContext('video_1', this),
+        wx.createVideoContext('video_2', this)
+      ]
+    }
+  },
+  created() {
+    this._swipering = false
+  },
+  methods: {
+    // 添加新的视频源
+    _videoListChanged(newVal) {
+
+      const data = this.data
+
+      newVal.forEach(item => {
+        data.nextQueue.push(item)
+      })
+      const maxCount = newVal.length
+      if (data.curQueue.length === 0) {
+        this.setData({
+          curQueue: data.nextQueue.splice(0, 3),
+          _maxCount: maxCount
+        }, () => {
+          this.playCurrent(0)
+        })
+      }
+    },
+
+    animationfinish(e) {
+      const {
+        _last,
+        _change,
+        curQueue,
+        prevQueue,
+        nextQueue
+      } = this.data
+
+      const current = e.detail.current
+      const diff = current - _last
+      this._swipering = false
+
+      this.data._last = current
+      this.playCurrent(current)
+
+      const direction = (diff === 1 || diff === -2) ? 'up' : 'down'
+
+      console.log('direction', direction)
+
+      if (direction === 'up') {
+        if (this.data._invalidDown === 0) {
+          this.data._dataIdx++
+          if (this.data._dataIdx >= this.data._maxCount) {
+            this.data._dataIdx = 0
+          }
+          this.loadCurQueue(this.data._dataIdx, current)
+        } else {
+          this.data._invalidDown -= 1
+        }
+      }
+
+      if (direction === 'down') {
+        if (this.data._invalidUp === 0) {
+          this.data._dataIdx--
+          if (this.data._dataIdx < 0) {
+            this.data._dataIdx = this.data._maxCount - 1
+          }
+          this.loadCurQueue(this.data._dataIdx, current)
+        } else {
+          this.data._invalidUp -= 1
+        }
+      }
+      this.triggerEvent('change', {
+        activeId: curQueue[current].id
+      })
+
+      let circular = true
+
+      // if (nextQueue.length === 0 && current !== 0) {
+      //   circular = false
+      // }
+
+      // if (prevQueue.length === 0 && current !== 2) {
+      //   circular = false
+      // }
+
+
+      this.setData({
+        curQueue,
+        circular,
+      })
+    },
+
+    loadCurQueue(dataIdx, playerIdx) {
+      const {
+        _maxCount,
+        curQueue,
+        videoList
+      } = this.data
+
+      const current = playerIdx;
+      let curDataIdx = dataIdx;
+
+      if (curDataIdx > _maxCount) {
+        curDataIdx = _maxCount;
+      }
+
+      let pre = 0,
+        next = 0,
+        preV, nextV, curVideo;
+
+      pre = current - 1;
+      if (pre < 0) {
+        pre = 2;
+      }
+      next = current + 1;
+      if (next > 2) {
+        next = 0;
+      }
+      if (curDataIdx - 1 >= 0) {
+        preV = videoList[curDataIdx - 1];
+      } else {
+        preV = videoList[_maxCount - 1];
+      }
+      console.log('curDataIdx + 1 ', curDataIdx + 1)
+      if (curDataIdx + 1 < _maxCount) {
+        nextV = videoList[curDataIdx + 1];
+      } else {
+        nextV = videoList[0];
+      }
+
+      curVideo = videoList[curDataIdx];
+      curQueue[pre] = preV
+      curQueue[next] = nextV
+      curQueue[current] = curVideo
+
+
+      console.log('preV', preV)
+      console.log('curVideo', curVideo)
+      console.log('nextV', nextV)
+      console.log('curQueue', curQueue)
+    },
+    /**
+     * 指定视频播放 
+     * @param {videoList(index)} dataIdx 
+     */
+    targetPlay(dataIdx) {
+      let curQueue = this.data.videoList.slice();
+
+      if (dataIdx >= this.data._maxCount) {
+        this._dataIdx = dataIdx = this.data._maxCount - 1
+      }
+
+      if (this.data._maxCount - dataIdx < 3) {
+        if (this.data._maxCount - dataIdx === 2) {
+          const last = this.data.videoList.slice().shift();
+          curQueue = curQueue.splice(dataIdx, 2).concat(last)
+        }
+        if (this.data._maxCount - dataIdx === 1) {
+          const first = this.data.videoList.slice().pop();
+          curQueue = [first].concat(curQueue.splice(0, 2));
+        }
+      } else {
+        curQueue = curQueue.splice(dataIdx, 3)
+      }
+      this.setData({
+        curQueue: curQueue,
+        _dataIdx: dataIdx,
+        playerIdx: 0
+      }, () => {
+        this.playCurrent(0)
+      })
+    },
+    // 避免卡顿
+    playCurrent(current) {
+
+      this.data._videoContexts.forEach((ctx, index) => {
+        index !== current ? ctx.pause() : ctx.play()
+      })
+
+      this.setData({
+        playing: true,
+        playerIdx: current
+      }, () => {
+        // this.data._videoContexts.forEach((ctx, index) => {
+        //   index !== current ? ctx.seek(0) : null
+        // })
+      })
+      //  // 重置所有视频pause时重置时间
+      //  setTimeout(() => {
+      //   this.data._videoContexts.forEach((ctx, index) => {
+      //     index !== current ? ctx.seek(0) : null
+      //   })
+      // }, 100)
+
+
+    },
+
+    onPlay(e) {
+      this.trigger(e, 'play')
+    },
+
+    onPause(e) {
+      this.trigger(e, 'pause')
+    },
+
+    onEnded(e) {
+      this.trigger(e, 'ended')
+    },
+
+    onError(e) {
+      this.trigger(e, 'error')
+    },
+
+    onTimeUpdate(e) {
+      this.trigger(e, 'timeupdate')
+    },
+
+    onWaiting(e) {
+      this.trigger(e, 'wait')
+    },
+
+    onProgress(e) {
+      this.trigger(e, 'progress')
+    },
+
+    onLoadedMetaData(e) {
+      this.trigger(e, 'loadedmetadata')
+    },
+    trigger(e, type, ext = {}) {
+      const detail = e.detail
+      const activeId = e.target.dataset.id
+      this.triggerEvent(type, Object.assign({
+        ...detail,
+        activeId
+      }, ext))
+    },
+    onVideoOverlayTap() {
+      if (this.data.playing) {
+        this.data._videoContexts.forEach((ctx, index) => {
+          ctx.pause()
+        })
+        this.setData({
+          playing: false
+        })
+      } else {
+        this.data._videoContexts.forEach((ctx, index) => {
+          index !== this.data.playerIdx ? ctx.pause() : ctx.play()
+        })
+        this.setData({
+          playing: true
+        })
+      }
+    },
+    onSwiping() {
+      this._swipering = true
+    }
+  }
+})

+ 4 - 0
components/video-swiper/index.json

@@ -0,0 +1,4 @@
+{
+    "component": true,
+    "usingComponents": {}
+}

+ 21 - 0
components/video-swiper/index.wxml

@@ -0,0 +1,21 @@
+<view class="container">
+  <swiper class="video-swiper" circular="{{circular}}" easing-function="{{easingFunction}}" vertical
+    current="{{initIndex}}" duration="{{duration}}" bindanimationfinish="animationfinish"
+    bindtransition="onSwiping"
+    >
+    <!-- curQueue 循环会导致video重新插入,objectFit 不可变更 -->
+    <swiper-item wx:for="{{curQueue}}" wx:key="*this">
+      <view class="video-swiper-item ">
+        <video id="video_{{index}}" class="video_item" loop="{{loop}}" enable-play-gesture="{{false}}"
+          autoplay="{{false}}" enable-progress-gesture="{{false}}" show-center-play-btn="{{false}}" controls="{{false}}"
+          src="{{item.url}}" data-id="{{item.id}}" object-fit="{{item.objectFit || 'cover'}}" data-index="{{index}}"
+          bindplay="onPlay" bindpause="onPause" bindended="onEnded" binderror="onError" bindtimeupdate="onTimeUpdate"
+          bindwaiting="onWaiting" bindprogress="onProgress" bindloadedmetadata="onLoadedMetaData">
+        </video>
+        <view class="video-overlay" data-player-idx="{{index}}" bind:tap="onVideoOverlayTap">
+
+        </view>
+      </view>
+    </swiper-item>
+  </swiper>
+</view>

+ 24 - 0
components/video-swiper/index.wxss

@@ -0,0 +1,24 @@
+.container {
+  width: 100%;
+  height: 100%;
+}
+
+.video-swiper {
+  width: 100%;
+  height: 100%;
+}
+
+.video-swiper-item,
+.video_item,
+.video-overlay {
+  height: 100%;
+  width: 100%;
+  position: relative;
+}
+
+.video-overlay {
+  position: absolute;
+  top: 0;
+  left: 0;
+  z-index: 2;
+}

+ 85 - 0
pages/index/index.js

@@ -0,0 +1,85 @@
+// index.js
+// 获取应用实例
+const app = getApp()
+
+wx.setNavigationBarColor({
+  frontColor: '#ffffff',
+  backgroundColor: '#ff0000',
+})
+const urls = [
+  'https://4dkk2.4dage.com/swipe-video-test/1.MP4',
+  'https://4dkk2.4dage.com/swipe-video-test/2.MP4',
+  'https://4dkk2.4dage.com/swipe-video-test/3.MP4',
+  'https://4dkk2.4dage.com/swipe-video-test/4.MP4',
+  'https://4dkk2.4dage.com/swipe-video-test/5.MP4',
+  // 'https://4dkk2.4dage.com/swipe-video-test/1.MP4',
+  // 'https://4dkk2.4dage.com/swipe-video-test/2.MP4',
+  // 'https://4dkk2.4dage.com/swipe-video-test/3.MP4',
+  // 'https://4dkk2.4dage.com/swipe-video-test/4.MP4',
+  // 'https://4dkk2.4dage.com/swipe-video-test/5.MP4',
+  // 'https://4dkk2.4dage.com/swipe-video-test/1.MP4',
+  // 'https://4dkk2.4dage.com/swipe-video-test/2.MP4',
+  // 'https://4dkk2.4dage.com/swipe-video-test/3.MP4',
+  // 'https://4dkk2.4dage.com/swipe-video-test/4.MP4',
+  // 'https://4dkk2.4dage.com/swipe-video-test/5.MP4',
+  // 'https://4dkk2.4dage.com/swipe-video-test/1.MP4',
+  // 'https://4dkk2.4dage.com/swipe-video-test/2.MP4',
+  // 'https://4dkk2.4dage.com/swipe-video-test/3.MP4',
+  // 'https://4dkk2.4dage.com/swipe-video-test/4.MP4',
+  // 'https://4dkk2.4dage.com/swipe-video-test/5.MP4',
+  // 'https://4dkk2.4dage.com/swipe-video-test/1.MP4',
+  // 'https://4dkk2.4dage.com/swipe-video-test/2.MP4',
+  // 'https://4dkk2.4dage.com/swipe-video-test/3.MP4',
+  // 'https://4dkk2.4dage.com/swipe-video-test/4.MP4',
+  // 'https://4dkk2.4dage.com/swipe-video-test/5.MP4',
+  // 'https://4dkk2.4dage.com/swipe-video-test/1.MP4',
+  // 'https://4dkk2.4dage.com/swipe-video-test/2.MP4',
+]
+
+const videoList = urls.map((url, index) => ({
+  id: index + 1,
+  url,
+  objectFit: 'cover'
+}))
+console.log('videoList', videoList)
+
+
+Page({
+  data: {
+    videoList,
+    targetPlayIndex: -1
+  },
+  
+  onLoad() {
+    // setTimeout(() => {
+    //   this.setData({
+    //     targetPlayIndex: 6
+    //   })
+    //   console.log('指向播放')
+    // }, 3000)
+
+  },
+  // 事件处理函数
+  onPlay(e) {},
+
+  onPause(e) {
+    //  console.log('pause', e.detail.activeId)
+  },
+
+  onEnded(e) {},
+
+  onError(e) {},
+
+  onWaiting(e) {},
+
+  onTimeUpdate(e) {},
+
+  onProgress(e) {},
+
+  onLoadedMetaData(e) {
+    // console.log('LoadedMetaData', e)
+  },
+  onChange(e) {
+    // console.log('onChange', e.detail)
+  }
+})

+ 6 - 0
pages/index/index.json

@@ -0,0 +1,6 @@
+{
+  "usingComponents": {
+    "video-swiper": "../../components/video-swiper"
+  },
+  "navigationStyle": "custom"
+}

+ 11 - 0
pages/index/index.wxml

@@ -0,0 +1,11 @@
+<!--index.wxml-->
+<view class="container">
+
+  <video-swiper duration="200" class="video-swiper" video-list="{{videoList}}" target-play-index="{{targetPlayIndex}}" bindplay="onPlay" bindpause="onPause"
+    bindtimeupdate="onTimeUpdate" bindended="onEnded" binderror="onError" bindwaiting="onWaiting"
+    bindprogress="onProgress" bindloadedmetadata="onLoadedMetaData" bindchange="onChange">
+
+  </video-swiper>
+
+
+</view>

+ 16 - 0
pages/index/index.wxss

@@ -0,0 +1,16 @@
+page {
+  background-color: #000;
+  overflow: hidden;
+  height: 100%;
+  width: 100%;
+}
+
+.container {
+  width: 100vw;
+  height: 100vh;
+}
+
+video-swiper {
+  width: 100%;
+  height: 100%;
+}

+ 18 - 0
pages/logs/logs.js

@@ -0,0 +1,18 @@
+// logs.js
+const util = require('../../utils/util.js')
+
+Page({
+  data: {
+    logs: []
+  },
+  onLoad() {
+    this.setData({
+      logs: (wx.getStorageSync('logs') || []).map(log => {
+        return {
+          date: util.formatTime(new Date(log)),
+          timeStamp: log
+        }
+      })
+    })
+  }
+})

+ 4 - 0
pages/logs/logs.json

@@ -0,0 +1,4 @@
+{
+  "navigationBarTitleText": "查看启动日志",
+  "usingComponents": {}
+}

+ 6 - 0
pages/logs/logs.wxml

@@ -0,0 +1,6 @@
+<!--logs.wxml-->
+<view class="container log-list">
+  <block wx:for="{{logs}}" wx:key="timeStamp" wx:for-item="log">
+    <text class="log-item">{{index + 1}}. {{log.date}}</text>
+  </block>
+</view>

+ 8 - 0
pages/logs/logs.wxss

@@ -0,0 +1,8 @@
+.log-list {
+  display: flex;
+  flex-direction: column;
+  padding: 40rpx;
+}
+.log-item {
+  margin: 10rpx;
+}

+ 75 - 0
project.config.json

@@ -0,0 +1,75 @@
+{
+  "description": "项目配置文件",
+  "packOptions": {
+    "ignore": []
+  },
+  "setting": {
+    "urlCheck": true,
+    "es6": true,
+    "enhance": false,
+    "postcss": true,
+    "preloadBackgroundData": false,
+    "minified": true,
+    "newFeature": false,
+    "coverView": true,
+    "nodeModules": false,
+    "autoAudits": false,
+    "showShadowRootInWxmlPanel": true,
+    "scopeDataCheck": false,
+    "uglifyFileName": false,
+    "checkInvalidKey": true,
+    "checkSiteMap": true,
+    "uploadWithSourceMap": true,
+    "compileHotReLoad": false,
+    "useMultiFrameRuntime": true,
+    "useApiHook": true,
+    "useApiHostProcess": false,
+    "babelSetting": {
+      "ignore": [],
+      "disablePlugins": [],
+      "outputPath": ""
+    },
+    "enableEngineNative": false,
+    "bundle": false,
+    "useIsolateContext": true,
+    "useCompilerModule": true,
+    "userConfirmedUseCompilerModuleSwitch": false,
+    "userConfirmedBundleSwitch": false,
+    "packNpmManually": false,
+    "packNpmRelationList": [],
+    "minifyWXSS": true
+  },
+  "compileType": "miniprogram",
+  "libVersion": "2.16.1",
+  "appid": "wx26de648117fcefaa",
+  "projectname": "swiperVideo",
+  "debugOptions": {
+    "hidedInDevtools": []
+  },
+  "scripts": {},
+  "staticServerOptions": {
+    "baseURL": "",
+    "servePath": ""
+  },
+  "isGameTourist": false,
+  "condition": {
+    "search": {
+      "list": []
+    },
+    "conversation": {
+      "list": []
+    },
+    "game": {
+      "list": []
+    },
+    "plugin": {
+      "list": []
+    },
+    "gamePlugin": {
+      "list": []
+    },
+    "miniprogram": {
+      "list": []
+    }
+  }
+}

+ 7 - 0
sitemap.json

@@ -0,0 +1,7 @@
+{
+  "desc": "关于本文件的更多信息,请参考文档 https://developers.weixin.qq.com/miniprogram/dev/framework/sitemap.html",
+  "rules": [{
+  "action": "allow",
+  "page": "*"
+  }]
+}

+ 19 - 0
utils/util.js

@@ -0,0 +1,19 @@
+const formatTime = date => {
+  const year = date.getFullYear()
+  const month = date.getMonth() + 1
+  const day = date.getDate()
+  const hour = date.getHours()
+  const minute = date.getMinutes()
+  const second = date.getSeconds()
+
+  return `${[year, month, day].map(formatNumber).join('/')} ${[hour, minute, second].map(formatNumber).join(':')}`
+}
+
+const formatNumber = n => {
+  n = n.toString()
+  return n[1] ? n : `0${n}`
+}
+
+module.exports = {
+  formatTime
+}