window location href 手机端无法跳转_制作移动端网易云音乐(jQuery版)

689e3604ef3777b424d9a2fb7b456ac4.png
一直都想做一个音乐播放器,这次总算做了一个。

398b1cc12e368f00c35e945a751285c1.png
第一个页面上半部分截图

关键词:JavaScript、jQuery、响应式布局 、MVC、eventHub(发布/订阅模式)、七牛&LeanCloud数据库、Swiper、移动端

描述:移动端播放歌曲、切换、暂停、搜索等功能,PC 端歌曲上传、删除、修改等功能。使用 jQuery、MVC,以及 LeanCloud、七牛等作为数据库实现。使用vConsole进行调试

源码链接:

ajing741472797/163-music-demo​github.com
26c51dc0e337acb47ad57c497aaa24af.png

预览链接:

我的云音乐​ajing741472797.github.io
408999d45e1242d23e00db630e09f526.png

整理一下页面分析和解决bug的通用方法:

一、页面分析

1、网易云音乐logo+下载APP:

logo用SVG做,a标签做跳转

2、推荐音乐、热歌榜、搜索三个Tab键跳转

  • 用ol>li>span标签包裹:推荐音乐、热歌榜、搜索
  • 通过onclick事件点击任意一个Tab,将其余两个Tab页面隐藏,并用::after来显示红色下划线
  • 对span使用inline-block,让红色下划线能够动态获取父元素的宽度
//index.html
<section id="tabs" class="globalTabs">
                <ol class="tabs-nav">
                    <li class=active data-tab-name="page-1">
                        <div class="text"> 推荐音乐 </div>
                    </li>
                    <li data-tab-name="page-2">
                        <div class="text">热歌榜</div>
                    </li>
                    <li data-tab-name="page-3">
                        <div class="text">搜索</div>
                    </li>
                </ol>
</section>

//tabs.js
 bindEvents(){
            this.view.$el.on('click','.tabs-nav > li',(e)=>{
                let $li = $(e.currentTarget)
                let pageName = $li.attr('data-tab-name')
                $li.addClass('active')
                .siblings().removeClass('active')
                window.eventHub.emit('selectTab', pageName)
            })
        }

//page-1.js

//view
        show(){
            this.$el.addClass('active')
        },
        hide(){
            this.$el.removeClass('active')
        }
//controller
        bindEventHub(){
         
            window.eventHub.on('selectTab',(tabName)=>{
                if(tabName === 'page-1'){
                    this.view.show()
                }else{
                    this.view.hide()
                }
            })
        }

3、在MVC中一个js模块引入多个js

664043593c595628292c04688f1dc2a0.png
//page-1.js

loadModule1(){
            let script1 = document.createElement('script')
            script1.src = './js/index/page-1-1.js' 
            script1.onload = function(){
            }
            document.body.appendChild(script1)
      
          },
          loadModule2(){
            let script2 = document.createElement('script')
            script2.src = './js/index/page-1-2.js' 
            script2.onload = function(){
            }
            document.body.appendChild(script2)
          }

4、推荐音乐页面-轮播

使用Swiper官网组件,引入min.css和min.js,自定修改样式

5、推荐音乐页面-推荐歌单

  • 使用ol>li*6>a>img+p标签插入跳转、图片、文字
  • 确定每个li元素的宽度和间隙
  • 使用flex布局:flex-wrap:wrap、flex-direction:row、justify-content:space-between
  • a标签的href=“跳转页面的地址+查询参数(?id=xxxxxx)”

6、推荐音乐页面-最新音乐

音乐歌曲、图片上传到七牛,地址、歌名、歌描述、歌词、歌的id保存到LeanCloud

从数据库批量获取所存音乐,并把获取的数据通过evenHub通知其他所需要数据的页面

        find(){
            var query = new AV.Query('Song');
            return query.find().then((songs)=>{
                this.data.songs = songs.map((song)=>{
                    return Object.assign({id: song.id}, song.attributes) // ...表示song.attributes有什么我就要什么
                })
                return songs
            })
        }
  • 对获取音乐的数组进行map()动态生成音乐清单,播放图标使用iconfont,smybol引用
renderSongList(data){
 let songs=data
 songs.map((song)=>{
    let name = song.attributes.name
    let descript =song.attributes.descript
    let url =song.attributes.url
    let id = song.id
    let $li = $(`
        <li>
            <h3>${name}</h3>
            <div class='sq'></div>
            <p>${descript}</p>
            <a href="./song.html?id=${id}">
                <svg class="icon" aria-hidden="true">
                   <use xlink:href="#icon-play1"></use>
                 </svg>
             </a>
         </li>           
     `)
    $(this.el).find('#lastestMusic').append($li)
  })
},

