本系列文章是为了记录学习中的知识点,便于后期自己观看。如果有需要的同学请登录慕课网,找到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>