精华内容
下载资源
问答
  • 万事开头难,做什么事都要有个起点,后面才能更好的进行下去,因此我选择的起点就是最为直观的登陆页面 /login/index.vue /src/views/login/index 去除那些无关的东西,比如什么 rules 校验啊,默认的账号密码之类...

    登陆

    万事开头难,做什么事都要有个起点,后面才能更好的进行下去,因此我选择的起点就是最为直观的登陆页面 /login/index.vue

    /src/views/login/index

    去除那些无关的东西,比如什么 rules 校验啊,默认的账号密码之类的东西,直接看核心登陆方法 handleLogin

      handleLogin() {
          this.$refs.loginForm.validate(valid => {
            if (valid) {
              this.loading = true
              # 请求 store 中的方法
              this.$store.dispatch('LoginByUsername', this.loginForm).then(() => {
                this.loading = false
                this.$router.push({ path: this.redirect || '/' })
              }).catch((errorMsg) => {
                this.loading = false
                this.$message({
                  type: 'error',
                  message: errorMsg
                })
              })
            } else {
              console.log('error submit!!')
              return false
            }
          })
        },

    store 主要事做一些缓存之类的处理,这里调用了 store 中的 LoginByUsername 把表单内容传过去,这里我就改了点 catch 把我自己需要的错误信息进行提示,其他东西不需要改动

    /src/store/modules/user

    上面登陆中请求的 LoginByUsername 正是在这

        // 用户名登录
        LoginByUsername({ commit }, userInfo) {
          const username = userInfo.username.trim()
          return new Promise((resolve, reject) => {
            // 请求后台登陆
            loginByUsername(username, userInfo.password).then(response => {
              const data = response.data
              if (response.data.errorCode !== 200) {
                // 登陆失败,回传提示信息
                reject(data.errorMsg)
              }
              // 设置 token,作为用户已登陆的前端标识,存在 cookie 中
              commit('SET_TOKEN', data.retData)
              setToken(data.retData)
              resolve()
            }).catch(error => {
              reject(error)
            })
          })
        },

    setToken() 方法会把 token 保存到 cookie 里,很重要

    下面有个 GetUserInfo 方法,在你登陆的时候会去获取你的权限数据

        // 获取用户信息
        GetUserInfo({ commit, state }) {
          return new Promise((resolve, reject) => {
            // 请求获取权限
            getUserInfo(state.token).then(response => {
              if (response.status !== 200) { // 由于mockjs 不支持自定义状态码只能这样hack
                reject('error')
              }
              const data = response.data
    
              if (data.retData.module && data.retData.module.length > 0) { // 验证返回的roles是否是一个非空数组
                commit('SET_ROLES', data.retData.module)
              } else {
                // 当用户无任何权限时设置
                commit('SET_ROLES', ['普通用户'])
              }
    
              commit('SET_NAME', data.retData.username)
              commit('SET_AVATAR', 'https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif')
              commit('SET_INTRODUCTION', data.retData.username)
              resolve(response)
            }).catch(error => {
              reject(error)
            })
          })
        },

    不需要知道那个 new Promise 啥的干啥用,反正我不知道,只要知道 getUserInfo 这个方法就行了,这个方法会以上面之前保存的 token 为参数去请求获取你的用户权限,原逻辑是没有权限就跳登陆页面,我这系统需要,没权限也有个首页可看,所以
    SET_ROLES 参数给了个“普通用户”,反正什么值无所谓有值,没权限就行。
    下面 commit 里的参数,分别为,用户名,头像,介绍,保留就好,也可自定义。
    PS:在每次页面跳转时候页面都会走一个 GetUserInfo ,以此来判断用户有没有失效,因此,我的做法是直接把用户信息包括权限保存在 redis 里取,不要每次都从库里查

    /src/utils/request.js

    此文件是请求与返回拦截器,我们看返回值拦截器

    每次你请求从后台接口返回时,都会经过返回值拦截器,默认的逻辑是返回的 code 不为 20000 时候就会被拦截,这里需要我们改为自己的逻辑,很多人配置都对,点登录一直 error 就是因为这个原因

    // response interceptor
    service.interceptors.response.use(
      response => response,
      /**
       * 下面的注释为通过在response里,自定义code来标示请求状态
       * 当code返回如下情况则说明权限有问题,登出并返回到登录页
       * 如想通过 xmlhttprequest 来状态码标识 逻辑可写在下面error中
       * 以下代码均为样例,请结合自生需求加以修改,若不需要,则可删除
       */
      // response => {
      //   const res = response.data
      //   if (res.code !== 20000) {
      //     Message({
      //       message: res.message,
      //       type: 'error',
      //       duration: 5 * 1000
      //     })
      //     // 50008:非法的token; 50012:其他客户端登录了;  50014:Token 过期了;
      //     if (res.code === 50008 || res.code === 50012 || res.code === 50014) {
      //       // 请自行在引入 MessageBox
      //       // import { Message, MessageBox } from 'element-ui'
      //       MessageBox.confirm('你已被登出,可以取消继续留在该页面,或者重新登录', '确定登出', {
      //         confirmButtonText: '重新登录',
      //         cancelButtonText: '取消',
      //         type: 'warning'
      //       }).then(() => {
      //         store.dispatch('FedLogOut').then(() => {
      //           location.reload() // 为了重新实例化vue-router对象 避免bug
      //         })
      //       })
      //     }
      //     return Promise.reject('error')
      //   } else {
      //     return response.data
      //   }
      // },
      error => {
        console.log('err' + error) // for debug
        Message({
          message: '连接异常',
          // message: error.message,
          type: 'error',
          duration: 5 * 1000
        })
        return Promise.reject(error)
      }
    )
    

     

    /src/api/login

    接口的请求都会从这里去请求后台,就不多赘述,基本只需要改动下请求路径就ok了。
    登出 logout 也是,请求保证后台注销,前端处理部分也是在 store/modules/user 里的 LogOut 方法,基本不需要改动。

     

    目录权限

    大多数系统都有根据用户权限,或者说角色,展示相应页面的要求

    /src/router/index

    结合框架来实现目录的控制,如果是传统项目,一般是把目录结构整个存在后台数据库里,然后前端循环展示出来,但是这个在这里不需要这么麻烦,只需要把每个页面对应的权限保存起来就行了

    目录在分为两个部分 constantRouterMap 与 asyncRouterMap
    constantRouterMap:主要是通用部分,每个用户都有的页面
    asyncRouterMap:需要进行权限过滤的页面

    所以像首页这种都丢在 constantRouterMap 里,而像权限管理页面这种放在 asyncRouterMap 里
    在目录上加上 permission 标识,这个是我自定义的唯一标识,用于区分每个页面的权限,我这里直接以页面的路径为值,因为路径肯定唯一

      {
        path: '',
        component: Layout,
        alwaysShow: true,
        redirect: '/xxx/xxx',
        name: 'xxx',
        meta: {
          title: 'xxx',
          icon: 'xxx'
        },
        children: [
          {
            path: '/xxx/xxx',
            component: () => import('@/views/xxx/xxx'),
            name: 'xxx',
            meta: {
              title: 'xxx',
              icon: 'message',
              # 权限标识,每个目录唯一
              permission: '/xxx/xxx',
              noCache: false }
          }
        ]
      },

    然后就只需要在加载目录时对目录进行判断就可以了

    /src/permission

    这个文件就是整个路由逻辑在这

    从这里的逻辑可以看到,登陆后,现在判断了用户权限,如果没权限就会进入之前说到的 GetUserInfo 方法去获取权限,由于要对目录进行控制,所以在 GetUserInfo 里我们也需要获取到目录的权限列表,只需要获取到有的就行了,没有权限的目录就不需要获取。
    在 GetUserInfo 的最后通过 resolve 方法把返回值返回这个页面,截图中  module 就是我获取到的有权限的目录列表,然后通过
    GenerateRoutes 来生成要加载的目录,接下来就是改这了
    PS:不少人加我 QQ,说不明白这里什么意思,以及不知道要怎样的目录数据,我做个简要的说明。从这个 permission 文件中我们可以看到目录渲染一共三步:
    1. GetUserInfo 中获取到你的目录数据(我权限数据全放这个方法里获取了:用户数据,目录权限,数据权限等),但这里主要的就是目录权限
    2.在上门这个 permission.js 目录渲染的逻辑中获取 GetUserInfo 中获取到的目录权限,也就是上面那句
    const roles = res.data.retData.module 这个 module 就是我返回值中目录权限的部分,module 就是模块的意思
    3.也就是下一步,在 GenerateRoutes 方法中把 roles 目录权限传进去进行目录渲染

    然后目录权限的数据到底长什么样?先来看下表结构

    我代码中拿到的数据:

    我们要用的只有这个 permission 字段,说简单点,你既可以和我一样直接拿到目录对象的所有数据,也可以单单取这个 permission 字段组成的数组,看自己习惯,具体后面判断逻辑在下面目录渲染的部分,单单数组的逻辑会更加简单点

    /src/store/modules/permission

    找到 GenerateRoutes

    const permission = {
      state: {
        routers: constantRouterMap,
        addRouters: []
      },
      mutations: {
        SET_ROUTERS: (state, routers) => {
          state.addRouters = routers
          state.routers = constantRouterMap.concat(routers)
        }
      },
      actions: {
        GenerateRoutes({ commit }, data) {
          return new Promise(resolve => {
            const { roles } = data
            // 权限对列表进行过滤
            const accessedRouters = filterAsyncRouter(asyncRouterMap, roles)
            commit('SET_ROUTERS', accessedRouters)
            resolve()
          })
        }
      }
    }

    对于通用目录我们不需要管,在原逻辑中,这里通过判断如果用户是管理员就直接把整个权限目录都加载,如果不是再进行过滤,因为我们这完全就通过后台控制,包括管理员所以就把判断去掉了,直接对目录进行过滤

    找到 filterAsyncRouter 方法

    /**
     * 递归过滤异步路由表,返回符合用户角色权限的路由表
     * @param routes asyncRouterMap
     * @param roles
     */
    function filterAsyncRouter(routes, roles) {
      const res = []
      routes.forEach(route => {
        const tmp = { ...route }
        if (hasPermission(roles, tmp)) {
          // 是否存在子节点,存在子节点说明当前节点为展开栏,并非页面
          if (tmp.children) {
            tmp.children = filterAsyncRouter(tmp.children, roles)
            // forCheck() 方法递归判断该节点下是否存在页面,不存在则隐藏
            // true:不存在,false:存在
            tmp.hidden = forCheck(tmp.children)
          }
          res.push(tmp)
        }
      })
    
      return res
    }
    
    /**
     * 递归子节点,判断是否存在展示页面,存在返回 false,不存在返回 true
     * @param routes
     */
    function forCheck(routes) {
      // 设置默认为隐藏
      let isHidden = true
      // 判断是否存页面,不存在说明该节点下不存在页面
      if (routes != null && routes.length > 0) {
        // 循环子目录,如果子目录中不存在需要权限页面
        // 说明子页面全是展开栏,隐藏
        for (const route of routes) {
          // 存在 permission 说明为页面,不存在说明为展开栏,将子页面列表继续递归
          if (route.meta && route.meta.permission) {
            isHidden = false
            return
          } else {
            isHidden = forCheck(route.children)
          }
        }
      }
      return isHidden
    }

    通过循环权限目录通过 hasPermission 方法进行判断
    forCheck 是我自己封装的方法,因为项目中不只存在二级目录,所以,通过递归的方式,判断子节点下是否有展示页,也就是带 permission 的页面,如果有,返回 false,没有返回 true

    /**
     * 通过meta.role判断是否与当前用户权限匹配
     * @param roles
     * @param route
     */
    function hasPermission(roles, route) {
      if (route.meta && route.meta.permission) {
        return roles.some(role => route.meta.permission.includes(role.permission))
      } else {
        return true
      }
    }

    hasPermission 方法,这里,我们之前加的 permission 参数就起到作用了
    如果有 permission 就进行判断, roles 参数就是 /src/permission 里传过来的 module 目录。
    通过 roles.some 循环 roles 别名 role,如果目录里 includes(包含)这个目录权限,就返回true,否则 false,为 true 的会被显示,当然如果直接没有 permission 就说明不需要后台管控,就直接 true,这样目录就被加载出来了。

    展开全文
  • 主要介绍了解决vuecli3中img src 的引入问题,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
  • <!DOCTYPE html> <html lang="en"> <...表单控件处理之使用vue控制radio表单的实例操作 </title> <script src="vue.js&q
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>表单控件处理之使用vue控制radio表单的实例操作 </title>
        <script src="vue.js"></script>
    </head>
    <body>
    <div id="lantian">
        <div>{{field.type}}</div>
        <input type="radio" v-model="field.type" value="draft">草稿
        <input type="radio" v-model="field.type" value="send">发布
    
    </div>
    <script>
        var app = new Vue({
            el: '#lantian',
            data: {
                field: {
                    type:'send',
                }
            }
        });
    </script>
    
    </body>
    </html>
    

      

    展开全文
  • vue控制列表的字数限制

    千次阅读 2019-06-25 16:07:11
    <template lang="pug"> div.radar ul.radar__list li.radar__item(v-for="item in controlTextList" :id="item.id") img.img(:src="item.imgSrc") div.item ...
    <template lang="pug">
      div.radar
          ul.radar__list
            li.radar__item(v-for="item in controlTextList" :id="item.id")
              img.img(:src="item.imgSrc")
              div.item
                p.title {{item.title}}
                  span.icon(:style="{'background-image': 'url('+item.back+')'}")
                p.number
                  span.browse {{item.browse}}
                  span.click(:class="item.share?'share':''") {{item.share}}
                div.item-following(v-if="item.portraits.length > 0")
                  div.portraits
                    img.portrait(:src="portrait" v-for="portrait in item.portraits")
                    span.numbers {{item.numbers}}
                  RouterLink.view(:to="{name: 'browse-details'}") 查看
    
    </template>
    export default {
      name: 'test',
      data () {
        return {
          totalList: {
            totalViews: '2222',
            totalShare: '721',
            list: [ // 列表
              {
                id: 0,
                imgSrc: 'http://source.haichuanshiye.cn/upload/2019/06/12/201906125d009c9e00126887243520.jpg', // 左边img
                title: '踏上时间的列车往回忆边缘开穿过日升月落开往春去冬来我愿翻山越岭', // 标题
                browse: '22次', // 浏览次数
                share: true, // 是分享true,点击false
                shareNum: '22次', // 分享次数
                portraits: [ // 浏览人的头像
                  'http://source.haichuanshiye.cn/upload/2018/10/16/201810165bc5453381b2c608749748.jpg',
                  'http://source.haichuanshiye.cn/upload/2018/10/16/201810165bc5453381b2c608749748.jpg',
                  'http://source.haichuanshiye.cn/upload/2018/10/16/201810165bc5453381b2c608749748.jpg'
                ],
                numbers: '等6人浏览', // 浏览量
                back: require('../../assets/icon/radar/yz.png')
              },
              {
                id: 1,
                imgSrc: 'http://source.haichuanshiye.cn/upload/2019/06/12/201906125d009c9e00126887243520.jpg',
                title: '踏上时间的列车往回忆边缘开',
                browse: '22次',
                share: false,
                shareNum: '2次',
                portraits: [
                  'http://source.haichuanshiye.cn/upload/2018/10/16/201810165bc5453381b2c608749748.jpg',
                  'http://source.haichuanshiye.cn/upload/2018/10/16/201810165bc5453381b2c608749748.jpg'
                ],
                numbers: '等6人浏览',
                back: require('../../assets/icon/radar/wd.png')
              },
              {
                id: 2,
                imgSrc: 'http://source.haichuanshiye.cn/upload/2019/06/12/201906125d009c9e00126887243520.jpg',
                title: '我愿翻山越岭',
                browse: '22次',
                share: true,
                shareNum: '222次',
                portraits: [
                  'http://source.haichuanshiye.cn/upload/2018/10/16/201810165bc5453381b2c608749748.jpg'
                ],
                numbers: '等6人浏览',
                back: require('../../assets/icon/radar/tg-xcx.png')
              },
              {
                id: 3,
                imgSrc: 'http://source.haichuanshiye.cn/upload/2019/06/12/201906125d009c9e00126887243520.jpg',
                title: '只为遇见你只为遇见你只为遇见你只为遇见你只为遇见你只为遇见你只为遇见你',
                browse: '22次',
                share: false,
                shareNum: '22222次',
                portraits: [],
                numbers: '等6人浏览',
                back: require('../../assets/icon/radar/tg-sp.png')
              }
            ]
          }
        }
      },
      created () {
        this.sortCurrent = 'all'
      },
      computed: {
        // 限制标题17个字后面的点点 
        controlTextList () {
          let list = this.totalList.list
          list.forEach((item, index) => {
            if (item.title.length > 17) { // 限制字数17个字
              list[index].title = item.title.slice(0, 17) + '...'
            }
          })
          return this.totalList.list
        }
      },
      methods: {
        
      }
    }
    展开全文
  • 使用vue控制video视频和弹幕功能

    千次阅读 2020-01-19 16:38:59
    2020-02-19 前两天想说练一下vue,就按照bilibili写了一...目录先放一下最终效果一、vue控制视频播放1. 引入视频2. 播放 / 暂停功能3. 获取并及时更新视频时间4. 播放完成 / 重放二、弹幕功能1. 添加弹幕容器2. 弹幕...
    • 2020-02-19
      前两天想说练一下vue,就按照bilibili写了一个demo(我第一次放这种模仿的页面,如果有哪里不合适的请告诉我哈),就写了比较核心的首页和视频播放页,包括控制视频和弹幕渲染等等的,记录一下。



    先放一下最终效果

    在这里插入图片描述
    因为当时做的时候为了追求效果打的真实的名字,就手动打了一下码


    一、vue控制视频播放


    1. 引入视频

    这个非常简单,就不用我说了吧~

    <video id="video" src="video/video-horizontal.mp4" ref="video">
    	您的浏览器不支持 video 标签。
    </video>
    

    2. 播放 / 暂停功能

    因为没有我们想手动控制视频的暂停或者播放,所以没有加自动播放属性,并且我们要绑定事件来控制视频。

    <!-- 开始 / 暂停按钮 -->
    <div class="play" @click="playOrPause()">
    	<img v-if="!isPlay" src="img/goPlay.png" />
    	<img v-cloak v-else src="img/pause.png" />
    </div>
    
    //  开始/ 暂停事件
      	playOrPause: function(){
      		if(this.isPlay){
      			$("#video").trigger("pause");
      		}else{
      			$("#video").trigger("play");	
      		}
      		this.isPlay = !this.isPlay;
      	}
    

    没有加自动播放属性的视频默认情况下是在暂停状态的,所以isPlay的默认值是false。

    data: {
       	isPlay: false
    }
    

    3. 获取并及时更新视频时间

    //  获取总时长
       	videoCanPlay: function(){
    	    var duration =this.$refs.video.duration;
    	    this.vcTotalTime = this.getFormatVideoTime(duration);
       	},
    //  更新进度时间
       	videoTimeUpdate: function(){
    	   	var currTime =this.$refs.video.currentTime;
    	   	var duration =this.$refs.video.duration;
    	   	this.vcCurrentTime = this.getFormatVideoTime(currTime);
    	   	var pre = currTime / duration;
    	   	this.vcProgress = pre*100;
       	},
    //  格式化时间
       	getFormatVideoTime: function(time) {
               var time = time;
               var m = parseInt(time%3600/60),
                   s = parseInt(time%60);
               m = m < 10 ? "0"+m : m;
               s = s < 10 ? "0"+s : s;
               return m+":"+s;
    	}
    
    <video id="video"  src="video/video-horizontal.mp4" ref="video"
    	   @canplay="videoCanPlay()" 
    	   @timeupdate="videoTimeUpdate()" >
    	您的浏览器不支持 video 标签。
    </video>
    

    然后在进度条和时间上也要展示最新的状态,所以这里也绑定上。

    <div class="progress">
    	<span class="alreadyRead" :style="{width: vcProgress*100/100 +'%'}"></span>
    	<img @mousedown="move" :style="{left: vcProgress*100/100 +'%'}" src="img/progress.png" />
    	<span class="unRead"></span>
    </div>
    <p v-cloak class="time">{{ vcCurrentTime }} / {{ vcTotalTime }}</p>
    

    这里绑定的move事件是用来拖动进度条调整时间的。
    (另外在电脑上模拟手机端测试的话是不好用的,测试的话要切成PC才可以,这里贴一下供大家参考哈。)

    //  拖动进度条
        move(e){
          	let odiv = e.target;    //获取目标元素
          	//算出鼠标相对元素的位置
          	let disX = e.clientX - odiv.offsetLeft;
          	let disY = e.clientY - odiv.offsetTop;
          	document.onmousemove = (e)=>{    //鼠标按下并移动的事件
    	        //用鼠标的位置减去鼠标相对元素的位置,得到元素的位置
    	        let left = e.clientX - disX;  
    	        let top = e.clientY - disY;
    	        //绑定元素位置到positionX和positionY上面
    	        this.positionX = top;
    	        this.positionY = left;
    	        //移动当前元素
    	        odiv.style.left = left + 'px';
    	        odiv.style.top = top + 'px';
          	};
          	document.onmouseup = (e) => {
    	        document.onmousemove = null;
    	        document.onmouseup = null;
          	};
        }
    

    4. 播放完成 / 重放

    播放完成后的效果图先贴一下

    在这里插入图片描述

    //	播放完成
    	videoEnd: function(){
    		$(".player-ended").css('display',"flex");
    	},
    //	重播
    	replay: function(){
    		$(".player-ended").hide();
    		$("#video").trigger("play");
    	}
    

    重播按钮上直接绑定replay就可以了,播放完成这样绑定

    <video id="video"  src="video/video-horizontal.mp4" ref="video"
    	   @canplay="videoCanPlay()" 
    	   @timeupdate="videoTimeUpdate()" 
    	   @ended="videoEnd()">
    	您的浏览器不支持 video 标签。
    </video>
    


    二、弹幕功能


    1. 添加弹幕容器

    容器和video相对定位就可以了,就不贴代码了。

    <div class="player-barrage">
    	<ul class="roll"></ul>
    	<ul class="center"></ul>
    </div>
    

    2. 弹幕列表

    这里的弹幕分为滚动和居中两种,
    一般来说应该是在初始化的时候在接口获取的,我这里自己练习,就写成固定的了。
    canAdd:用来判断是否已经添加过了,已经添加过的弹幕不再重复添加了。
    time:弹幕出现时间。
    color:弹幕颜色。
    cont:弹幕内容。

    data:{
       	rollBarrageIndex: 0,		//用来固定位置的
       	rollBarrageList:[{		//滚动弹幕
       		canAdd: true,
       		time: '00:00',
       		color: '#fcf044',
       		cont: '1测试弹幕测试幕测试弹幕测试弹幕1'
       	},{
       		canAdd: true,
       		time: '00:00',
       		color: '#ffffff',
       		cont: '11测试弹幕测试弹幕测试弹幕幕测试弹幕测试弹幕测试弹幕测试弹幕测试弹幕1'
       	}],
        	
       	centerBarrageIndex: 0,
       	centerBarrageList:[{		//顶部居中弹幕
       		canAdd: true,
       		time: '00:00',
       		color: '#ffffff',
       		cont: '00000000000000000000000'
       	},{
       		canAdd: true,
       		time: '00:00',
       		color: '#e82624',
       		cont: '1111111111111111111111111111111111111111111111111'
       	}]
    }
    

    3. 在页面中渲染弹幕

    在这里先说下哈,bilibili的APP的弹幕相关功能真的是很非常完善的,要是用H5做很麻烦,
    比如暂停视频时弹幕应该也跟随暂停,这个功能使用canvas更方便做出来,
    在这里因为只是练习demo,弹幕的动效和渲染我都是用定时器做的,没办法完成到对应暂停,只做了些基础的功能。

    //	更新滚动弹幕
        updataRollBarrage: function(){
       		var _this = this;
       		$(_this.rollBarrageList).each(function(i,item){
       			//当符合出现弹幕时间,并且弹幕没有被添加过时,添加弹幕
       			if(item.time == _this.vcCurrentTime && item.canAdd){
       				//弹幕要固定top是因为清除已经展示过的弹幕会让后面的弹幕往上移
       				$(".player-barrage .roll").append("<li style='top: " + _this.rollBarrageIndex*21 + "px; color: " + item.color + ";'><p>" + item.cont + "</p></li>");
       				//选中当前添加的弹幕
       				var obj = $(".player-barrage .roll li").last();
       				//当前弹幕匀速向左移动
       				var speed = obj.width() * 7;
       				$(obj).find('p').animate({left:"-100%"}, speed, "linear",function(){
       					//这个时候弹幕已经跟屏幕左对齐了,然后再让父元素移出屏幕
       					//不用之前的speed是为了和之前的速度保持一致
       					var speedBox = $(obj).parent().width() * 7; 
       					$(obj).animate({left:"0%"}, speedBox, "linear",function(){
       						//父元素也移出屏幕后,可以清除当前弹幕
       						$(obj).remove();       						
       					})
       				});
       				//我这里是默认展示五行滚动弹幕
       				//如果想根据同一时间弹幕数量的多少决定展示几行弹幕,可以添加当前时间的判断,但是在demo里面我就不写这么复杂了
       				_this.rollBarrageIndex = (_this.rollBarrageIndex >= 4) ? (_this.rollBarrageIndex - 4) : (_this.rollBarrageIndex + 1);
       				//已添加过的弹幕不再添加了
       				item.canAdd = false;
       				//终止each循环
       				return false;
       			}
       		})
       	},
    //	更新居中弹幕
        updataCenterBarrage: function(){
        	//大部分逻辑和滚动弹幕一样,动效直接在css里面写,所以这里只要定时清除就可以了
       		var _this = this;
       		$(_this.centerBarrageList).each(function(i,item){
       			if(item.time == _this.vcCurrentTime && item.canAdd){
       				$(".player-barrage .center").append("<li style='top: " + _this.centerBarrageIndex*21 + "px; color: " + item.color + ";'><p>" + item.cont + "</p></li>");
       				var obj = $(".player-barrage .center li").last();
       				//我这里设置3秒后自动清除
       				//如果想根据内容多少来判断展示时间,获取一下宽度,乘以一个系数就可以了,别忘了设置最短时间
    				setTimeout(function(){
     						$(obj).remove(); 
    				},3000)
       				_this.centerBarrageIndex = (_this.centerBarrageIndex >= 4) ? (_this.centerBarrageIndex - 4) : (_this.centerBarrageIndex + 1);
       				item.canAdd = false;
       				return false;
       			}
       		})
       	}
    

    然后这两个方法都在更新进度时间中调用就可以了

    //  更新进度时间
       	videoTimeUpdate: function(){
        	var currTime =this.$refs.video.currentTime;
        	var duration =this.$refs.video.duration;
        	this.vcCurrentTime = this.getFormatVideoTime(currTime);
        	var pre = currTime / duration;
        	this.vcProgress = pre*100;
        	//调用渲染弹幕方法
        	this.updataRollBarrage();
        	this.updataCenterBarrage();
    	}
    

    然后在重放的时候,也别忘了重新设置可以被添加

    //	重播
    	replay: function(){
    		console.log("replay");
    		$(".player-ended").hide();
    		$("video").trigger("play");	
    		//清除当前屏幕上的弹幕
    		$(".player-barrage li").remove();
    		//可重新添加弹幕
    		$(this.rollBarrageList).each(function(i,item){
    			item.canAdd = true;
    		})
    		$(this.centerBarrageList).each(function(i,item){
    			item.canAdd = true;
    		})
    	}
    


    展开全文
  • Vue路由权限控制解析

    2021-01-18 17:12:20
    实际开发项目中,关于登录和路由权限的控制参照了vue-element-admin这个明星项目,并在此基础上基于业务进行了整合,接下来我会以这个项目为例,仔细地剖析整个路由和权限校验的过程,也算是对这个知识点的一些总结...
  • VUE

    千次阅读 多人点赞 2018-12-04 16:53:47
    VUE学习vue-router安装vue-router编写vue-router的配置文件使vue-router配置生效配置多级路由 vue-router vue-router是用作前端路由的。...在src目录下新建一个router的配置文件router.js (名字自定义): ...
  • Vue

    2020-06-02 22:06:21
    控制块隐藏 v-if:不渲染dom v-show:display:none控制隐藏 渲染dom 渲染循环列表 v-for 事件绑定 v-on:click @click 属性绑定 v-bind:src v-bind:class :src :class new一个vue对象 需要设置它的属性, data vue对象...
  • 点题::src需要使用require() 直接上代码 如果当前路由等于这个图片对应的菜单就让图片显示激活,否则就显示未激活的 <el-menu-item index="1"> <template slot="title"> <img :src=" ...
  • 注意:vue-router是无法完全控制前端路由权限。 1、实现思路 使用vue-router实例函数addRoutes动态添加路由规则,不多废话直接上思维导图: 2、实现步骤 2.1、路由匹配判断 // src/router.js import Vue from '...
  • vue中实现登录控制

    千次阅读 2018-03-02 14:47:12
    vue中使用vue-router实现登录的控制在做后台管理系统中很常见,但是不想之前熟悉的流程那样,不过只要大家理解vue-router的使用也是很好实现的。首先我们需要编写登录页面和主页面:&lt;template&gt; &...
  • vue src动态加载请求获取的图片

    千次阅读 2018-09-20 17:52:04
    https://blog.csdn.net/u012612399/article/details/80364493
  • Vue img中src地址无法访问时处理

    千次阅读 2019-01-07 15:37:26
    直接利用img原生属性onerror来设置默认图片 &lt;img class="room_img" onerror="javascript:this.src='@/assets/image/...可是这样存在一个问题,如果默认图片也访问不到,src就会在后台给的地...
  • HTML的img标签的src是有两种, 绝对路径/相对路径, 相对路径可以转成绝对路径 比如: http(s)????/www.baidu.com/test.jpg 前面http(s)????/www.baidu.com 指明那一台Web服务器, /test....那么问题是为什么到了Vue里面...
  • 一共用到了两种方式: 第一种:对象 第二种:数组 <!... <... ...vue.js 多种方式控制class属性 </title> <script src="vue.js"></script> <script src="node_modules/axios/d
  • Vue保持用户登录及权限控制

    千次阅读 2019-05-20 17:59:10
    vue使用vue-router和vuex对用户进行登录状态控制及用户权限控制
  • 文章目录1. 一张图搞懂按钮权限逻辑1. 第一步:创建src/directives/permission.js2....在vue全局挂载方法$_has,主要实现思路是将页面的按钮英文名传入,用以对比目前是否在权限数组里面 方法定义好以后在m
  • 结合Vue控制字符和字节的显示个数

    千次阅读 2017-10-23 00:05:30
    需求:结合Vue实现下面的效果 输入框中最多输入16个字符 汉字最多显示5个,超出部分以...显示 英文最多显示10个,超出部分以...显示 实现 搭建简单页面,并设置简单样式 在正式开始写核心代码之前,要先把代码结构...
  • vue+vue-video-player+RTMP+动态修改src流地址 最近为了学习srs转码用于直播,先学习搭建了一个直播网页,采用的是vue和element-ui,其中如果引入rtmp播放器让我困惑了许久,看了很多别人的博客,试了很多都不行,...
  • vue控制代码执行顺序之$nextTick()(三)

    千次阅读 2019-04-01 10:43:21
    vue 有一个重要的概念:异步更新队列。Vue在观察到数据时并不是直接更新DOM,而是开启一个队列,并缓冲在同一个事件循环中发生的所有数据变化。在缓冲时会去除重复数据,从而避免不必要的计算和DOM操作。 如下:通过...
  • vue中动态控制标签属性

    千次阅读 2020-09-29 10:28:03
    <body> <div id="app">...img v-bind:src="src" alt="" v-on:mouseenter="change()"> <div v-bind:class="msg" v-on:mouseenter="c()" v-on:mouseleave="l()"></div> </d.
  • vue权限控制路由(vue-router 动态添加路由)

    万次阅读 多人点赞 2018-09-13 15:13:36
    import Vue from 'vue' import {router} from './index' import login from '@/views/login/login' import layout from '@/views/layout/layout' import home from '@/views/home/home.vue' import depDsStorageList...
  • Vue-element-admin模板项目的src/permission.js文件中,给出了路由守卫、加载动态路由的实现方案,在实现了基于不同角色加载动态路由的功能。我们只需要稍作改动,就能将基于角色加载路由改造为基于权限加载路由。...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 25,874
精华内容 10,349
关键字:

vue控制src

vue 订阅