00cf87b7a561d20f7f5014d2f56356c0.png
第一个页面-最新音乐

7、推荐音乐页面-底部logo

logo用SVG做,a标签做跳转

8、热歌榜-云音乐背景

d5e56f36677ad8292526e2e35bea86ac.png
  • 其中白色的字的图片是一个雪碧图
  • 外面再套一个特定宽高的div使用overflow:hidden
  • 对雪碧图进行移动

9、热歌榜-音乐清单

57ac3b82e7394eb1c464ce4caadea3d9.png
第二个页面-热歌榜
  • 同5从数据库动态获取所有的音乐
        find(){
            var query = new AV.Query('Song');
            return query.find().then((songs)=>{
                this.data.songs = songs.map((song)=>{
                    return Object.assign({id: song.id}, song.attributes) // ...表示song.attributes有什么我就要什么
                })
                return songs
            })
        }
  • 1、2、3数字高亮
        activeNumber(){
            let $songsNumber = $('.hotMusicList').find('.songNumber')
            for(let i=0;i<=2;i++){
                $songsNumber.eq(i).addClass('active')
            }
        },
  • 1、2、3变成01、02、03
<div class="songNumber">${this.makeNumber(n)}</div>


     makeNumber(n){
            if(n<10){
                n=`0${n}`
                return n
            }
        },

10、搜索-搜索框

d38d7857bfd76c4a8a5a82c566346716.png
第三个页面-搜索

搜索框html代码如下

<div class="searchContainer">
     <img class="searchIcon"src="xxx" alt="">
     <input type="search" placeholder="搜索歌曲、歌手" >
     <img class="deleteIcon"src="xxx" alt="">
 </div>
  • 对输入框的value.length进行监听,searchIcon默认显示,当用户输入内容时deleteIcon显示。
changeDelete(value){
    if(value.length===0){
         $('.deleteIcon').removeClass('active')
    }else{
         $('.deleteIcon').addClass('active')
     }
 }
  • 当用户点击删除按钮,则令value=“”,并显示热门搜索

4bec8c392f3b6b34239d4cec4d0ab89b.png
第三个页面-搜索

11、搜索-从数据库动态显示用户输入的音乐

  • 每400毫秒获取一次用户输入的value值
  • 把value值传进一个函数,该函数从数据库获取所有音乐的信息,并找出包含value值的音乐,并返回数组
search(keyword){
    return new Promise((resolve,reject)=>{
        let database=this.model.data
        let result = database.filter(function(item){
             return item.name.indexOf(keyword)>=0
        })
             resolve(result)
      })
}
  • 获取该数组的音乐的信息,进行页面的渲染
renderSongList(result){
   $('.searchSongList>a').remove()
   result.map((item)=>{
       $aTag=$(`
          <a href="./song.html?id=${item.id}">
              <img src="./img/search.png" alt="">
              <p>${item.name}</p>                            
          </a>`)
       $('.searchSongList').append($aTag)
    })
},
renderNoSong(){
   $(this.el).find('.searchSongList>a>p').text('sorry,该歌曲暂未收入')
}
  • 整段代码如下:
let timer
$(this.view.el).find('input[type="search"]').on('input',(e)=>{
   let $input=$(e.currentTarget)
   let value = $input.val().trim()  //val()返回或设置被选元素的值,trim() 函数用于去除字符串两端的空白字符
   if(timer){
       clearTimeout(timer)
    }
    timer = setTimeout(()=>{
       if(value.length===0){
            this.view.changeDelete(value)
            this.view.isHotSong()
        }else{
            this.view.changeDelete(value)
            this.view.isSearch()
            this.view.renderSearchText(value)  
            this.search(value).then((result)=>{
                let songNumber = result.length
                if(songNumber==0){
                    this.view.renderNoSong()               
                }else{
                    this.view.renderSongList(result)     
                }
             })
        }
    },400)
})

12、播放音乐页面-光盘旋转

2947697e02b767eb14c9537d3a1a33d3.png
第四个页面-歌曲播放
  • 三张图片进行旋转
  • 根据播放和暂停来激活active 旋转
@keyframes circle{
    0%{
 transform: rotate(0);
    }
    100%{
 transform: rotate(360deg);
    }
}
.disc-container>.disc.active{
    animation: circle 20s linear infinite;
}

