기술 포인트: ES6+Webpack+HTML5 Audio+Sass
여기에서는 H5 뮤직 플레이어를 처음부터 구현하는 방법을 단계별로 학습해 보겠습니다.
먼저 최종 구현을 살펴보겠습니다. 데모 링크
그런 다음 본격적으로 시작하겠습니다.
음악 플레이어가 되려면 웹에서 오디오가 재생되는 방식을 잘 이해해야 합니다. , HTML5 오디오가 주로 사용됩니다. 태그
오디오 태그에 관해서는 많은 속성, 메소드 및 이벤트가 있습니다. 여기서는 일반적인 소개를 하겠습니다.
속성:
src: 필수, 오디오 소스;
컨트롤: 일반, 설정 후 브라우저의 기본 오디오 제어판이 표시되며, 설정되지 않은 경우 오디오 태그는 기본적으로 숨겨집니다. 설정 후 자동으로 재생됩니다(모바일 장치에서는 지원되지 않음)
loop: 설정 후 오디오가 반복 재생됩니다.
preload: 공통, 오디오 사전 로드 설정(모바일 단말기에서는 지원되지 않음)
volume: rare, set; 또는 오디오 크기를 반환합니다. 값은 0-1 부동 소수점 숫자입니다(모바일 장치에서는 지원되지 않음).
muted: 희귀, 음소거 상태로 설정 또는 반환
duration: 희귀, 오디오 지속 시간 반환; 현재 재생 시간을 설정하거나 반환합니다.
paused: 드물게, 현재 재생 상태로 돌아갑니다.
buffered: 드물게, 버퍼링된 기간 정보, 즉 로드 진행률이 포함된 TimeRanges 개체입니다. 이 객체에는 현재 버퍼링된 오디오 세그먼트 수를 나타내기 위해 0부터 시작하는 숫자를 반환하는 속성 길이가 포함되어 있습니다. 또한 각각 매개 변수, 즉 오디오 세그먼트를 전달해야 하는 두 가지 메서드도 포함되어 있습니다. 오디오가 0부터 로드되었습니다. start는 세그먼트의 시작 시간을 반환하고 end는 세그먼트의 종료 시간을 반환합니다. 예를 들어, 0을 전달하고, 첫 번째 단락의 시작은 0이고, 종료 시간은 17이며, 단위는 초입니다. 여기에
속성이 도입되었습니다. 다음과 같이 덜 사용되는 속성이 있을 수 있습니다. 영상 재생 중에 사용될 수도 있으니 아직은 설명하지 않겠습니다.
메서드:
play(): 오디오 재생 시작;
pause(): 오디오 재생 일시 중지;
Event:
canplay: 현재 오디오 재생을 시작할 수 있습니다(버퍼링된 부분만 로드되고 전부 로드되지는 않음).
canplaythrough: 일시 중지 없이 재생할 수 있습니다(즉, 모든 오디오가 로드됨).
durationchange: 오디오 지속 시간이 변경됩니다.
ended: 재생이 종료됩니다.
pause: 재생이 일시 중지됩니다.
progress: 오디오 다운로드 프로세스 중에 트리거됩니다. 오디오의 버퍼링된 속성에 액세스하여 로딩 진행률을 얻을 수 있습니다.
seeking: 오디오 점프 중에 트리거됩니다. : 오디오 점프가 완료될 때, 즉 currentTime이 수정될 때 트리거됩니다.
timeupdate: 오디오 재생 중에 트리거되고 currentTime 속성이 동기적으로 업데이트됩니다.
이벤트는 여기에 소개되지만 일반적으로 사용되지 않는 이벤트가 있을 수 있습니다. 아직 설명하지 않았습니다.
마지막으로 로드 시작부터 재생 종료까지 오디오에 의해 트리거되는 이벤트 흐름과 다양한 기간에 작동할 수 있는 속성을 설명하겠습니다.
loadstart: 로드 시작
durationchange: 오디오 지속 시간을 가져옵니다. 기간 속성 가져오기);
progress: 오디오 다운로드(다운로드 프로세스와 함께 트리거되며 이때 버퍼링 속성을 얻을 수 있음)
canplay: 로드된 오디오가 재생을 시작하기에 충분합니다(또한 트리거됩니다). 일시 정지 후 재생을 시작할 때마다);
canplaythrough: 오디오가 모두 로드됩니다.
timeupdate: 재생 중(currentTime 속성이 동기적으로 업데이트됩니다.)
seeking: 현재 재생 진행률이 수정됩니다. 수정됨);
seeked: 현재 재생 진행 수정이 완료되었습니다.
ended: 재생이 완료되었습니다.
전체 오디오의 일반적인 이벤트 흐름이지만 나열되지 않은 경우도 있습니다.
이벤트 트리거 프로세스 중에 오디오 로딩이 시작되기 전에 컨트롤, 루프, 볼륨 등과 같은 몇 가지 속성을 설정할 수 있습니다.
전체 구조를 결정합니다.
다음과 같이 만들어지기 때문입니다. 플러그인 이 메소드는 다른 사람들이 사용할 수 있도록 npm에 게시되어 있으므로 우리는 코드 작성에 객체 지향 접근 방식을 사용합니다. 사용자의 요구 사항이 다르기 때문에 설계 초기에 많은 수의 API와 구성 항목이 노출됩니다. 대부분의 사용자 요구 사항을 충족합니다.
플레이어 UI 및 상호 작용 결정:
아마 모든 사람이 인터페이스에 대해 자신만의 생각을 갖고 있을 수 있으므로 여기서는 자세히 설명하지 않겠습니다. 제가 만든 플레이어 UI를 분해해 보겠습니다.
볼 수 있습니다. 인터페이스에서 한 가지 플레이어에 필요한 가장 기본적인 기능:
재생/일시 중지, 커버/노래 제목/가수 표시, 재생 진행률 표시줄/로딩 진행률 표시줄/진행 작업 기능, 순환 모드 전환, 진행 텍스트 업데이트/노래 길이, 음소거/볼륨 크기 제어, 목록 표시 상태 제어, 목록 항목을 클릭하면 노래 전환
사용자 요구를 충족시키고 구성 항목 및 API를 제공하려는 시작점과 결합하여 구성 항목 및 노출된 API 항목을 얻을 수 있습니다. 디자인하고 싶은 항목:
구성 항목: 자동 재생 켜짐 여부, 기본 노래 목록 표시 상태, 기본 루프 모드 설정
API: 재생/일시 중지/토글, 루프 모드 전환, 음소거/계속, 목록 표시 상태전환, 이전곡/다음곡/곡전환, 현재 인스턴스 삭제
프로젝트 구조 정립 및 코딩 시작 :
우리는 webpack을 사용하기 때문에 CSS를 js로 직접 패키징하여 사용할 수 있도록 합니다. 사용자를 위한 플러그인:
require('./skPlayer.scss');
Extract Public Methods 플레이어에는 추출해야 할 수 있는 많은 공개 메소드가 있습니다. 예를 들면 다음과 같습니다. 재생 진행률 표시줄 및 볼륨 진행률 표시줄을 클릭할 때 다음을 수행해야 합니다. 진행 점프를 위해 마우스와 진행률 표시줄의 왼쪽 끝 사이의 거리를 계산합니다. 시간은 초 단위로 계산됩니다. 단위 시간을 표준 시간 형식 등으로 변환합니다.
const Util = { leftDistance: (el) => { let left = el.offsetLeft; let scrollLeft;while (el.offsetParent) { el = el.offsetParent; left += el.offsetLeft; } scrollLeft = document.body.scrollLeft + document.documentElement.scrollLeft;return left - scrollLeft; }, timeFormat: (time) => { let tempMin = parseInt(time / 60); let tempSec = parseInt(time % 60); let curMin = tempMin < 10 ? ('0' + tempMin) : tempMin; let curSec = tempSec < 10 ? ('0' + tempSec) : tempSec;return curMin + ':' + curSec; }, percentFormat: (percent) => {return (percent * 100).toFixed(2) + '%'; }, ajax: (option) => { option.beforeSend && option.beforeSend(); let xhr = new XMLHttpRequest(); xhr.onreadystatechange = () => {if(xhr.readyState === 4){if(xhr.status >= 200 && xhr.status < 300){ option.success && option.success(xhr.responseText); }else{ option.fail && option.fail(xhr.status); } } }; xhr.open('GET',option.url); xhr.send(null); } };
초기 설계로 인해 플레이어의 고유성을 고려하여 하나의 인스턴스만 존재할 수 있도록 설계되었으며 전역 변수가 설정되었습니다. 현재 인스턴스가 존재하는지 확인하려면:
let instance = false;
사용하는 경우 ES6에서는 메인 로직을 생성자 안에 넣고 보편성과 API를 공용 함수 안에 넣습니다.
class skPlayer { constructor(option){ } template(){ } init(){ } bind(){ } prev(){ } next(){ } switchMusic(index){ } play(){ } pause(){ } toggle(){ } toggleList(){ } toggleMute(){ } switchMode(){ } destroy(){ } }
인스턴스 판단, 프로토타입 없이 빈 객체가 있는 경우, ES6에서는 생성자는 기본적으로 프로토타입이 있는 인스턴스를 반환합니다.
if(instance){ console.error('SKPlayer只能存在一个实例!');return Object.create(null); }else{ instance = true; }
구성 항목을 초기화하고 기본 구성은 사용자 구성과 병합됩니다.
const defaultOption = { ... };this.option = Object.assign({},defaultOption,option);
일반적으로 사용되는 속성은 인스턴스에 바인딩됩니다.
this.root = this.option.element;this.type = this.option.music.type;this.music = this.option.music.source;this.isMobile = /mobile/i.test(window.navigator.userAgent);
일부 공개 API 내부는 기본적으로 인스턴스를 가리키지만, 코드의 양을 줄이기 위해 작업 인터페이스와 API에서 함수를 호출하고 이벤트를 바인딩하는 코드 세트를 사용합니다. 이 지점이 변경되므로 이것을 바인딩합니다. 물론 바인딩을 통해 이벤트를 바인딩할 때 화살표 기능을 사용할 수도 있습니다.
this.toggle = this.toggle.bind(this);this.toggleList = this.toggleList.bind(this);this.toggleMute = this.toggleMute.bind(this);this.switchMode = this.switchMode.bind(this);
다음으로 ES6 문자열 템플릿을 사용하여 HTML 생성을 시작하고 페이지에 삽입합니다.
this.root.innerHTML = this.template();
다음으로 초기화합니다. 초기화 프로세스 중에 공통 DOM 노드가 바인딩되고 구성 항목이 초기화되며 작업 인터페이스가 초기화됩니다.
this.init();
init(){this.dom = { cover: this.root.querySelector('.skPlayer-cover'), playbutton: this.root.querySelector('.skPlayer-play-btn'), name: this.root.querySelector('.skPlayer-name'), author: this.root.querySelector('.skPlayer-author'), timeline_total: this.root.querySelector('.skPlayer-percent'), timeline_loaded: this.root.querySelector('.skPlayer-line-loading'), timeline_played: this.root.querySelector('.skPlayer-percent .skPlayer-line'), timetext_total: this.root.querySelector('.skPlayer-total'), timetext_played: this.root.querySelector('.skPlayer-cur'), volumebutton: this.root.querySelector('.skPlayer-icon'), volumeline_total: this.root.querySelector('.skPlayer-volume .skPlayer-percent'), volumeline_value: this.root.querySelector('.skPlayer-volume .skPlayer-line'), switchbutton: this.root.querySelector('.skPlayer-list-switch'), modebutton: this.root.querySelector('.skPlayer-mode'), musiclist: this.root.querySelector('.skPlayer-list'), musicitem: this.root.querySelectorAll('.skPlayer-list li') };this.audio = this.root.querySelector('.skPlayer-source');if(this.option.listshow){this.root.className = 'skPlayer-list-on'; }if(this.option.mode === 'singleloop'){this.audio.loop = true; }this.dom.musicitem[0].className = 'skPlayer-curMusic'; }
이벤트 바인딩, 주로 오디오 이벤트에 바인딩 및 운영 패널 이벤트:
this.bind();
bind(){this.updateLine = () => { let percent = this.audio.buffered.length ? (this.audio.buffered.end(this.audio.buffered.length - 1) / this.audio.duration) : 0;this.dom.timeline_loaded.style.width = Util.percentFormat(percent); };// this.audio.addEventListener('load', (e) => {// if(this.option.autoplay && this.isMobile){// this.play();// }// });this.audio.addEventListener('durationchange', (e) => {this.dom.timetext_total.innerHTML = Util.timeFormat(this.audio.duration);this.updateLine(); });this.audio.addEventListener('progress', (e) => {this.updateLine(); });this.audio.addEventListener('canplay', (e) => {if(this.option.autoplay && !this.isMobile){this.play(); } });this.audio.addEventListener('timeupdate', (e) => { let percent = this.audio.currentTime / this.audio.duration;this.dom.timeline_played.style.width = Util.percentFormat(percent);this.dom.timetext_played.innerHTML = Util.timeFormat(this.audio.currentTime); });//this.audio.addEventListener('seeked', (e) => {// this.play();//});this.audio.addEventListener('ended', (e) => {this.next(); });this.dom.playbutton.addEventListener('click', this.toggle);this.dom.switchbutton.addEventListener('click', this.toggleList);if(!this.isMobile){this.dom.volumebutton.addEventListener('click', this.toggleMute); }this.dom.modebutton.addEventListener('click', this.switchMode);this.dom.musiclist.addEventListener('click', (e) => { let target,index,curIndex;if(e.target.tagName.toUpperCase() === 'LI'){ target = e.target; }else{ target = e.target.parentElement; } index = parseInt(target.getAttribute('data-index')); curIndex = parseInt(this.dom.musiclist.querySelector('.skPlayer-curMusic').getAttribute('data-index'));if(index === curIndex){this.play(); }else{this.switchMusic(index + 1); } });this.dom.timeline_total.addEventListener('click', (event) => { let e = event || window.event; let percent = (e.clientX - Util.leftDistance(this.dom.timeline_total)) / this.dom.timeline_total.clientWidth;if(!isNaN(this.audio.duration)){this.dom.timeline_played.style.width = Util.percentFormat(percent);this.dom.timetext_played.innerHTML = Util.timeFormat(percent * this.audio.duration);this.audio.currentTime = percent * this.audio.duration; } });if(!this.isMobile){this.dom.volumeline_total.addEventListener('click', (event) => { let e = event || window.event; let percent = (e.clientX - Util.leftDistance(this.dom.volumeline_total)) / this.dom.volumeline_total.clientWidth;this.dom.volumeline_value.style.width = Util.percentFormat(percent);this.audio.volume = percent;if(this.audio.muted){this.toggleMute(); } }); } }
至此,核心代码基本完成,接下来就是自己根据需要完成API部分。
最后我们暴露模块:
module.exports = skPlayer;
一个HTML5音乐播放器就大功告成了 ~ !
위 내용은 HTML5 뮤직 플레이어의 예 구현의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!