>웹 프론트엔드 >JS 튜토리얼 >JS는 음악 플레이어 인터페이스를 구현합니다.

JS는 음악 플레이어 인터페이스를 구현합니다.

php中世界最好的语言
php中世界最好的语言원래의
2018-03-23 16:36:183970검색

이번에는 뮤직 플레이어 인터페이스 구현을 위한 JS를 소개하겠습니다. JS가 뮤직 플레이어 인터페이스를 구현하기 위한 주의사항은 무엇인가요? 다음은 실제 사례입니다.

이 글에서는 Vue 페이지에 뮤직 플레이어를 구현한 사례를 소개하고 모두와 공유합니다.

효과는 다음과 같습니다.

프로젝트 주소: https:/ /github.com/ermu592275254/MiniMusicPlayer

데모 주소: https://ermu592275254.github.io/MiniMusicPlayer/(노래 링크가 만료되었습니다)

개발 전 개념

Interface

뮤직 플레이어를 만들려면 , 인터페이스가 멋져야 합니다. 너무 낮아서 음악을 들어도 아무 느낌이 안 드네요. 업무용으로 사용하기 위한 것이므로 NetEase Cloud Music과 비슷한 인터페이스를 적당한 크기로 만들었습니다. 휴대폰과 호환될 필요가 없습니다.

CSS를 사용하여 아이콘 만들기

이는 간단하고 실용적인 요구 사항을 기반으로 합니다. 아이콘은 SVG, URL 또는 CSS일 수 있습니다. URL에 비해 SVG와 CSS가 더 좋습니다. 연습을 위해 결국 CSS를 선택했습니다. after와 before를 잘 활용하면 DOM 중첩을 많이 줄일 수 있습니다.

.next {
  position: relative;
  display: inline-block;
  height: 36px;
  width: 36px;
  border: 2px solid #fff;
  border-radius: 20px;
  -webkit-border-radius: 20px;
  -moz-border-radius: 20px;
}
    
.next:before {
  content: '';
  height: 0;
  width: 0;
  display: block;
  border: 10px transparent solid;
  border-right-width: 0;
  border-left-color: #fff;
  position: absolute;
  top: 8px;
  left: 10px;
}
.next:after {
  content: '';
  height: 20px;
  width: 4px;
  display: block;
  background: #fff;
  position: absolute;
  top: 8px;
  left: 22px;
}

레코드 그리기

NetEase Cloud의 레코드는 너무 아름답습니다. 저도 레코드를 만들고 싶습니다! 상자 그림자를 잘 활용하면 요소 하나로 아름다운 레코드 효과를 만들 수 있습니다.