13、获取封面和背景图

 this.$el.css('background-image', `url(${song.background})`)
 this.$el.find('img.cover').attr('src',song.cover)

14、播放音乐页面-音乐播放与暂停

  • 对播放与暂停的icon进行onclick监听,并通过evenHub通知光盘模块进行切换旋转状态,以及icon切换
            play(){
            this.$el.find('audio')[0].play()//audio[0] audio中的dom元素
            },
            pause(){
            this.$el.find('audio')[0].pause()
             }


            $(this.view.el).on('click','.icon-play',()=>{
                this.model.data.status = 'playing'
                this.view.render(this.model.data)
                this.view.play()
            })
            $(this.view.el).on('click','.icon-pause',()=>{
                this.model.data.status = 'pause'
                this.view.render(this.model.data)
                this.view.pause()
            })

             if(status === 'playing'){
             this.$el.find('.disc-container').addClass('playing')
             this.$el.find('.disc-container .pointer').removeClass('active')

            }else{
             this.$el.find('.disc-container').removeClass('playing')
             this.$el.find('.disc-container .pointer').addClass('active')
            }
            

15、播放音乐页面-歌词同步

原理:lyric是对应了时间和歌词的,然后我们获取当前歌词的时间,与歌曲播放时间轴一致,然后当前的与下次生成的歌词p之间就是我们要找的在播放的那句歌词,渲染高亮并使其歌词随时间移动。

  • 获取歌词并处理,使其渲染的时间与时间轴一致
            let {lyrics} = song

            lyrics.split('n').map((string)=>{
                let p = document.createElement('p')
                let regex = /[([d:.]+)](.+)///正则
                let matches = string.match(regex) //字符串匹配正则
                if(matches){
                    p.textContent = matches[2]
                    let time = matches[1]
                    let parts = time.split(':')
                    let minutes = parts[0]
                    let seconds = parts[1]
                    let newTime = parseInt(minutes,10) * 60 + parseFloat(seconds,10)
                    p.setAttribute('data-time', newTime)//等于时间轴
                }else{
                    p.textContent = ''
                }
                this.$el.find('.lyric>.lines').append(p)
            })
  • 调用
            if(this.$el.find('audio').attr('src') !== song.url){//同一首歌不改src,这样暂停歌曲不会重置 
               let audio =  this.$el.find('audio').attr('src',song.url).get(0)//get(0)就是audio
               audio.onended = ()=>{
                    window.eventHub.emit('songEnd')//歌曲结束使旋转动画停止
                }
               audio.ontimeupdate = ()=>{   //ontimeupdate 歌曲或视频当前的播放位置发送改变时触发
                   this.showLyric(audio.currentTime)
               }
            }
  • 渲染歌词&移动
      showLyric(time){
            let allP = this.$el.find('.lyric>.lines>p')
            
            for(let i =0;i<allP.length;i++){
                if(i === allP.length-1){//如果是最后一行就显示最后一行
                    console.log(allP[i])
                    break
                }else{
                    let currentTime = allP.eq(i).attr('data-time')
                let nextTime = allP.eq(i+1).attr('data-time')
                if(currentTime <= time && time < nextTime){
                    let p = allP[i]
                    let pHeight = p.getBoundingClientRect().top//getBoundingClientRect DOM API 获取当前元素距离屏幕的位置
                    let linesHeight = this.$el.find('.lyric>.lines')[0].getBoundingClientRect().top
                    let height = pHeight - linesHeight
                    this.$el.find('.lyric>.lines').css({
                        transform: `translateY(${- (height - 25)}px)`
                    })
                    $(p).addClass('active').siblings('.active').removeClass('active')
                    break
                }
                }
            }
        },

二、bug解决方案

1、遇到报错

  • 查StackOverFlow
  • 查Google
  • 查知乎
  • 查组件官网

2、没有报错,但不出结果

  • JS:console.log('xxx')、console.log(变量)
  • CSS:border:1px solid red

3、移动端调试

  • 使用onerror事件,移动端没有console.log(),改为alert()
 <script>
     window.onerror = function(message, file, row){
       alert(message)
       alert(file)
       alert(row)
             }// 移动端调试
 </script>
  • 使用vConsole
    //安装
       npm install vconsole
       cp node_modules/vconsole/dist/vconsole.min.js vendors


	<script src="../vendors/vconsole.min.js"></script>
		
         <script>
		var vConsole = new VConsole();
		window.onerror = function(message, file, row){
                console.log(message)
                console.log(file)
		console.log(row)
	         }// 移动端调试
	</script>
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值