zoukankan      html  css  js  c++  java
  • vue音乐项目歌手详情页小结

    技术栈

    1,vue
    2,vuex
    3,vue-router(子路由)
    需求分析

    1)歌手列表点击歌手会跳转到下级页面歌手详情页,歌手详情页由四个部分组成

    歌手图片
    返回按钮:点击返回歌手tab页
    随机播放按钮
    歌手歌曲滚动
    2)歌曲栏向上滚动到顶部时,图片和播放按钮随之隐藏,保留歌手名和返回按钮

    3)歌曲栏向下拖动时,歌手图片的高度作出无缝增高

    新的组件

    singer-detail.vue
    
    music-list.vue
    
    song-list.vue



    实现
    1.vuex 在介绍子路由的实现前我们先来看vuex

    这里写图片描述
    在src文件夹下新建一个store文件夹,该文件夹下有多个js文件

    actions.js涉及的异步操作
    
    getters.js store中定义的getter
    
    这里返回state里的singer
    
    export const singer = state => state.singer
    
    index.js 组装模块并导出
    
    mutation.js 跟级别的mutation
    //使用 ES2015 风格的计算属性命名功能来使用一个常量作为函数名
    import * as types from './mutation-types'
    const mutations = {
    [types.SET_SINGER](state, singer) {
    state.singer = singer
    }
    }
    export default mutations
    
    mutation-type.js 对mutation作相应的映射
    
    state.js 定义store的state

    1.子路由的实现
    router文件夹下index.js配置歌手页的子路由

    {
    path: '/singer',
    component: Singer,
    children: [
    {
    path: ':id',
    component: SingerDetail
    }
    ]
    }

    singer.vue中使用vue-router

    <template>
    <div class="singer" ref="singer">
    <listview :data = "singers" @select="selectSinger"></listview>
    <router-view></router-view>
    </div>
    </template>


    歌手页点击歌手时singer.vue 中通过@select接受子组件传递来的singer属性,@select="selectSinger"selectSinger方法根据singer.id实现子路由跳转

    selectSinger(singer) {
    this.$router.push({
    path: `/singer/${singer.id}`
    })
    this.setSinger(singer)
    }


    listview.vue歌手列表通过$emit方法传递出singer,这里的item代表每个singer

    selectItem(item) {
    this.$emit('select', item)
    //console.log('item:' + item)
    }

    2.歌手详情页的实现

    歌手详情页实现用了以下组件

    <singer-detail>
    <musci-list>
    ...
    <scroll>
    <song-list>...</song-list>
    <loading>...</loading>
    </scroll>
    </music-list>
    </singer-detail>

    在Vue Devtools中可以清晰看到模板的结构。
    singer.vue下有两个同级子组件分别为singer-detail和list-view,其中singer-detail组件是通过路由来切换的。
    singer-detail组件下有music-list组件,这是歌手详情页组件,该组件下有scroll组件,scroll组件实现滚动,scroll组件下的song-list组件是歌手歌曲列表组件,loading组件是优化组件,当歌手歌曲还没有获取时显示正在加载,从而提高用户体验。

    数据的获取与传递

    歌手对应歌曲数据获取
    src/api/singer.js通过getSingerDetail方法获取歌曲

    export function getSingerDetail(singerId) {
    const url = 'https://c.y.qq.com/v8/fcg-bin/fcg_v8_singer_track_cp.fcg'
    
    const data = Object.assign({}, commonParams, {
    hostUin: 0,
    needNewCode: 0,
    platform: 'yqq',
    order: 'listen',
    begin: 0,
    num: 80,
    songstatus: 1,
    singermid: singerId
    })
    
    return jsonp(url, data, options)
    }


    在singer-detail.vue组件中写了一个私有方法_getDetail用来获取歌手数据,singer.vue的data属性中维护了songs数组变量

    data() {
    return {
    songs: []
      }
    }
    _getDetail() {
    if (!this.singer.id) {
    this.$router.push('/singer')
    return
    }
    console.log(this.singer)
    getSingerDetail(this.singer.id).then((res) => {
    if (res.code === ERR_OK) {
    console.log(res.data.list)
    this.songs = this._normalizeSongs(res.data.list)
    }
    })
    }

    _getDetail方法中使用了_normalizeSongs方法对请求到的数据进行处理,因为jsonp请求返回到的数据不是我们想要的,所以需要用此方法对数据进行处理。对请求返回到的数据返回一个songs数组,数组的每个元素代表每个song,其中song是一个类,这个类里面保留着歌曲的相关信息,如歌曲id,歌手姓名等等。

    这是一个音乐app,后面还会涉及到歌曲的处理,所以出于代码可复用和可维护的角度来考虑,在common/js/song.js下创建了一个song Class,并创建了一个createSong工厂方法在传入参数后直接返回一个new song

    _normalizeSongs(list) {
    let ret = []
    list.forEach((item) => {
    let {musicData} = item
    if (musicData.songid && musicData.albummid) {
    ret.push(createSong(musicData))
    }
    })
    return ret
    }



    common/js/song.js

    export default class Song {
    constructor({id, mid, singer, name, album, duration, image, url}) {
    this.id = id
    this.mid = mid
    this.singer = singer
    this.name = name
    this.album = album
    this.duration = duration
    this.image = image
    this.url = url
    }
    }
    export function createSong(musicData) {
    return new Song({
    id: musicData.songid,
    mid: musicData.songmid,
    singer: filterSinger(musicData.singer),
    name: musicData.songname,
    album: musicData.albumname,
    duration: musicData.interval,
    image: `https://y.gtimg.cn/music/photo_new/T002R300x300M000${musicData.albummid}.jpg?max_age=2592000`,
    url: `http://ws.stream.qqmusic.qq.com/${musicData.songid}.m4a?fromtag=46`
    })
    }
    function filterSinger(singer) {
    let ret = []
    if (!singer) {
    return ''
    }
    singer.forEach((s) => {
    ret.push(s.name)
    })
    return ret.join('/')
    }


    最后在singer-detail组件的created阶段获取数据

    created() {
    this._getDetail()
    }


    上面我介绍了如何获取歌手详情页组件的相关数据,这些数据最终的目的是用来渲染的。

    歌手图片实现

    在需求分析中提到的歌手图片,这是一张宽高比为10:7的图片,我们在之前的博文中提到了如何实现宽高比相等的图片,这里解决思路一样
    music-list.vue中stylus

    .bg-image
    position: relative
     100%
    height: 0
    padding-top: 70%
    transform-origin: top
    background-size: cover



    歌手详情页歌手图片,歌手名,返回和播放按钮都是在music-list.vue中实现的(html,css),music-list下的子组件song-list维护了歌手歌曲列表。

    点击图片返回效果实现,给按钮添加点击事件back

    back() {
    this.$router.back()
    },


    歌曲列表滚动效果实现

    歌曲列表滚动效果使用了基础组件scroll组件,这里重点说明几个关键点,滚动中特效的实现利用了CSS3 transform的translate3d和scale方法

    向上滚动实现图片隐藏效果
    首先将scroll组件样式overflow:hidden去掉
    给图片div层添加一个同级div class="bg-layer"
    music-list组件data维护了scrollY并在watch中设置监听,当scrollY发生改变时触发相应事件

    向下滚动实现图片放大(transform:scale)

    具体代码重点scrollY

    scrollY(newVal) {
    let translateY = Math.max(this.minTransalteY, newVal)
    let zIndex = 0
    let scale = 1
    let blur =0
    const percent = Math.abs(newVal / this.imageHeight)
    if (newVal > 0) {
    scale = 1 + percent
    zIndex = 10
    } else {
    blur = Math.min(20, percent * 20)
    }
    this.$refs.layer.style[transform] = `translate3d(0,${translateY}px,0)`
    this.$refs.filter.style[backdrop] = `blur(${blur}px)`
    if (newVal < this.minTransalteY) {
    zIndex = 10
    this.$refs.bgImage.style.paddingTop = 0
    this.$refs.bgImage.style.height = `${RESERVED_HEIGHT}px`
    this.$refs.playBtn.style.display = "none"
    } else {
    this.$refs.bgImage.style.paddingTop = '70%'
    this.$refs.bgImage.style.height = 0
    this.$refs.playBtn.style.display = ''
    }
    this.$refs.bgImage.style[transform] = `scale(${scale})`
    this.$refs.bgImage.style.zIndex = zIndex
    }

    CSS3transform属性浏览器兼容处理

    let elementStyle = document.createElement('div').style
    
    let vendor = (() => {
    let transformNames = {
    webkit: 'webkitTransform',
    Moz: 'MozTransform',
    O: 'OTransform',
    ms: 'msTransform',
    standard: 'transform'
    }
    
    for (let key in transformNames) {
    if (elementStyle[transformNames[key]] !== undefined) {
    return key
    }
    }
    
    return false
    })()
    
    export function prefixStyle(style) {
    if (vendor === false) {
    return false
    }
    
    if (vendor === 'standard') {
    return style
    }
    
    return vendor + style.charAt(0).toUpperCase() + style.substr(1)
    }

    调测反思
    在项目调测过程中,我们要结合Vue Devtools,比如我们在获得歌曲数组时在singer-detail中console.log(this.songs)会发现无法看到具体内容

    但在Vue Devtools中的singer-detail.vue组件的data中可以很清楚的看到songs数组

    总结

    子路由使用
    滚动处理
    边界处理
    浏览器兼容性
    代码复用和可维护性

  • 相关阅读:
    Java变量在内存中的存储
    Java成员变量和局部变量
    Java类的定义与类的实例化
    面向对象编程思想
    Java数组深入
    Java的Arrays类 基本用法
    Intellij IDEA如何设置快速调整字体大小的快捷键
    Win10如何快速截屏
    应用层协议原理(二)
    应用层协议原理(一)
  • 原文地址:https://www.cnblogs.com/catbrother/p/9179610.html
Copyright © 2011-2022 走看看