• vue移动端项目经验(二)


    视频插件vue-video-player的使用及注意事项

    官方文档
    video.js:https://docs.videojs.com/docs/api/player.html
    vue-video-player:https://github.com/surmon-china/vue-video-player
    
    1、包的安装
    npm install vue-video-player --save
    
    2、包的引入
    import VideoPlayer from 'vue-video-player'
    require('video.js/dist/video-js.css')  //若此行报错无法找到,则改为require('vue-video-player/node_modules/video.js/dist/video-js.css')
    require('vue-video-player/src/custom-theme.css')
    Vue.use(VideoPlayer)
    
    3、插件使用
    html部分
    <div style="overflow:hidden">  //移动端当屏幕宽度较小时,此视频控件调节音量时会使得屏幕出现横向滚动条,故在父级盒子使用overflow:hidden来阻止滚动条出现,以免影响用户体验。
        <video-player  class="video-player vjs-custom-skin"
            ref="videoPlayer" 
            :playsinline="true" 
            :options="playerOptions"   //相关配置
            @play="onPlayerPlay($event)"//监听开始状态(非必需),这是事件,按需使用
            @pause="onPlayerPause($event)"//监听暂停状态(非必需),这是事件,按需使用
        >
        </video-player>
    </div>
    
    
    js部分
    data(){
        return{
    
            playerOptions : {
                playbackRates: [0.5, 1.0, 1.5, 2.0], //播放速度
                autoplay: false, //如果true,浏览器准备好时开始回放。
                muted: false, // 默认情况下将会消除任何音频。
                loop: false, // 导致视频一结束就重新开始。
                preload: 'auto', // 建议浏览器在<video>加载元素后是否应该开始下载视频数据。auto浏览器选择最佳行为,立即开始加载视频(如果浏览器支持)
                language: 'zh-CN',
                aspectRatio: '16:9', // 将播放器置于流畅模式,并在计算播放器的动态大小时使用该值。值应该代表一个比例 - 用冒号分隔的两个数字(例如"16:9"或"4:3")
                fluid: true, // 当true时,Video.js player将拥有流体大小。换句话说,它将按比例缩放以适应其容器。
                sources: [{
                    type: "",//这里的种类支持很多种:基本视频格式、直播、流媒体等,具体可以参看git网址项目
                    src: "" //url地址
                }],
                poster: "../../static/images/test.jpg", //你的封面地址
                //  document.documentElement.clientWidth, //播放器宽度
                notSupportedMessage: '此视频暂无法播放,请稍后再试', //允许覆盖Video.js无法播放媒体源时显示的默认信息。
                controlBar: {
                      timeDivider: true,  //当前时间和持续时间的分隔符
                      durationDisplay: true,  //显示持续时间
                      remainingTimeDisplay: false,  //是否显示剩余时间功能
                      fullscreenToggle: true  //全屏按钮
                }
            }
    
        }
    },
    
    //挂载视频组件(非必须)不写这一步也可以实现播放,添加这个是为了自定义按钮使用
    computed: {
        player() {
            return this.$refs.videoPlayer.player//自定义播放
        }
    },
    
    

    视频插件xgplayer(西瓜播放器)的使用及注意事项

    更多详情见官方文档:http://h5player.bytedance.com/
    
    贴代码:
    1、安装包:npm install xgplayer
    2、代码部分:
    <template>
           <div id="mse"></div>   //1、标签,必须要有
    </template>
    
    <script>
        import * as axios from 'axios'
        import common from '../../kits/common.js'
        import Player from 'xgplayer';   //2、引入视频组件
        export default{
            name:'Demo',
            data(){
                return{
                    playerConfig:{   //3、动态设置相关值
                        id: 'mse',
                        normalUrl:'',  //普清
                        midUrl:'',   //高清
                        hightUrl: '', //超清
                        poster: '',   //封面
                    }
                }
            },
            created(){
                this.getVideoInfo()
            },
            methods:{
                playerInit(){   //4、设置视频配置(注意:playerInit应放在异步函数里或mounted之后,不可在created里直接加载,否则不生效)
                    let player = new Player({   //初始化player实例对象
                        id:'mse',
                        url: this.playerConfig.highUrl,   //视频链接
                        poster:this.playerConfig.poster,    //封面图片
                        fluid: true,   //流式布局
                        volume: 0.6,   //初始音量
                        whitelist: ['iPhone','Android'],   //白名单
                        playsinline: true,   //内联模式
                        videoInit: true,
                        'x5-video-player-type': 'h5',
                    });
                    player.emit('resourceReady', [{name: '超清', url: this.playerConfig.highUrl}, {name: '高清', url: this.playerConfig.highUrl},{name: '标清', url: this.playerConfig.highUrl},]);
                }
            },
            getVideoInfo(){
                axios.get(`${common.videoApi}/getVideo`).then(res=>{
                    this.playerConfig.highUrl=res.data.data.url1    //获取超清视频链接
                    this.playerConfig.midUrl=res.data.data.url2    //获取高清视频链接
                    this.playerConfig.poster=res.data.data.vimage   //获取视频封面
                    this.playerInit()   //为什么在这里调用playerInit函数而不在mounted生命周期中调用?因为接口请求是异步的,在mounted中接口调用还未返回数据,故playerConfig中的数据仍然为空。而视频组件依赖于playerConfig中的数据
                })
            }
        }
        
    </script>
    
    【【【直播】】】:直播与视频播放差不多,就以下几点不同
    1、引入不同:
    import 'xgplayer';   //先引入xgplayer
    import HlsJsPlayer from 'xgplayer-hls.js';  //再引入组件
    2、初始化不同:
    let player=new HlsJsPlayer({    //初始化实例对象HlsJsPlayer
        id:'hlsmse',
        url: this.hlsPlayerConfig.url,   
        poster:this.hlsPlayerConfig.poster,    
    })
    
    
    【【【注意事项:西瓜播放器xgplayer我用的时候有个Bug,就是在当前页面,xgplayer的视频链接url每重新赋值一次,xgplayer都会在原有的<video>标签上再生成一个新的<video>标签,并去覆盖原有的标签,通过这种方式实现当前视频的更新,但这也导致一个问题,因为原有的<video>标签不会被清空。就是说会出现多个video标签在同一个页面上的同一个位置。不仅影响性能,还会导致视频组件占位冲突。目前我想到的解决办法:在url重新赋值后,利用provide / inject 组合 刷新当前页面,以达到重新加载xgplayer组件的目的。(如何刷新,见下方)】】】
    【【【09.10.24,想到了以上bug第二种解决办法:在重新初始化播放器之前,利用js先删除旧的已经渲染过的<div id="mse"></div>,紧接在再创建一个新的<div id="mse"></div>即可,代码如下
        <div class="videoBody" id="videoBody">
               <div v-show="islive==false" id="mse" class="mse"></div>
               <div v-show="islive!==true" id="hlsmse" class="hlsmse"></div>
               <div id="msedemo"></div>
        </div>
    
    重新创建mse:
       document.getElementById('videoBody').removeChild(document.getElementById('mse'))
       document.getElementById('videoBody').insertBefore(document.createElement('div'),document.getElementById('hlsmse')) 
       document.getElementById('hlsmse').previousElementSibling.setAttribute('id','mse') 
    
    重新创建hlsmse:
       document.getElementById('videoBody').removeChild(document.getElementById('hlsmse'))
       document.getElementById('videoBody').insertBefore(document.createElement('div'),document.getElementById('msedemo')) 
       document.getElementById('msedemo').previousElementSibling.setAttribute('id','hlsmse') 
    】】】
    
    
    【【【注册事件,如监听播放暂停事件】】】:
    playerInit(){   //xgplayer配置
        let player = new Player({
            id:'mse',
            url: this.playerConfig.url,   
        });
        player.on('play',function(){    //注册并监听播放事件
            consol.log('播放了')
        })
        player.on('pause',function(){   //注册并监听暂停事件
            console.log('暂停了')
        })
    },
    具体可查看官网:http://h5player.bytedance.com/api/#%E4%BA%8B%E4%BB%B6
    【【【上次播放到这里】】】:
    1、在playInit的new Player里添加:
    progressDot: [{'time': this.playerConfig.lastTime,'text':'您上次看到了这里'}],
    2、在playInit函数最后添加以下代码
    if(this.playerConfig.currentTime>0){
        player.currentTime=this.playerConfig.lastTime   //将上次播放的时间配置到当前播放器的播放时间
    }
    this.postTime=setInterval(()=>{
        if(player.hasStart){
            axios.post(`${common.videoapi}/amstc/addVideoTime?vid=${this.vid}&uid=${this.uid}&currenttime=${parseInt(player.currentTime)}`)   //每过20秒将当前播放时间上传至服务器,下次进入此页面获取上次播放时间。
        }
    },20000)
    
    3、在页面销毁后,清除定时器
    destroyed(){
        clearInterval(this.postTime)
    },
    注意,此功能适用于pc端,移动端安卓ios会有兼容问题
    
    【【【安卓手机微信端里,组件出现黑屏情况,视频加载不出来,并报错The play() request was interrupted by a new load request】】】:
    提示:在安卓端的微信内嵌的浏览器里,视频组件是可以实现自动播放的,在其他地方浏览器则不可播放,除非初始时是静音状态。因为浏览器视频播放协议的原因,video在用户交互之前,一般不允许自动播放。
    原因看这:http://link.sov5.cn/l/LKioRMRBQX
    解决:
    let player=new HlsJsPlayer({
        id:'hlsmse',
        autoplay:false,  //这里取消自动播放
        url: this.hlsPlayerConfig.url,   
        poster:this.posterImg,    
        fluid: true,   
        volume: 0.6,   
        useHls: true,   
        whitelist: ['iPhone','Android'],  
        playsinline: true,   
        ignores: ['time','volume'],
        'x5-video-player-type': 'h5',
    })
    
    + player.start(this.hlsPlayerConfig.url)   //调用此函数即可,调用此函数在安卓微信端会自动播放
    
    

    vue如何刷新当前页面

    转自:https://www.cnblogs.com/mmzuo-798/p/9356253.html

    重新刷新当前页面,首先想到的就是路由重新导入当前页面,比如这样:
    this.$router.push({name:'goodsList',params:{vid:vid}})
    但是:我们会发现用vue-router在当前页面再重定向路由到页面本身,就算改变id,页面也是不进行刷新的,根本没有任何作用~所以这个方法out

    下面提供两种方法:
    1、强制刷新法,页面会类似于ctrl+f5的那种刷新,会有一段时间的空白出现,用户体验很不好,不建议使用
    location. reload()
    this.$router.go(0)

    2、provide / inject 组合 方式是我试过最实用的
    ① 首先,要修改下你的app.vue,如图

    ② 通过声明reload方法,控制router-view的显示或隐藏,从而控制页面的再次加载,这边定义了
    isRouterAlive //true or false 来控制,然后在需要当前页面刷新的页面中注入App.vue组件提供(provide)的 reload 依赖,然后直接用this.reload来调用就行

    vue利用watch监听路由变化,可配合上面刷新方法使用

    watch: {
          $route() {
            console.log("路由发生变化了")
            this.goodId=this.$route.params.vid  //2、获取新id重新请求数据
            axios.get(`${common.goodsapi}/getgoods?id=${this.goodId}`).then(res=>{.....})
            this.reload()  //3、监听到路由变化后刷新页面,同时完成页面数据更新
          }
        },
    methods:{
        goToGoodsList(){
            this.$router.push({name:'goodsList',params:{vid:vid}})    //1、点击后路由跳转仍在本页面,路由仅所携带的id发生改变。
        }
    }
    

    回到顶部

    document.documentElement.scrollTo(0,0)
    

    转换时间格式

            transCommentDate(dateNum){
                const date=new Date(dateNum)
                let Y=date.getFullYear()
                let M=date.getMonth()+1
                M=M<10?('0'+M):M
                let D=date.getDate()
                D=D<10?('0'+D):D
                let h=date.getHours()
                h=h<10?('0'+h):h
                let m=date.getMinutes()
                m=m<10?('0'+m):m
                let s=date.getSeconds()
                s=s<10?('0'+s):s
    
                return `${Y}-${M}-${D} ${h}:${m}:${s}`
            },
    

    切换输入框时的获取焦点问题

    需求:有两个input框(评论和回复),在点击“回个话”按钮后,评论input框切换成回复input框,同时回复input框获取焦点
    
    评论框:<input class="comment" id="comment" v-if="!isReply" />   回复框:<input class="reply" id="reply" v-slse-if="isReply"/>
    
    思路:当点击“回个话”按钮后,执行函数使this.isReply=true,回复框显示。再使用document.getElementById("reply").focus()使其获取焦点。
    
    问题:以上思路执行后会报错,错误为document.getElementById("reply")找不到。为什么呢,因为回复框原本是不存在的,只有在页面mounted之后,回复框dom才会加载出来,而document.getElementById("reply")是在页面加载之前就执行了,故找不到。
    
    【【【解决办法:将两个输入框的id设置成一样的(如id="inputLabel"),再使用document.getElementById("inputLabel")即可。】】】
    

    利用websocket实现直播实时评论

    直接贴代码
    <template>
        <div>
            <div>
                <ul>
                    <li v-for="(item,index) in messages" :key="index">{{item.ccontent}}</li>   //渲染从socket获取到的评论数据
                </ul>
            </div>
            <input v-model="commentValue" />
            <div @click="sendComment">发表评论</div>  //发送评论数据到socket
        <div>
    </template>
    
    <script>
        import * as axios from 'axios'
        import common from '../../sets/common.js'
        import {getCookie} from '../../sets/cookie.js'  //引入cookie
        data(){
            return{
                usertoken:null,   //用户信息
                wspath:'',   //websocket的path
                socket:null,   //socket实例对象
                messages:[],   //从socket获取的评论数据
                commentValue:'',   //用户输入的评论内容
            }
        }
        created(){
            this.usertoken = getCookie('Token')  
            this.chatInit()  //初始化socket
        },
        methods:{
            //1、实例化socket
            chatInit(){
                if(typeof(WebSocket) === 'undefined'){
                    this.$toast({
                        message:'您的浏览器不支持socket',
                        duration:2000,  
                    })
                }else{
                    // 实例化socket
                    if(this.usertoken){
                      this.wspath = `${common.socketlink}${this.roomid}?token=${this.usertoken}`    //socketlink由后端提供给我,roomid从直播接口获取,token自己去设置
                    } else {
                      this.wspath = `${common.socketlink}${this.roomid}`
                    }
                    //初始化socket实例对象
                    this.socket = new WebSocket(this.wspath)   
                    // 监听socket连接
                    this.socket.onopen = ()=>{
                        console.log('socket连接成功')
                    }
                    // 监听socket错误信息
                    this.socket.onerror = ()=>{
                        console.log('socket连接错误')
                    }
                    //监听socket关闭
                    this.socket.onclose=()=>{
                        console.log('socket连接关闭')
                    }
                    // 监听socket消息
                    this.socket.onmessage = this.getComment    //这里面调用函数时不能带(),若this.getComment()会报错
                }
            },
            //2、监听socket消息
            getComment(msg){
                this.messages=this.messages.concat(JSON.parse(msg.data))
            },
            //3、发送评论
            sendComment(){
                    this.socket.send(`{"ccontent":"${this.commentValue}"}`)    //这里注意上传时前面参数如ccontent要与后端的一致,不然socket会检测上传错误,从而自动关闭连接
                    this.commentValue=''
                    document.getElementById('commentScrollHidden').scrollTop=document.getElementById('scrollBottom').offsetHeight   //发送评论后评论区回到底部
                }
            },
        }
    </script>
    
    

    实现简单的微信分享功能(若想使用js-sdk实现分享功能请移步 https://www.cnblogs.com/huihuihero/p/12132952.html)

    ①先动态设置每个页面的title(方法见下方),
    ②有些页面需要自定义标题的也可以通过 document.title= 设置(方法见下方)。
    ③最后就是ios微信浏览器兼容问题,解决了即可(方法见下方)
    

    ios微信浏览器分享问题

    【问题】:ios端微信内分享的链接url不改变,总是同一个页面
    
    【原因】:
    IOS:微信IOS设备,所记录的url是进入页的url,所以无论分享哪个页面,总是会分享成初始进入页的url
    Android:微信安卓设备,所记录的是当前页的url,所以分享出去的也会是当前页的url
    
    【详细解释】:Vue的路由默认的mode是hash,这个模式带来的bug不是一般的多,在hash模式下url地址会出现#,这个#号会使window.location.href()方法失效,因此location.href方式这里最好不要用。并且ios微信支付的调起也会因为#的存在而导致jsap调起失败,但是Android并没有问题。因此mode的模式最好是history,这种模式下微信支付调起完全没问题。接下来就是微信分享的问题,在history模式下android还是能够正常分享,但是ios系统在这种模式下在微信浏览器下的url地址就是一开始进入页面的url,不会改变,无论路由如何的切换(android则不存在这种情况)。
    
    【解决思路】:思路1、用js-sdk,详见微信文档。思路2、判断若是微信端,则在进入页面后将页面的路由拼接给url,已达到重新定义url的效果
    
    【解决方法】:在main.js中添加以下代码(利用Vue Router的afterEach方法)
    router.afterEach((to, from) => {
    	const u = navigator.userAgent.toLowerCase()
            //下面这句是做判断,是否是ios。当然你也可以做一些其他判断,如这里我通过to.path=='/demo/demoReply'将一些页面排除在外
    	if(u.indexOf("like mac os x") < 0 || u.match(/MicroMessenger/i) != 'micromessenger' || u.match(/WebP/i) == "webp"||to.path=='/pArticle/pArcReply'||to.path=='/demo/demoReply') return
    	if (to.path !== global.location.pathname) {
    	  location.assign(to.fullPath)    //因为vue路由的hash模式,这里最好不要用 location.href= 或 location.replace() ,以避免url中#带来的问题
    	}
    })
    【提个醒】:1、使用以上方法时,遇到了个问题,一是路由传参使用params时,当参数超过2个就会出现bug,然后以query方式传参解决了,但参数再多时,query方式也不好用了,最终只好把这些页面在上述代码中通过判断方式排除了,暂未知晓原因。
    2、这个只能在线上用可以,线下时,需要先注释掉,不能使用。
    

    微信分享自定义标题

    在当前页面设置title即可,分享出去会自带页面title
    通过document.title="中国进入5G时代"方式设置
    getVideoInfo(){
        axios.get(`${common.videoapi}/getvideoinfo`).then(res=>{
            document.title=res.data.data.name
        })
    }
    

    vue根据路由动态改变页面标题

    在main.js中添加以下代码
    router.beforeEach((to, from, next) => {
        /* 路由发生变化修改页面title */
        if (to.meta.title) {
            document.title = to.meta.title;
        }
        next();
    })
    
    在router.js路由页面
    {
            path:'/demo/demoReply',
    	component:demoReply,
    	name:'DemoReply',
    	meta:{
    		title:'评论回复'   //设置标题
    	}
    },
    

    改变v-html内的指定标签的样式,这里以img为例,设置img最大宽度100%,高度自适应。(注:一定要设置高度自适应)

    方式1:js设置
    updated(){
        let elem=document.getElementById('demo').getElementsByTagName('img')    //获取指定标签里的img
        let elem2=Array.from(elem)   //因为getElementsByTagName()返回的是一个伪数组,所以这里使用Array.from转换为数组
            for(let item of elem2){
                item.style.maxWidth="100%"
                item.style.height="auto"
            }
        },
    
    
    方式2:css穿透模式下设置
    /deep/  htmlcontent{
        img{
            max-100%!important;
            height:auto!important;
        }
    }
    

    vue2.0项目上线屏蔽console.log()

    在build/webpack.prod.conf.js文件里的UglifyJsPlugin里加上这样两行代码即可
    new UglifyJsPlugin({
          uglifyOptions: {
            compress: {
              warnings: false,
              drop_debugger: true,    //屏蔽debugger
              drop_console: true,    //屏蔽console
            }
          },
          sourceMap: config.build.productionSourceMap,
          parallel: true
        }),
    

    减少,压缩项目打包后体积

    在 config/index.js 中修改参数,
    找到 productionSourceMap: true,改为 false 即可。
    

    时间转换

    transDate(dateNum){
                const date=new Date(dateNum)
                let Y=date.getFullYear()
                let M=date.getMonth()+1
                M=M<10?('0'+M):M
                let D=date.getDate()
                D=D<10?('0'+D):D
                let h=date.getHours()
                h=h<10?('0'+h):h
                let m=date.getMinutes()
                m=m<10?('0'+m):m
                let s=date.getSeconds()
                s=s<10?('0'+s):s
    
                return `${Y}-${M}-${D} ${h}:${m}:${s}`
            },
    

    解决格林尼治时间在ios端和android端的兼容问题

    格林尼治时间格式:2019-03-05T09:02:24.000+0000
    
    android端:
    new Date("2019-03-05T09:02:24.000+0000")  可正常转换。
    new Date("2019-03-05T09:02:24.000+0000".substr(0,19))  转换出来的时间会早8个小时
    
    ios端:
    new Date("2019-03-05T09:02:24.000+0000")  输出为NaN,不可用。
    new Date("2019-03-05T09:02:24.000+0000".substr(0,19))  可正常转换
    
    故通过判断是否是ios端,再选择转换方式。具体代码如下:
    transDate(dateNum){
            let u = navigator.userAgent;
            if (!!u.match(/(i[^;]+;( U;)? CPU.+Mac OS X/)) {   //ios端
                const date=new Date(dateNum.substr(0,19))   //截取前19位后再转换
                let Y=date.getFullYear()
                let M=date.getMonth()+1
                M=M<10?('0'+M):M
                let D=date.getDate()
                D=D<10?('0'+D):D
                let h=date.getHours()
                h=h<10?('0'+h):h
                let m=date.getMinutes()
                m=m<10?('0'+m):m
                let s=date.getSeconds()
                s=s<10?('0'+s):s
                return `${Y}-${M}-${D} ${h}:${m}:${s}`
            }else{                                             //非ios端
                const date=new Date(dateNum)   //不截取,正常转换
                let Y=date.getFullYear()
                let M=date.getMonth()+1
                M=M<10?('0'+M):M
                let D=date.getDate()
                D=D<10?('0'+D):D
                let h=date.getHours()
                h=h<10?('0'+h):h
                let m=date.getMinutes()
                m=m<10?('0'+m):m
                let s=date.getSeconds()
                s=s<10?('0'+s):s
                return `${Y}-${M}-${D} ${h}:${m}:${s}`
            }
                
    },
    

    判断手机操作系统是ios端还是android端

    checkPort(){
        let u = navigator.userAgent, app = navigator.appVersion;
        let isAndroid = u.indexOf('Android') > -1 || u.indexOf('Linux') > -1; //android终端
        let isIOS = !!u.match(/(i[^;]+;( U;)? CPU.+Mac OS X/); //ios终端
        if (isAndroid) {
           //这个是安卓操作系统
        }
        if (isIOS) {
      //这个是ios操作系统
        }
    }
    
  • 相关阅读:
    Windows Phone 7 开发之检查手机网络
    还原ipa里的png图片资源
    phpmyadmin 自动登录的办法
    Composer 是什么
    vuejs 和 element 搭建的一个后台管理界面
    Vue2 后台管理系统解决方案
    解决无限 This file is indented with tabs instead of 4 spaces
    无聊的小知识 规格严格
    va_list!!! 规格严格
    Java动态编译一个简单的例子(我转载的,但是经过修定,可以在Eclipse下运行) 规格严格
  • 原文地址:https://www.cnblogs.com/huihuihero/p/11649096.html
Copyright © 2020-2023  润新知