zoukankan      html  css  js  c++  java
  • vue移动音乐app开发学习(三):轮播图组件的开发

    本系列文章是为了记录学习中的知识点,便于后期自己观看。如果有需要的同学请登录慕课网,找到Vue 2.0 高级实战-开发移动端音乐WebApp进行观看,传送门

    完成后的页面状态以及项目结构如下:

    一:创建轮播图组件slider.vue

    1:在src/base下新建base文件夹,然后创建silder.vue:

    <template>
      <div class="slider" ref="slider">
        <div class="slider-group" ref="sliderGroup">
          <slot>
          </slot>
        </div>
        <div class="dots">
          <span class="dot" :class="{active: currentPageIndex === index }" v-for="(item, index) in dots"></span>
        </div>
      </div>
    </template>
    
    <script type="text/ecmascript-6">
      import {addClass} from 'common/js/dom'
      import BScroll from 'better-scroll'
      export default {
        name: 'slider',
        props: {
          loop: {
            type: Boolean,
            default: true
          },
          autoPlay: {
            type: Boolean,
            default: true
          },
          interval: {
            type: Number,
            default: 4000
          }
        },
        data() {
          return {
            dots: [],
            currentPageIndex: 0
          }
        },
        mounted() {
          setTimeout(() => {
            this._setSliderWidth()
            this._initDots()
            this._initSlider()
    
            if (this.autoPlay) {
              this._play()
            }
          }, 20)
    
          window.addEventListener('resize', () => {
            if (!this.slider) {
              return
            }
            this._setSliderWidth(true)
            this.slider.refresh()
          })
        },
        activated() {
          if (this.autoPlay) {
            this._play()
          }
        },
        deactivated() {
          clearTimeout(this.timer)
        },
        beforeDestroy() {
          clearTimeout(this.timer)
        },
        methods: {
          _setSliderWidth(isResize) {
            this.children = this.$refs.sliderGroup.children
    
            let width = 0
            let sliderWidth = this.$refs.slider.clientWidth
            for (let i = 0; i < this.children.length; i++) {
              let child = this.children[i]
              addClass(child, 'slider-item')
    
              child.style.width = sliderWidth + 'px'
              width += sliderWidth
            }
            if (this.loop && !isResize) {
              width += 2 * sliderWidth
            }
            this.$refs.sliderGroup.style.width = width + 'px'
          },
          _initSlider() {
            this.slider = new BScroll(this.$refs.slider, {
              scrollX: true,
              scrollY: false,
              momentum: false,
              snap: true,
              snapLoop: this.loop,
              snapThreshold: 0.3,
              snapSpeed: 400
            })
    
            this.slider.on('scrollEnd', () => {
              let pageIndex = this.slider.getCurrentPage().pageX
              if (this.loop) {
                pageIndex -= 1
              }
              this.currentPageIndex = pageIndex
    
              if (this.autoPlay) {
                this._play()
              }
            })
    
            this.slider.on('beforeScrollStart', () => {
              if (this.autoPlay) {
                clearTimeout(this.timer)
              }
            })
          },
          _initDots() {
            this.dots = new Array(this.children.length)
          },
          _play() {
            let pageIndex = this.currentPageIndex + 1
            if (this.loop) {
              pageIndex += 1
            }
            this.timer = setTimeout(() => {
              this.slider.goToPage(pageIndex, 0, 400)
            }, this.interval)
          }
        }
      }
    </script>
    
    <style scoped lang="stylus" rel="stylesheet/stylus">
      @import "~common/stylus/variable"
    
      .slider
        min-height: 1px
        .slider-group
          position: relative
          overflow: hidden
          white-space: nowrap
          .slider-item
            float: left
            box-sizing: border-box
            overflow: hidden
            text-align: center
            a
              display: block
               100%
              overflow: hidden
              text-decoration: none
            img
              display: block
               100%
        .dots
          position: absolute
          right: 0
          left: 0
          bottom: 12px
          text-align: center
          font-size: 0
          .dot
            display: inline-block
            margin: 0 4px
             8px
            height: 8px
            border-radius: 50%
            background: $color-text-l
            &.active
               20px
              border-radius: 5px
              background: $color-text-ll
    </style>
    

      

    2:组件中会含有addclass和hasclass的操作,但是这里没有用jquery,而是用原生的js封装了这两个方法。在src/common/js下新建dom.js:

    export function hasClass(el, className) {
      let reg = new RegExp('(^|\s)' + className + '(\s|$)')
      return reg.test(el.className)
    }
    
    export function addClass(el, className) {
      if (hasClass(el, className)) {
        return
      }
    
      let newClass = el.className.split(' ')
      newClass.push(className)
      el.className = newClass.join(' ')
    }
    

    3:页面的数据是用jsonp跨域请求qq音乐上的数据。github地址:传送门。接下来我们在src/common/js下新建jsonp.js,用来封装公共的jsonp方法:

    import originJsonp from 'jsonp'
    
    export default function jsonp(url, data, option) {
      url += (url.indexOf('?') < 0 ? '?' : '&') + param(data)
    
      return new Promise((resolve, reject) => {
        originJsonp(url, option, (err, data) => {
          if (!err) {
            resolve(data)
          } else {
            reject(err)
          }
        })
      })
    }
    
    export function param(data) {
      let url = ''
      for (var k in data) {
        let value = data[k] !== undefined ? data[k] : ''
        url += '&' + k + '=' + encodeURIComponent(value)
      }
      return url ? url.substring(1) : ''
    }
    

      

     4:当我们异步请求数据的时候,一般都会有一些公共的参数,所以我们可以将这些公共的参数定义为常量。在src/api下新建config.js:

    export const commonParams = {
      g_tk: 1928093487,
      inCharset: 'utf-8',
      outCharset: 'utf-8',
      notice: 0,
      format: 'jsonp'
    }
    
    export const options = {
      param: 'jsonpCallback'
    }
    
    export const ERR_OK = 0
    

    4:定义获取轮播图数据的文件。在src/api下新建recommend.js:

    import jsonp from 'common/js/jsonp'
    import {commonParams, options} from './config'
    
    export function getRecommend() {
      const url = 'https://c.y.qq.com/musichall/fcgi-bin/fcg_yqqhomepagerecommend.fcg'
    
      const data = Object.assign({}, commonParams, {
        platform: 'h5',
        uin: 0,
        needNewCode: 1
      })
    
      return jsonp(url, data, options)
    }
    

    5:修改recommend.vue:

    <template>
      <div class="recommend" ref="recommend">
       <div class="recommend-content">
         <div v-if="recommends.length" class="slider-wrapper" ref="sliderWrapper">
          <slider>
            <div v-for="item in recommends">
              <a :href="item.linkUrl">
                 <img class="needsclick" @load="loadImage" :src="item.picUrl">
              </a>
            </div>
          </slider>
        </div>
      </div>
       </div>
    </template>
    
    <script type="text/ecmascript-6">
      import Slider from 'base/slider/slider'
      import {getRecommend} from 'api/recommend'
      import {ERR_OK} from 'api/config'
    
      export default {
        data() {
          return {
            recommends: []
          }
        },
        created() {
          this._getRecommend()
        },
        methods: {
          _getRecommend() {
            getRecommend().then((res) => {
              if (res.code === ERR_OK) {
                this.recommends = res.data.slider
              }
            })
          },
          loadImage() {
            if (!this.checkloaded) {
              this.checkloaded = true
              this.$refs.scroll.refresh()
            }
          }
        },
        components: {
          Slider
        }
      }
    </script>
    
    <style scoped lang="stylus" rel="stylesheet/stylus">
    @import "~common/stylus/variable"
    
      .recommend
        position: fixed
         100%
        top: 88px
        bottom: 0
        .recommend-content
          height: 100%
          overflow: hidden
          .slider-wrapper
            position: relative
             100%
            overflow: hidden
          .recommend-list
            .list-title
              height: 65px
              line-height: 65px
              text-align: center
              font-size: $font-size-medium
              color: $color-theme
            .item
              display: flex
              box-sizing: border-box
              align-items: center
              padding: 0 20px 20px 20px
              .icon
                flex: 0 0 60px
                 60px
                padding-right: 20px
              .text
                display: flex
                flex-direction: column
                justify-content: center
                flex: 1
                line-height: 20px
                overflow: hidden
                font-size: $font-size-medium
                .name
                  margin-bottom: 10px
                  color: $color-text
                .desc
                  color: $color-text-d
          .loading-container
            position: absolute
             100%
            top: 50%
            transform: translateY(-50%)
    </style>
    
  • 相关阅读:
    [Leetcode] Largest Rectangle in Histogram
    [Leetcode] Unique Binary Search Trees II
    [Leetcode] Remove Duplicates from Sorted List II
    [Leetcode] Container With Most Water
    [Leetcode] Trapping Rain Water
    [Jobdu] 题目1390:矩形覆盖
    [Leetcode] Integer to Roman
    [Leetcode] Word Break
    Notes on Convolutional Neural Networks
    lecture5-对象识别与卷积神经网络
  • 原文地址:https://www.cnblogs.com/momozjm/p/7268558.html
Copyright © 2011-2022 走看看