美文网首页
vue开发一款h5弹幕项目

vue开发一款h5弹幕项目

作者: 前端蜗牛老师 | 来源:发表于2023-03-15 22:10 被阅读0次

    两天时间用vue写一个手持弹幕,(没有完成所有功能)

    先看一下效果

    视频演示

    这里使用vue 写的,只支持vue语法。后期有时间会考虑写成npm 插件。欢迎大神PR/

    有两个组件 , 贴一下代码,如果不懂,欢迎留言

    第一个

    vue-bullet-chat.vue
    
    
    
    <template>
      <div class="vue-bullet-chat-wrapper" :style="background" @click="bulletChatClick">
        <div class="vbc-top">
          <div class="vbc-lock-wrapper">
            <svg t="1612009086242" class="vbc-icon-svg vbc-icon-open vbc-show-active" viewBox="0 0 1223 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="11149" width="128" height="128"><path d="M874.207389 0.2134C694.712336 6.861365 556.767063 159.764558 556.767063 337.59762v119.663369h-498.59737C26.59186 458.92298 0 483.852849 0 515.430682v452.061616c0 31.577833 26.59186 56.507702 56.507702 56.507702h731.276143c31.577833 0 56.507702-26.59186 56.507702-56.507702V515.430682c0-31.577833-26.59186-56.507702-56.507702-56.507702H698.036318v-127.973325C698.036318 221.258234 792.769819 133.172698 904.123231 143.144646c98.057483 9.971947 169.523106 94.7335 169.523106 192.790983v187.805009c0 16.619912 13.29593 29.915842 29.915843 29.915843h81.43757c16.619912 0 29.915842-13.29593 29.915842-29.915843V329.287664C1216.577584 143.144646 1062.012399-6.434565 874.207389 0.2134z" p-id="11150"></path></svg>
            <svg t="1612009127525" class="vbc-icon-svg vbc-icon-close" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="12557" width="128" height="128"><path d="M842.224081 429.259288h-72.934698V258.894547C769.289383 116.096916 653.9801 0 511.9701 0 370.038864 0 254.572054 116.096916 254.572054 258.815783v170.522268H181.71612A63.798169 63.798169 0 0 0 118.15424 493.214983v466.671795c0 35.443427 28.433505 64.034459 63.56188 64.034459h660.586724a63.719406 63.719406 0 0 0 63.483117-64.034459V493.214983a63.798169 63.798169 0 0 0-63.56188-63.876932z m-294.574264 309.539266v101.840781a8.427659 8.427659 0 0 1-8.270133 8.42766h-54.661641a8.427659 8.427659 0 0 1-8.42766-8.42766V738.798554A79.393277 79.393277 0 0 1 511.9701 588.439658a79.393277 79.393277 0 0 1 35.75848 150.280133z m122.870549-309.539266H353.498598V263.068995c0-87.8997 71.201908-159.574187 158.629029-159.574187 87.427121 0 158.550265 71.674487 158.550265 159.574187v166.190293z" p-id="12558"></path></svg>
          </div>
    
        </div>
        <div class="vbc-text-wrapper vbc-flex-sb-column">
          <div class="vbc-text" id="vbcTextInput" :style="textStyle">
            <span :class="textClass">{{value}}</span>
          </div>
        </div>
        <div class="vbc-input-wrapper vbc-flex-sb vbc-input-wrapper-active" @click.stop>
          <div class="vbc-flex-sb">
            <input class="input-box" ref="inputBox" v-model="setValue" placeholder="请输入弹幕文字" @focus="handleFocus" @keypress.enter="handleInput" />
            <div class="vbc-close-box">
              <svg t="1612167727491" class="vbc-close-icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1694" width="128" height="128"><path d="M512 64a448 448 0 1 1 0 896A448 448 0 0 1 512 64zM408.576 363.136a32 32 0 1 0-45.312 45.248l103.808 103.744-103.808 103.744a32 32 0 1 0 45.312 45.248l103.744-103.68 103.744 103.68a32 32 0 1 0 45.248-45.248l-103.744-103.68 103.744-103.808a32 32 0 0 0-45.248-45.248L512.32 466.88z" fill="#B8B8B8" p-id="1695"></path></svg>
            </div>
          </div>
          <div class="svg-box" @click="handlePopup">
            <svg t="1611985197202" class="vbc-icon-svg" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"
                 p-id="5543" width="128" height="128">
              <path d="M661.3 250.7H138.7c-41.2 0-74.7 33.4-74.7 74.7v560c0 41.2 33.4 74.7 74.7 74.7h522.7c41.2 0 74.7-33.4 74.7-74.7v-560c-0.1-41.3-33.5-74.7-74.8-74.7z m18.7 616c0 20.6-16.7 37.3-37.3 37.3H157.3c-20.6 0-37.3-16.7-37.3-37.3V344c0-20.6 16.7-37.3 37.3-37.3h485.3c20.6 0 37.3 16.7 37.3 37.3v522.7z"
                    p-id="5544" fill="#ffffff"></path>
              <path d="M596 624h-37.3c-15.4 0-28 12.6-28 28s12.6 28 28 28H596c15.4 0 28-12.6 28-28s-12.6-28-28-28z m0-93.3h-93.3c-15.4 0-28 12.6-28 28s12.6 28 28 28H596c15.4 0 28-12.6 28-28s-12.6-28-28-28z m0-93.4H465.3c-15.4 0-28 12.6-28 28s12.6 28 28 28H596c15.4 0 28-12.6 28-28s-12.6-28-28-28z m-297 0l-123 336h77.7l24.7-77h122.1l25.2 77h78.1l-120.6-336H299zM294 641l44.3-145.8h2L383.6 641H294z"
                    p-id="5545" fill="#ffffff"></path>
              <path d="M885.3 64H418.7c-41.1 0-74.7 33.6-74.7 74.7v46.7c0 15.5 12.5 28 28 28s28-12.5 28-28v-28c0-20.5 16.8-37.3 37.3-37.3h429.3c20.5 0 37.3 16.8 37.3 37.3V680c0 20.5-16.8 37.3-37.3 37.3h-65.3c-15.5 0-28 12.5-28 28s12.5 28 28 28h84c41.1 0 74.7-33.6 74.7-74.7v-560c0-41-33.6-74.6-74.7-74.6z"
                    p-id="5546" fill="#ffffff"></path>
            </svg>
          </div>
        </div>
        <vue-bullet-chat-popup
                :vbc-popup-vis-able.sync="vbcPopupVisAble"
                :animation="animation"
                @effect="getEffect"
                @color="getColor"
                @speed="getSpeed"
                @fontSize="getFontSize"
        />
      </div>
    </template>
    
    <script>
      import VueBulletChatPopup from "./vue-bullet-chat-popup";
      export default {
        name: "vue-bullet-chat",
        components: { VueBulletChatPopup },
        props: {
          background: {
            type: Object,
            default: function () {
              return {
                backgroundColor: 'black'
              }
            }
          },
          textObj: {
            type: Object,
            default: function () {
              return {
                transform: 'rotate(90deg)',
                letterSpacing: '8px'
              }
            }
          }
        },
        data() {
          return {
            el: '',
            textWrapper: '',
            innerHeight: '', // 屏幕高度
            i: 0,
            timer: null,
            initBottom: '',
            initValue: '', // 初始距离底部的值
            initBottomCopy: '',
            value: '请输入文字显示文字弹幕', // 设置值
            clickFlag: false,
            inputWrapper: false,
            isLock: false,
            setValue: '',
            lockClose: '',
            lockOpen: '',
            lockWrapper: '',
            closeTimer: null,
            initTransform: '',
            reqAnFrame: '',
            inputBox: '',
            closeIcon: '',
            vbcPopupVisAble: false,
            animation: '',
            color: 'white',
            fontSize: '48px',
            textClass: '',
            speed: 2,
            requestAnimationFrame: '',
            cancelAnimationFrame: ''
          }
        },
        watch: {
          setValue() {
            if(!this.setValue) {
              this.utils.removeClass(this.closeIcon, 'vbc-close-active')
            }else {
              this.utils.addClass(this.closeIcon, 'vbc-close-active')
            }
          }
        },
        computed: {
          textStyle() {
            return {
              ...this.textObj,
              color: this.color,
              fontSize: this.fontSize,
              opacity: 0
            }
          }
        },
        mounted() {
          this.utils.fitIos()
          this.getEle()
          this.getText()
        },
    
    
        beforeDestroy() {
          window.cancelAnimationFrame(this.reqAnFrame)
        },
    
        methods: {
          getEle() {
            this.el = this.utils.classEle('vue-bullet-chat-wrapper')
            this.inputWrapper = this.el.getElementsByClassName('vbc-input-wrapper')[0]
            this.textWrapper = this.el.getElementsByClassName('vbc-text')[0]
            this.lockWrapper = this.el.getElementsByClassName('vbc-lock-wrapper')[0]
            this.lockClose = this.utils.classEle('vbc-icon-close')
            this.lockOpen = this.utils.classEle('vbc-icon-open')
            this.inputBox = this.utils.classEle('input-box')
            this.initTransform = this.utils.deepClone(this.textWrapper.style.transform)
            this.closeIcon = this.utils.classEle('vbc-close-box')
    
            // 设置锁
            this.lockWrapper.onclick = () => {
              if (!this.isLock) {
                this.isLock = true
                this.utils.hiddenClass('vbc-icon-open')
                this.utils.showClass('vbc-icon-close')
                this.utils.removeClass(this.inputWrapper, 'vbc-input-wrapper-active')
                this.utils.addClass(this.lockWrapper, 'lock-wrapper-active')
    
              }else {
                this.isLock = false
                this.utils.hiddenClass('vbc-icon-close')
                this.utils.showClass('vbc-icon-open')
              }
            }
    
            // 点击清空
    
            this.closeIcon.onclick = () => {
              this.setValue = ''
              this.$refs.inputBox.focus()
            }
    
            // this.init()
            // this.closeFun()
          },
    
          init() {
            this.textWrapper.style.opacity = '1'
            this.initBottom = Math.round(window.innerHeight / 2 + this.textWrapper.getBoundingClientRect().height / 2)
            this.initBottomCopy = this.utils.deepClone(this.initBottom)
            this.textWrapper.style.transform = 'translateY('+ this.initBottom + 'px)' + this.initTransform // 初始化文字位置
            this.move()
          },
    
          move() {
            this.initBottom -= this.speed
            this.textWrapper.style.transform = 'translateY('+ this.initBottom + 'px)' + this.initTransform // 初始化文字位置
            if ( this.initBottom <= -this.initBottomCopy ) {
              this.initBottom = this.initBottomCopy
              this.i = 0
            }
            this.reqAnFrame = window.requestAnimationFrame(this.move)
          },
    
          handleInput() {
            this.textWrapper.style.opacity = '0'
            window.cancelAnimationFrame(this.reqAnFrame)
            if(this.setValue) {
              this.value = this.setValue
            }else {
              this.value = '请输入文字显示文字弹幕'
              this.utils.removeClass(this.closeIcon, 'vbc-close-active')
            }
    
            this.$refs.inputBox.blur()
            this.bulletChatClick()
            setTimeout(() => {
              this.init()
            }, 100)
          },
    
          bulletChatClick() {
            if(!this.isLock) {
              if (!this.clickFlag) {
                this.utils.removeClass(this.closeIcon, 'vbc-close-active')
                this.utils.removeClass(this.inputWrapper, 'vbc-input-wrapper-active')
                this.utils.addClass(this.lockWrapper, 'lock-wrapper-active')
                this.clickFlag = true
              }else {
                this.utils.addClass(this.inputWrapper, 'vbc-input-wrapper-active')
                this.utils.removeClass(this.lockWrapper, 'lock-wrapper-active')
                this.clickFlag = false
              }
            }else {
              if (!this.clickFlag) {
                this.clickFlag = true
                this.utils.addClass(this.lockWrapper, 'lock-wrapper-active')
                this.utils.hiddenClass('vbc-icon-open')
              }else {
                this.clickFlag = false
                this.utils.removeClass(this.lockWrapper, 'lock-wrapper-active')
                this.utils.showClass('vbc-icon-close')
              }
            }
            this.closeFun()
          },
          closeFun() {
            if(!this.clickFlag) {
              clearTimeout(this.closeTimer)
              this.closeTimer = setTimeout( () => {
                this.utils.removeClass(this.inputWrapper, 'vbc-input-wrapper-active')
                this.utils.addClass(this.lockWrapper, 'lock-wrapper-active')
                this.clickFlag = true
              }, 3000)
            }
          },
          handleFocus() {
            this.utils.addClass(this.closeIcon, 'vbc-close-active')
            clearTimeout(this.closeTimer)
            // this.clickFlag = true
            this.utils.addClass(this.inputWrapper, 'vbc-input-wrapper-active')
            this.utils.removeClass(this.lockWrapper, 'lock-wrapper-active')
          },
    
          handlePopup() {
            this.vbcPopupVisAble = true
          },
    
          getColor(v) {
            this.color = v
          },
    
          getEffect(v) {
            this.textClass = v
          },
    
          getFontSize(v) {
            this.textWrapper.style.opacity = '0'
            this.fontSize = v
            window.cancelAnimationFrame(this.reqAnFrame)
            setTimeout(() => {
              this.init()
            }, 100)
          },
    
          getText() {
            // this.color = this.utils.get('vbcColor') || this.color
            // this.textClass = this.utils.get('vbcEffect') || this.fontSize
            // this.speed = +this.utils.get('vbcSpeed') || +this.speed
            // this.fontSize = this.utils.get('vbcFontSize') || this.fontSize
          },
    
          getSpeed(v) {
            this.textWrapper.style.opacity = '0'
            this.speed = +v
            if(v === '0') {
              window.cancelAnimationFrame(this.reqAnFrame)
              this.reqAnFrame = null
            }else {
              window.cancelAnimationFrame(this.reqAnFrame)
              setTimeout(() => {
                this.init()
              }, 100)
            }
          }
        }
      }
    </script>
    <style scoped>
      .input-box::placeholder {
        font-size: 24px;
        line-height: 100px;
        vertical-align: baseline;
        color: blue;
      }
    </style>
    
    <style scoped>
      .vue-bullet-chat-wrapper {
        width: 100%;
        height: 100vh;
        margin: 0;
        padding: 0;
        overflow: hidden;
        position: relative;
        user-select: none;
      }
    
      .vue-bullet-chat-wrapper .vbc-text {
        white-space: nowrap;
        letter-spacing: 8px;
      }
      .vue-bullet-chat-wrapper .vbc-input-wrapper {
        width: 100%;
        position: absolute;
        bottom: 0;
        margin: 0 auto;
        padding-bottom: 20px;
        transition: all .3s cubic-bezier(0.175, 0.885, 0.32, 1.275);
        transform: translate(0, 120px);
      }
      .vue-bullet-chat-wrapper .vbc-input-wrapper-active {
        transition: all .3s cubic-bezier(0.175, 0.885, 0.32, 1.275);
        transform: translate(0, 0px);
      }
    
      .vue-bullet-chat-wrapper .svg-box {
        position: relative;
        width: 80px;
        height: 80px;
        background: #222;
        border-radius: 14px;
      }
    
      .vue-bullet-chat-wrapper .vbc-icon-svg {
        position: absolute;
        left: 15px;
        top: 15px;
        width: 50px;
        height: 50px;
      }
    
      .vue-bullet-chat-wrapper .input-box {
        width: 570px;
        display: block;
        height: 80px;
        background: #222;
        border: none;
        outline: none;
        border-radius: 6px;
        text-indent: 1em;
        color: white;
        font-size: 30px;
      }
    
      /*顶部*/
      .vue-bullet-chat-wrapper .vbc-top {
        position: absolute;
        top: 0;
        left: 0;
        width: 100%;
        height: 80px;
        fill: white;
        z-index: 10;
      }
      .vue-bullet-chat-wrapper .vbc-top .vbc-icon-svg {
        opacity: 0
      }
      .vue-bullet-chat-wrapper .vbc-top .vbc-icon-svg.vbc-show-active {
        opacity: 1;
      }
    
      .vbc-text-wrapper {
        position: absolute;
        top: 0;
        right: 0;
        bottom: 0;
        left: 0;
        width: 100%;
      }
      .vbc-lock-wrapper {
        height: 40px;
        transition: all .3s cubic-bezier(0.175, 0.885, 0.32, 1.275);
        transform: translate(0, 0px);
      }
      .vbc-lock-wrapper.lock-wrapper-active {
        transition: all .3s cubic-bezier(0.175, 0.885, 0.32, 1.275);
        transform: translate(0, -80px);
      }
      .vbc-close-box {
        position: relative;
      }
      .vbc-close-box .vbc-close-icon {
        position: absolute;
        top: -10px;
        right: 10px;
        width: 30px;
        height: 30px;
        opacity: 0;
        transition: all .3s;
      }
      .vbc-close-box.vbc-close-active .vbc-close-icon {
        opacity: 1;
      }
    </style>
    
    
    
    
    
    
    第二个弹框组件
    
    vue-bullet-chat-popup.vue
    
    
    
    <template>
      <transition name="bullet-chat-fade">
        <div v-show="vbcPopupVisAble" tabindex="-1" :style="style" class="bullet-chat-popup" @click.stop>
          <div class="bullet-chat-mask" @click="handleClick"></div>
          <transition name="bullet-chat-slide">
            <div class="bullet-chat-dialog" v-show="show" :style="dialogStyle">
              <div class="vbc-popup-top vbc-flex-sb">
                <div v-for="(item, index) in topTitle" :key="index" :class="{'vbc-popup-top-active': item === active}">
                  <span @click="handleClickTop(item)">{{item}}</span>
                </div>
              </div>
              <div class="bullet-chat-content">
                <div v-for="(item, index) in textOpt" :key="index" class="content-item">
                  <p class="bullet-chat-cont-title">{{item.header}}</p>
                  <div class="cont-item">
                    <span v-for="(spanItem, i) in item.selectOpt" :key="i" :class="{'one-span':item.type === 1, 'two-span': item.type === 2 || item.type === 3 || item.type === 4, active: spanItem.active, activeColor: spanItem.active }" :style="{background: spanItem.background}" @click="handleSpan(spanItem, index)">
                      {{spanItem.label}}
                    </span>
                  </div>
                </div>
              </div>
            </div>
          </transition>
        </div>
      </transition>
    </template>
    
    <script>
      export default {
        name: "VueBulletChatPopup",
        props: {
          vbcPopupVisAble: {
            type: Boolean,
            default: false
          },
          duration: {
            type: Number,
            default: 300
          }
        },
        data() {
          return {
            topTitle: ['文字', '背景', '音乐'],
            active: '文字',
            show: false,
            textOpt: [{
              header: '效果(多选)',
              type: 1,
              selectOpt: [{
                label: '阴影',
                value: 'vbc-text-shadow',
                active: false,
                background: '#3d3d3d'
              }, {
                label: '叠字',
                value: 'vbc-text-double',
                active: false,
                background: '#3d3d3d'
              }, {
                label: '闪烁',
                value: 'vbc-text-twinkle',
                active: false,
                background: '#3d3d3d'
              }, {
                label: '描边',
                value: 'vbc-text-stroke',
                active: false,
                background: '#3d3d3d'
              }, {
                label: '酷炫',
                value: 'vbc-text-masked',
                active: false,
                background: '#3d3d3d'
              }]
            }, {
              header: '字体颜色',
              type: 2,
              selectOpt: [{
                label: '',
                value: '1',
                active: true,
                background: "white",
              }, {
                label: '',
                value: '2',
                active: false,
                background: 'red'
              }, {
                label: '',
                value: '3',
                active: false,
                background: '#FD2E74'
              }, {
                label: '',
                value: '4',
                active: false,
                background: '#FCDE46'
              }, {
                label: '',
                value: '5',
                active: false,
                background: '#FD2E74'
              }, {
                label: '',
                value: '6',
                active: false,
                background: '#61FE4B'
              }, {
                label: '',
                value: '7',
                active: false,
                background: '#41A0FE'
              }, {
                label: '',
                value: '8',
                active: false,
                background: '#FC5727'
              }, {
                label: '',
                value: '9',
                active: false,
                background: '#8911FE'
              }, {
                label: '',
                value: '10',
                active: false,
                background: '#65FFC9'
              }]
            }, {
              header: '速度',
              type: 3,
              selectOpt: [{
                label: '静止',
                value: '0',
                active: false,
                background: '#3d3d3d'
              }, {
                label: '0.5x',
                value: '1',
                active: false,
                background: '#3d3d3d'
              }, {
                label: '1x',
                value: '2',
                active: true,
                background: '#3d3d3d'
              }, {
                label: '1.5x',
                value: '3',
                active: false,
                background: '#3d3d3d'
              }, {
                label: '2x',
                value: '4',
                active: false,
                background: '#3d3d3d'
              }]
            }, {
              header: '字号',
              type: 4,
              selectOpt: [{
                label: '24',
                value: '24',
                active: false,
                background: '#3d3d3d'
              }, {
                label: '36',
                value: '36',
                active: false,
                background: '#3d3d3d'
              }, {
                label: '48',
                value: '48',
                active: true,
                background: '#3d3d3d'
              }, {
                label: '64',
                value: '64',
                active: false,
                background: '#3d3d3d'
              }, {
                label: '72',
                value: '72',
                active: false,
                background: '#3d3d3d'
              }, {
                label: '120',
                value: '120',
                active: false,
                background: '#3d3d3d'
              }]
            }, {
              header: '字体',
              type: 5,
              selectOpt: [{
                label: '宋体',
                value: '宋体',
                active: false,
                background: '#3d3d3d'
              }, {
                label: '宋体',
                value: '宋体',
                active: false,
                background: '#3d3d3d'
              }]
            }]
          }
        },
        watch: {
          vbcPopupVisAble(v) {
            this.show = v
          }
        },
        computed: {
          style() {
            return {
              animationDuration: `${this.duration}ms`
            };
          },
          dialogStyle() {
            return {
              animationDuration: `${this.duration}ms`,
            }
          }
        },
        mounted() {
          this.textOpt = this.utils.get('textOpt', true) || this.textOpt
        },
        methods: {
          handleClickTop(v) {
            this.active = v
          },
          handleClick() {
            this.$emit('update:vbcPopupVisAble', false)
          },
          handleSpan(item, index) {
            this.textOpt[index].selectOpt.map(val => {
              if(val.active) {
                val.active = false
              }
            })
            if(!item.active) {
              item.active = true
            }
            if(index === 0) {
              this.$emit('effect', item.value)
              this.utils.set('vbcEffect', item.value)
            } else if(index === 1) {
              this.$emit('color', item.background)
              this.utils.set('vbcColor', item.background)
            }else if(index === 2) {
              this.$emit('speed', item.value)
              this.utils.set('vbcSpeed', item.value)
            }else if (index === 3) {
              this.$emit('fontSize', item.value + 'px')
              this.utils.set('vbcFontSize', item.value + 'px')
            }
            this.utils.set('textOpt', JSON.stringify(this.textOpt))
          },
        }
      }
    </script>
    
    
    
    
    
    
    还有一个util.js, 自己封装的工具方法
    
    
    
    
    class Utils {
      constructor() {
    
      }
      hasClass(ele, cls) {
        return !!ele.className.match(new RegExp('(\\s|^)' + cls + '(\\s|$)'))
      }
      addClass(ele, cls) {
        if (!this.hasClass(ele, cls)) ele.className += ' ' + cls
      }
      removeClass(ele, cls) {
        if (this.hasClass(ele, cls)) {
          const reg = new RegExp('(\\s|^)' + cls + '(\\s|$)')
          ele.className = ele.className.replace(reg, '')
        }
      }
      set(key, value) {
        localStorage.setItem(key, value)
      }
    
      get(key, isObj = false) {
        if(isObj) {
          return JSON.parse(localStorage.getItem(key))
        }else {
          return localStorage.getItem(key)
        }
      }
      showClass(cls) {
        cls ? document.getElementsByClassName(cls)[0].style.opacity = '1' : new Error('请输入类名')
      }
      hiddenClass(cls) {
        cls ? document.getElementsByClassName(cls)[0].style.opacity = '0' : new Error('请输入类名')
      }
      changeInnerText(cls, text) {
        document.getElementsByClassName(cls)[0].innerHTML = text
      }
    
      clickfu (to, cls) {
        //回调函数,to为点击对象
        to.setAttribute("class", cls);
        const siblings = to.parentNode.childNodes;
        for (let i = 0; i < siblings.length; i++)
          if (siblings[i].nodeType == 1 && siblings[i] != to) siblings[i].className = '';
      }
    
      formatSeconds(value) {
        if(!value) return '00:00'
        value = parseInt(value);
        let time;
        if (value > -1) {
          let hour = Math.floor(value / 3600);
          let min = Math.floor(value / 60) % 60;
          let sec = value % 60;
          let day = parseInt(hour / 24);
          if (day > 0) {
            hour = hour - 24 * day;
            time = day + "day " + hour + ":";
          } else if (hour > 0) {
            time = hour + ":";
          }else {
            time = "";
          }
          if (min < 10) {
            time += "0";
          }
          time += min + ":";
          if (sec < 10) {
            time += "0";
          }
          time += sec;
        }
        return time;
      }
    
      classEle(cls) {
        return  cls && document.getElementsByClassName(cls)[0]
      }
    
      deepClone(source) {
        if (source && typeof source !== 'object') {
          return JSON.parse(JSON.stringify(source))
        }
        const targetObj = source.constructor === Array ? [] : {}
        Object.keys(source).forEach(keys => {
          if (source[keys] && typeof source[keys] === 'object') {
            targetObj[keys] = deepClone(source[keys])
          } else {
            targetObj[keys] = source[keys]
          }
        })
        return targetObj
      }
    
      isIPHONE() {
        return navigator.userAgent.toUpperCase().indexOf('IPHONE') !== -1;
      }
    
      /**
       * 解决IOS:input框输入完成,键盘关闭后位置上移问题
       */
    
      fitIos() {
        const u = navigator.userAgent;
        let flag;
        let myFunction;
        let isIOS = !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/);
        if(isIOS){
          document.body.addEventListener('focusin', () => {  //软键盘弹起事件
            flag=true;
            clearTimeout(myFunction);
          })
          document.body.addEventListener('focusout', () => { //软键盘关闭事件
            flag=false;
            if(!flag){
              myFunction = setTimeout(function(){
    
                window.scrollTo({top:0,left:0,behavior:"smooth"})//重点  =======当键盘收起的时候让页面回到原始位置(这里的top可以根据你们个人的需求改变,并不一定要回到页面顶部)
    
              },200);
            }else{
              return
            }
          })
        }else{
          return
        }
      }
    
    
    }
    
    export default Utils
    
    

    感兴趣的可以到github上下载源码,但是开源不易,请点个小星星支持一下,谢谢啦😀😀😀

    源码

    线上演示

    最后祝大家,兔年大吉,事业牛气冲天

    相关文章

      网友评论

          本文标题:vue开发一款h5弹幕项目

          本文链接:https://www.haomeiwen.com/subject/cglrrdtx.html