总结singer页面:

1.api中去获取 [‘热’,A-Z] 以及根据[‘热’,A-Z]获取的所有歌手的数据

2.渲染数据

  • 2.1 渲染左边 字母title [‘热’,A-Z] + 该字母开头的歌手
    因为存储字母的下标 与 存储对应字母开头歌手的下标 是一致的
    [‘热’, A, B, C, D, E…]
    [ ['热’门的所有的歌手], [字母A开头的歌手],[字母B开头的歌手],[字母C开头的歌手],[字母D开头的歌手]…]
    直接循环存储对应字母开头歌手,获取index,根据存储字母数组的keys 以及 获取到的 index: keys[index]即可拿到对应的字母
  • 2.2 渲染右边字母条 循环keys中的字母

3.使用 iscroll 包裹左边的内容,使其超出的内容可以滚动

4.点击右边字母条,左边可以滚动到指定的字母歌手区域

  • 4.1 获取到[‘热’,A-Z]标题距离顶部的高度(offsetTop)根据顺序放进数组groupsTop
  • 4.2 点击右边的字母条的字母,可以获取到对应的index,根据 groupsTop[index] 获取该字母的offsetTop
  • 4.3 因为左侧的内容使用iscroll包裹,iscroll上有scrollTo,可以滚动到指定的高度
    this.$refs.ScrollView.scrollTo(0, -offsetY)

5.滚动左侧的内容,使字母条对应的字母高亮

  • 5.1 因为左侧的内容使用iscroll包裹,iscroll上有scrolling,可以监听滚动的高度,获取当前滚动的距离 y(负值)
  • 5.3 循环数组groupsTop,获取 groupsTop[i],groupsTop[i+1],判断 groupsTop[i]< -y < groupsTop[i+1],获取到的i就是index
  • 5.4 根据这个 index === keys的index 增加类名active使其高亮

6.左侧的字母标题吸顶效果

  • 6.1 编写一个存放字母的元素(fixTitle),定位在顶部,根据5中当前滚动的距离y获取到对应的index
  • 6.2 fixTitle中显示 keys[index]

7.当滚动到两个标题相触的时候,上一个标题有个被推出的移动效果

  • 7.1 获取到标题的高度fixTitleHeight(offsetHeight)
  • 7.2 下一组标题的偏移位 + 当前滚动出去的偏移位:diffOffsetY = nextTop + y
  • 7.3 判断计算的结果是否是0~分组标题的高度的值 0 < diffOffsetY < fixTitleHeight
  • 7.4 上面判断为true,将当前的fixTitle增加this.$refs.fixTitle.style.transform = translateY(${fixTitleOffsetY}px)
  • 7.5 判断条件为false,fixTitleOffsetY = 0

代码实现

// singer
<template><div><div class="singer"><ScrollView ref="ScrollView"><ul class="list-wrapper"><liclass="list-group"v-for="(item, index) in list":key="index"ref="group"><h2 class="group-title">{{ keys[index] }}</h2><ul><li class="group-item" v-for="obj in item" :key="obj.id + index"><img v-lazy="obj.img1v1Url" alt="" /><p>{{ obj.name }}</p></li></ul></li></ul></ScrollView><!-- A-Z导航 --><!-- <ul class="list-keys"> --><!-- <li --><!-- v-for="(key, index) in keys" --><!-- :key="key" --><!-- @click.stop="keyDown(index)" --><!-- :class="{ active: currentIndex === index }" --><!-- > --><!-- {{ key }} --><!-- </li> --><!-- </ul> --><ul class="list-keys"><liv-for="(key, index) in keys":key="key":data-index="index"@touchstart.stop.prevent="touchstart"@touchmove.stop.prevent="touchmove":class="{ active: currentIndex === index }">{{ key }}</li></ul><div class="fix-title" v-show="fixTitle !== ''" ref="fixTitle">{{fixTitle}}</div></div></div>
</template><script>
import { getAllArtists } from '../api/index'
import ScrollView from '../components/ScrollView.vue'
export default {name: '',components: {ScrollView},data () {return {keys: [],list: [],groupsTop: [],currentIndex: 0,beiginOffsetY: 0,moveOffsetY: 0,scrollY: 0}},methods: {_keyDown (index) {// 点击对应的字母,拿到index,根据groupsTop[index]获取到对应的距离顶部的高度this.currentIndex = indexconst offsetY = this.groupsTop[index]this.$refs.ScrollView.scrollTo(0, -offsetY)},touchstart (e) {// e.target.dataset.index 是字符串const index = parseInt(e.target.dataset.index)this._keyDown(index)this.beiginOffsetY = e.touches[0].pageY},touchmove (e) {// console.log('e', e.target)this.MoveOffsetY = e.touches[0].pageYconst offsetY =(this.moveOffsetY - this.beiginOffsetY) / e.target.offsetHeightlet index = parseInt(e.target.dataset.index) + Math.floor(offsetY)if (index < 0) {index = 0} else if (index > this.keys.length - 1) {index = this.keys.length - 1}this._keyDown(index)}},computed: {// 固定分组标题fixTitle () {// this.scrollY >= 0 表示当前处于最顶部的时候往下拖if (this.scrollY >= 0) {return ''} else {// 根据当前的currentIndex获取到当前的groupTitlereturn this.keys[this.currentIndex]}}},created () {getAllArtists().then(res => {console.log(res)this.keys = res.keysthis.list = res.list}).catch(err => {console.log('err', err)})},mounted () {// 通过滚动的y值 去对比 group的offsetTop,获取当前的indexthis.$refs.ScrollView.scrolling(y => {this.scrollY = y// console.log('y', y)// 处理第一个区域if (y >= 0) {this.currentIndex = 0return}// 处理中间区域for (let i = 0; i < this.groupsTop.length - 1; i++) {const preTop = this.groupsTop[i]const nextTop = this.groupsTop[i + 1]if (-y >= preTop && -y <= nextTop) {this.currentIndex = i// 当滚动到两个标题相触的时候,上一个标题有个被推出的移动效果// 1.用下一组标题的偏移位 + 当前滚动出去的偏移位const diffOffsetY = nextTop + ylet fixTitleOffsetY = 0// 2.判断计算的结果是否是0~分组标题的高度的值if (diffOffsetY >= 0 && diffOffsetY <= this.fixTitleHeight) {fixTitleOffsetY = diffOffsetY - this.fixTitleHeight} else {fixTitleOffsetY = 0}if (fixTitleOffsetY === this.fixTitleOffsetY) {return}this.fixTitleOffsetY = fixTitleOffsetYthis.$refs.fixTitle.style.transform = `translateY(${fixTitleOffsetY}px)`return}}// 处理最后一个区域this.currentIndex = this.groupsTop.length - 1})},watch: {// 通过监听list,获取每个group的offsetToplist () {// 直接打印this.$refs.group为undefined,因为数据发生变化,数据可能没有渲染完成// watch只能监听数据的变化,数据变化不一定已经渲染完了// 为了保证是渲染完成之后再去获取,我们可以借助$nextTick方法来实现// 也就是说在$nextTick回调函数中一定能拿到渲染完成之后的数据,因为.$nextTick的回调函数只有渲染完成之后才会执行this.$nextTick(() => {console.log(this.$refs.group)this.$refs.group.forEach(group => {// 获取所有group-title距离顶部的距离this.groupsTop.push(group.offsetTop)})})},fixTitle () {this.$nextTick(() => {this.fixTitleHeight = this.$refs.fixTitle.offsetHeight})}}
}
</script><style lang="scss" scoped>
@import "@/assets/css/mixin";
@import "@/assets/css/variable";
.singer {position: fixed;top: 184px;left: 0;right: 0;bottom: 0;@include bg_sub_color();overflow: hidden;.list-wrapper {// width: 100%;// height: 100%;.list-group {.group-title {@include bg_color();@include font_size($font_medium);color: #fff;padding: 10px 20px;box-sizing: border-box;}.group-item {display: flex;justify-content: flex-start;padding: 10px 20px;border-bottom: 1px solid #ccc;img {width: 100px;height: 100px;border-radius: 50%;overflow: hidden;}p {@include font_size($font_medium);@include font_color();display: flex;align-items: center;margin-left: 20px;}}}}.list-keys {position: fixed;right: 10px;top: 60%;transform: translate(-50%, -50%);li {@include font_color();@include font_size($font_medium_s);padding: 3px 0;&.active {text-shadow: 0 0 10px #000;}}}.fix-title {position: absolute;left: 0;right: 0;top: 0;padding: 10px 20px;box-sizing: border-box;@include font_size($font_medium);color: #fff;@include bg_color()}
}
</style>

IScroll-----ScrollView组件内容

<template><div id="wrapper" ref="wrapper"><slot></slot></div>
</template><script>
// iscroll-probe专业版本,可以监听滚动的位置等细节
import IScroll from 'iscroll/build/iscroll-probe'
export default {name: '',mounted () {this.iscroll = new IScroll(this.$refs.wrapper, {mouseWheel: true,scrollbars: false, // 是否显示滚动条probeType: 3, //  像素级的触发scroll事件// 解决拖拽卡顿问题scrollX: false,scrollY: true// disablePointer: true,// disableTouch: false// disableMouse: true})// setTimeout(() => {//   //  数据是从网络上获取的,获取完需要重新计算滚动范围//   this.iscroll.refresh()// }, 5000)// 1.创建一个观察者对象/*** MutationObserver只要监听到指定内容发生变化,就会执行传入回调函数* mutationList:发生变化的数组* observer:观察者对象*/var observer = new MutationObserver((mutationList, observer) => {// console.log(mutationList)this.iscroll.refresh()})// 2.告诉观察者对象需要观察什么内容const config = {childList: true, // 观察目标子节点的变化,是否有添加或者删除subtree: true, // 观察后代节点,默认为 falseattributeFilter: ['height', 'offsetHeight'] // 观察特定属性 子节点的高度}// 3.告诉观察者对象,我们需要观察谁,需要观察什么内容/*** 第一个参数:告诉观察者我们需要观察谁* 第二个参数:告诉观察者我们需要观察什么内容*/observer.observe(this.$refs.wrapper, config)},methods: {// 提供一个监听滚动距离的方法给外界使用scrolling (Fn) {this.iscroll.on('scroll', function () {Fn(this.y) // 把当前偏移位给外界})},refresh () {setTimeout(() => {this.iscroll.refresh()}, 100)},// 滚动方法scrollTo (x, y, time) {this.iscroll.scrollTo(x, y, time)}}
}
</script><style lang="scss" scoped>
#wrapper {width: 100%;height: 100%;
}
</style>

api

// api
export const getHotArtists = () => {return new Promise(function (resolve, reject) {Network.get('top/artists?offset=0&limit=5').then(function (res) {resolve(res.artists)}).catch(function (err) {reject(err)})})
}
// 根据 ['热',A-Z] 获取对应的歌手
export const getLetterArtists = letter => {return new Promise(function (resolve, reject) {const letterArtists = []Network.all([Network.get(`artist/list?offset=0&limit=5&type=1&area=7&initial=${letter}`),Network.get(`artist/list?offset=0&limit=5&type=1&area=96&initial=${letter}`),Network.get(`artist/list?offset=0&limit=5&type=2&area=7&initial=${letter}`),Network.get(`artist/list?offset=0&limit=5&type=2&area=96&initial=${letter}`),Network.get(`artist/list?offset=0&limit=5&type=3&area=7&initial=${letter}`),Network.get(`artist/list?offset=0&limit=5&type=3&area=96&initial=${letter}`)]).then(res => {// console.log("res", res);res.forEach(item => {letterArtists.push(...item.artists)})// console.log('letterArtists', letterArtists)resolve(letterArtists)}).catch(err => {console.log(err)})})
}
// 获取 ['热',A-Z] 以及根据['热',A-Z]获取的所有歌手的数据
export const getAllArtists = letter => {return new Promise(function (resolve, reject) {const keys = ['热']const list = [getHotArtists()]// 先生成A-Z所有ASSCIIfor (let i = 65; i < 91; i++) {const char = String.fromCharCode(i)// console.log('cahr', char)keys.push(char)list.push(getLetterArtists(char))}Network.all(list).then(res => {const obj = {}obj.keys = keysobj.list = res// console.log(res);resolve(obj)}).catch(err => {console.log(err)reject(err)})console.log('cahr', keys)})
}

网易云音乐的API

网易云音乐的API


学习笔记,版权Jonathan所有

vue音乐项目歌手页面滚动、吸顶效果相关推荐

  1. Flutter 页面滚动吸顶详解(NestedScrollView)

    前言 在业务开发中我们经常会有滚动吸顶的效果,目前Flutter也有很多种实现方式,这里介绍一下本人在开发中使用到的基于NestedScrollView实现的滚动吸顶组件:以及中间涉及的各种定位的布局 ...

  2. vue音乐项目歌手详情页小结

    技术栈 1,vue 2,vuex 3,vue-router(子路由) 需求分析 1)歌手列表点击歌手会跳转到下级页面歌手详情页,歌手详情页由四个部分组成 歌手图片 返回按钮:点击返回歌手tab页 随机 ...

  3. vue中怎么实现吸顶效果

    在 web 应用中,我们经常需要让页面中的一个或多个元素在页面滚动时保持固定位置.这种效果通常被称为吸顶效果,因为它使元素像粘在页面顶部一样固定不动. 在 Vue 中,实现吸顶效果有不同的方法.本文将 ...

  4. Vue实现滚动吸顶,文案动态更改

    Vue实现滚动吸顶,文案动态更改 1.效果和代码如下 <template><div class="record"><div class="c ...

  5. 2021-12-02 vue移动端卖座电影项目(五) 封装Film下的二级路由,FilmHeader实现吸顶效果

    文章目录 1.封装Film下的二级路由 目的/效果 步骤 2.让FilmHeader.vue实现向下滑动时的吸顶效果(固钉效果) 思路 步骤 最终效果 3.离开Film页面时取消触发handleScr ...

  6. 滚动页面,实现导航栏固定在顶部(吸顶效果)

    内容说明 页面中有导航栏,当页面滚动超出一定范围时,它会固定在设置好的位置,一般是固定在顶部. 本文有两种方式实现,一种是sticky,兼容不好:另外一种是sticky的解体..兼容效果万能 1.首先 ...

  7. vue开发(三)vue-scroller实现下拉刷新,上拉加载笔记(包括吸顶效果失效的问题)

    项目中要实现下拉刷新,上拉加载,首先想到了vue-scroller. npm网址:vue-scroller 简单记录一下自己的使用过程,以备不时之需. 安装依赖: npm install vue-sc ...

  8. Vue实现导航栏吸顶效果

    <!DOCTYPE html> <html lang="en"><head><meta charset="UTF-8" ...

  9. 微信小程序实现滚动吸顶

    1.实现效果 2.实现原理 方法一:position:sticky简单粗暴,但存在部分机型不支持 1.position:sticky( position: -webkit-sticky): 类似pos ...

最新文章

  1. centos6.7 64位环境下部署MySQL-5.7.13
  2. 一个write和printf混用的例子
  3. Quartz.Net 学习之路02 初探Quartz.Net
  4. Scala教程之:Future和Promise
  5. 字体类形:font-family, font-style
  6. .git文件夹_Git幸存者指南
  7. ASP.NET Core 设置允许跨域访问
  8. TreeSet与TreeMap
  9. MacBook如何配置环境变量
  10. ssh链接报错Server responded “Algorithm negotiation failed”
  11. 推荐几个值得关注的微信公众号
  12. ds5100更换电池 ibm_IBMDS5100更换电池
  13. HyperV虚拟机连接时主机无法连接网络 2022-06-13
  14. git回退到上一个版本:
  15. C语言建立循环单链表并输出
  16. Nginx安装配置报错详解
  17. b64_c3VuJTIwYm95 好看的电影推荐
  18. Radxa Rock 3a NPU调用指南
  19. java语言程序设计第三版答案郎波著,都是精髓!
  20. 2062门课程名称翻译大全

热门文章

  1. 数据结构与算法:十大排序算法之归并排序
  2. Navicat连接MySQL8.0版本时出现Client does not support authentication protocol requested by server;报错的问题解决办法
  3. jquery.rotate.js 转盘抽奖示例
  4. 打开android studio项目,为什么我们没有一个文件来打开Android Studio项目?
  5. python等比例压缩图片_python(PIL)图像处理(等比例压缩、裁剪压缩) 缩略(水印)图详解...
  6. Unity 图片开启不同选项内存占用
  7. Unity 2D和3D对象的点击
  8. python列表用法大全
  9. F5-yumnfsftp
  10. SpringBoot实战总汇--详解