.disc {
  position: relative;
  margin-top: 10%;
  margin-left: 10%;
  width: 300px;
  height: 300px;
  border-radius: 300px;
  transform: rotate(45deg);
  background-image: radial-gradient(5em 30em ellipse, #fff, #000);
  border: 2px solid #131313;
  box-shadow: 0 0 0 10px #343935;
  opacity: 0.7;
}

범위를 진행률 표시줄로 사용

오디오 자체의 스타일도 보기 흉하고 브라우저마다 표시되는 효과도 다릅니다. 물론 오디오 스타일을 수정할 수 있습니다. 전통적인 방법은 컨트롤 속성을 통해 오디오를 숨긴 다음 대신 p를 사용하는 것입니다. 지금은 HTML5 시대입니다. 물론 장면에 더 적합한 새로운 요소를 사용해야 합니다.

input[type=range] {
  -webkit-appearance: none;
  width: 80%;
  height: 8px;
  border-radius: 10px;
  background-color: #fff;
}
input[type=range]::-webkit-slider-thumb{
  -webkit-appearance: none;
} 
input[type=range]::-webkit-slider-runnable-track {
  height: 8px;
  border-radius: 20px;
}
input[type=range]:focus {
  outline: none;
}
input[type=range]::-webkit-slider-thumb {
  -webkit-appearance: none;
  margin-top: -3px;
  height: 14px;
  width: 14px;
  background: #eb7470;
  border-radius: 50%;
  border: solid 3px #fff;
  box-shadow: 0 0 0 3px rgba(255, 255, 255, 0.5);
}

배경 필터 블러

사진을 배경으로 설정하면 플레이어 전체 모습의 절반이 배경이 된다고 할 수 있습니다. CSS3 필터를 사용하면 설정도 매우 간단합니다.

.bg-blur {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 100%;
  height: 100%;
  background-position: center;
  background-repeat: no-repeat;
  background-size: cover;
  filter: blur(20px);
  z-index: -1;
}

배경 이미지는 js를 통해 제어됩니다.

<p class="bg-blur" :style="`background-image:url(${currentSong.album_logo})`"></p>

노래 리소스

인터페이스 아래로 올라가세요

Xiami 공식 웹사이트로 직접 이동하여 네트워크를 열고 URL을 우편 배달부에 복사하여 요청하세요. 헤더를 수정하면 Referer가 검증되는 것으로 나타났습니다. 즉, Xiami에서 허용한 도메인 이름만 이 인터페이스에 액세스할 수 있습니다. 인터페이스가 jsonp를 지원하므로 http://api.xiami.com/web?v=2.0&app_key=1&key=aliez&page=1&limit=5&callback=jsonp154&r=search/songs

도메인 간 문제를 해결하세요

. 처음에는 크롬 브라우저를 크로스 도메인으로 설정한 뒤 $.ajax를 통해 jsonp 요청을 해보았습니다. 정상적으로 접속 가능합니다.

그런데 갑자기 작동이 멈췄습니다. Xiami가 제한을 적용한 것인가요?

그래서 노드를 사용하여 서비스를 시작하고 리퍼러를 위조하여 요청을 시작한 다음 해당 요청을 페이지로 전달했습니다. 실수로 에이전트를 썼습니다.

...
case '/song':
  let songOptions = {
    url: 'http://api.xiami.com/web?'+ urlArr[1],
    headers: {
      'Referer': 'http://m.xiami.com/'
    }
  };
  function callback1(error, response, body) {
    if (!error && response.statusCode == 200) {
      res.end(body);
    }
  }
  request(songOptions, callback1);
  break;
...

가사 스크롤

고품질 플레이어로서 가사 스크롤은 필수입니다.

Principle

각 가사를 해당 시간의 개체로 저장하세요. 현재 노래의 길이가 가사의 시간보다 크거나 같으며 가사의 다음 줄의 시간보다 작을 경우 가사를 표시 영역으로 스크롤하세요. 그리고 글꼴 색상을 변경해 보세요.

가사 형식 지정

인터페이스에서 반환된 가사가 헷갈립니다. 주의 깊게 연구한 결과 패턴이 있다는 것을 발견했습니다.

[ti:aLIEz]
[ar:SawanoHiroyuki[nZk]:mizuki]
[al:o1]
[ly:澤野弘之]
[mu:澤野弘之]
[ma:]
[pu:]
[by:ttpod]
[total:268512]
[offset:0]
[00:00.000]<195>aLIEz <199>- <451>SawanoHiroyuki[nZk]:mizuki
[x-trans]彻头彻尾的谎言 - SawanoHiroyuki[nZk]:mizuki
[00:01.095]<201>作<250>詞<200>:<201>澤<200>野<199>弘<300>之
[x-trans]
[00:02.846]<200>作<150>曲<150>:<200>澤<200>野<351>弘<349>之
[x-trans]
[00:20.828]<200>決<250>め<200>つ<201>け<149>ば<201>か<349>り
[x-trans]一直独断专权
[00:23.279]<200>自<200>惚<200>れ<200>を<200>着<400>た
[x-trans]总是自负逞强
[00:24.979]<200>チ<200>ー<200>プ<450>な<550>hokori<350>で
[x-trans]明明只是一文不值的骄傲
......
  refactoringLyrics(lyric){
  let text = lyric.split('[offset:0]')[1];
  let textArr = text.split('\n');
  let lyricsArr = [], translate = [];
  textArr.forEach((item, index) => {
    let time = 0, text = '';
    if (item.indexOf('[x-trans]') > -1) {
      translate.push(item.split('[x-trans]')[1])
    } else if (item.trim() != '') {
      time = item.slice(1, 6).split(':');
      time = parseInt(time[0]) * 60 + parseInt(time[1]);
      text = item.slice(11);
      let arr = text.split('>');
      let str = arr.reduce((a, b) => {
        return a.split('<&#39;)[0] + b.split(&#39;<&#39;)[0]
      });
      let obj = {
        time: time,
        text: str
      };
      lyricsArr.push(obj);
    }
  });
  for (let i in translate) {
    lyricsArr[i].text = lyricsArr[i].text + &#39;\n&#39; + translate[i];
  }
  this.currentLyrics = lyricsArr;
},

검색창 구현

동일 파일 아래에 하위 컴포넌트 탑재

모듈식 개발을 따르기 위해 검색창을 하위 컴포넌트로 작성하기로 결정했습니다. 같은 페이지에 하위 구성요소를 작성할 때 해당 템플릿에 하위 구성요소를 마운트하는 것이 중요합니다. 이 템플릿은 상위 구성 요소의 장착 요소에 포함될 수 없습니다. 그렇지 않으면 상위 구성 요소가 렌더링될 때 하위 구성 요소의 데이터를 렌더링할 수 없기 때문에 정의되지 않음이 보고됩니다.

<p id="app" class="main">
...
</p>
<template id="search-box">
...
</template>
var searchBox = {
    template: '#search-box',
    props: {
      isShow: Boolean,
      openFun: Function
    },
    data(){
      return {
        resultList: [],
        searchValue: '',
      }
    },
    methods: {
    }
  };
 new Vue({
  el: '#app',
  components: {
    'com-tip': tip,
    'search-box': searchBox
  },
  ...
})

eventBus로 데이터 전송 문제 해결

通过jsonp去请求数据,需要设置一个callback函数,此callback写成一个全局函数,如果不这样写,而是通过 searchBox.methods.callback的形式,this指向将为methods。而无法直接给searchBox的data赋值。 于是通过eventBus来处理,这样更易维护。

var EventBus = new Vue();
var callBack = function(result) {
  console.log(result);
  EventBus.$emit('callBack', result);
};
...
mounted(){
  let self = this;
  EventBus.$on('callBack', function(res) {
    if (res && res.data) {
      self.resultList = res.data.songs;
    }
  })
}
...

localStrong储存歌曲信息

下次再打开,应该播放列表应该保留上一次的数据,这个可直接用localstrong实现

踩了坑

prop传递数据

使用cdn,vue的prop只支持中线格式,驼峰格式不生效

ps: 在用webpack打包的项目中用驼峰是可以,在打包过程中,会做处理。

// 正确写法
<search-box :is-show="showSearch" :open-fun="openSearch" @push-song="pushNewSong"
        @play-song="playSong"></search-box>
// 错误写法
<search-box :isShow="showSearch" :openFun="openSearch" @pushSong="pushNewSong"
        @playSong="playSong"></search-box>

待优化

手动修改进度,偶尔会不生效。

搜索暂不支持分页

不支持建歌单

背景颜色与进度条颜色相近需修改进度条颜色

不支持播放模式选择-单曲循环-随机播放

相信看了本文案例你已经掌握了方法,更多精彩请关注php中文网其它相关文章!

推荐阅读:

Express与Koa2的使用详解

地图网格的实现

위 내용은 JS는 음악 플레이어 인터페이스를 구현합니다.의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
이전 기사:지도 그리드 구현다음 기사:지도 그리드 구현