No edit summary |
No edit summary |
||
Line 31: | Line 31: | ||
}); | }); | ||
}); | }); | ||
import lottieWeb from 'https://cdn.skypack.dev/lottie-web'; | |||
class AudioPlayer extends HTMLElement { | |||
constructor() { | |||
super(); | |||
const template = document.querySelector('template'); | |||
const templateContent = template.content; | |||
const shadow = this.attachShadow({mode: 'open'}); | |||
shadow.appendChild(templateContent.cloneNode(true)); | |||
} | |||
connectedCallback() { | |||
everything(this); | |||
} | |||
} | |||
const everything = function(element) { | |||
const shadow = element.shadowRoot; | |||
const audioPlayerContainer = shadow.getElementById('audio-player-container'); | |||
const playIconContainer = shadow.getElementById('play-icon'); | |||
const seekSlider = shadow.getElementById('seek-slider'); | |||
const volumeSlider = shadow.getElementById('volume-slider'); | |||
const muteIconContainer = shadow.getElementById('mute-icon'); | |||
const audio = shadow.querySelector('audio'); | |||
const durationContainer = shadow.getElementById('duration'); | |||
const currentTimeContainer = shadow.getElementById('current-time'); | |||
const outputContainer = shadow.getElementById('volume-output'); | |||
let playState = 'play'; | |||
let muteState = 'unmute'; | |||
let raf = null; | |||
audio.src = element.getAttribute('data-src'); | |||
const playAnimation = lottieWeb.loadAnimation({ | |||
container: playIconContainer, | |||
path: 'https://maxst.icons8.com/vue-static/landings/animated-icons/icons/pause/pause.json', | |||
renderer: 'svg', | |||
loop: false, | |||
autoplay: false, | |||
name: "Play Animation", | |||
}); | |||
const muteAnimation = lottieWeb.loadAnimation({ | |||
container: muteIconContainer, | |||
path: 'https://maxst.icons8.com/vue-static/landings/animated-icons/icons/mute/mute.json', | |||
renderer: 'svg', | |||
loop: false, | |||
autoplay: false, | |||
name: "Mute Animation", | |||
}); | |||
playAnimation.goToAndStop(14, true); | |||
const whilePlaying = () => { | |||
seekSlider.value = Math.floor(audio.currentTime); | |||
currentTimeContainer.textContent = calculateTime(seekSlider.value); | |||
audioPlayerContainer.style.setProperty('--seek-before-width', `${seekSlider.value / seekSlider.max * 100}%`); | |||
raf = requestAnimationFrame(whilePlaying); | |||
} | |||
const showRangeProgress = (rangeInput) => { | |||
if(rangeInput === seekSlider) audioPlayerContainer.style.setProperty('--seek-before-width', rangeInput.value / rangeInput.max * 100 + '%'); | |||
else audioPlayerContainer.style.setProperty('--volume-before-width', rangeInput.value / rangeInput.max * 100 + '%'); | |||
} | |||
const calculateTime = (secs) => { | |||
const minutes = Math.floor(secs / 60); | |||
const seconds = Math.floor(secs % 60); | |||
const returnedSeconds = seconds < 10 ? `0${seconds}` : `${seconds}`; | |||
return `${minutes}:${returnedSeconds}`; | |||
} | |||
const displayDuration = () => { | |||
durationContainer.textContent = calculateTime(audio.duration); | |||
} | |||
const setSliderMax = () => { | |||
seekSlider.max = Math.floor(audio.duration); | |||
} | |||
const displayBufferedAmount = () => { | |||
const bufferedAmount = Math.floor(audio.buffered.end(audio.buffered.length - 1)); | |||
audioPlayerContainer.style.setProperty('--buffered-width', `${(bufferedAmount / seekSlider.max) * 100}%`); | |||
} | |||
if (audio.readyState > 0) { | |||
displayDuration(); | |||
setSliderMax(); | |||
displayBufferedAmount(); | |||
} else { | |||
audio.addEventListener('loadedmetadata', () => { | |||
displayDuration(); | |||
setSliderMax(); | |||
displayBufferedAmount(); | |||
}); | |||
} | |||
playIconContainer.addEventListener('click', () => { | |||
if(playState === 'play') { | |||
audio.play(); | |||
playAnimation.playSegments([14, 27], true); | |||
requestAnimationFrame(whilePlaying); | |||
playState = 'pause'; | |||
} else { | |||
audio.pause(); | |||
playAnimation.playSegments([0, 14], true); | |||
cancelAnimationFrame(raf); | |||
playState = 'play'; | |||
} | |||
}); | |||
muteIconContainer.addEventListener('click', () => { | |||
if(muteState === 'unmute') { | |||
muteAnimation.playSegments([0, 15], true); | |||
audio.muted = true; | |||
muteState = 'mute'; | |||
} else { | |||
muteAnimation.playSegments([15, 25], true); | |||
audio.muted = false; | |||
muteState = 'unmute'; | |||
} | |||
}); | |||
audio.addEventListener('progress', displayBufferedAmount); | |||
seekSlider.addEventListener('input', (e) => { | |||
showRangeProgress(e.target); | |||
currentTimeContainer.textContent = calculateTime(seekSlider.value); | |||
if(!audio.paused) { | |||
cancelAnimationFrame(raf); | |||
} | |||
}); | |||
seekSlider.addEventListener('change', () => { | |||
audio.currentTime = seekSlider.value; | |||
if(!audio.paused) { | |||
requestAnimationFrame(whilePlaying); | |||
} | |||
}); | |||
volumeSlider.addEventListener('input', (e) => { | |||
const value = e.target.value; | |||
showRangeProgress(e.target); | |||
outputContainer.textContent = value; | |||
audio.volume = value / 100; | |||
}); | |||
if('mediaSession' in navigator) { | |||
navigator.mediaSession.metadata = new MediaMetadata({ | |||
title: 'Komorebi', | |||
artist: 'Anitek', | |||
album: 'MainStay', | |||
artwork: [ | |||
{ src: 'https://assets.codepen.io/4358584/1.300.jpg', sizes: '96x96', type: 'image/png' }, | |||
{ src: 'https://assets.codepen.io/4358584/1.300.jpg', sizes: '128x128', type: 'image/png' }, | |||
{ src: 'https://assets.codepen.io/4358584/1.300.jpg', sizes: '192x192', type: 'image/png' }, | |||
{ src: 'https://assets.codepen.io/4358584/1.300.jpg', sizes: '256x256', type: 'image/png' }, | |||
{ src: 'https://assets.codepen.io/4358584/1.300.jpg', sizes: '384x384', type: 'image/png' }, | |||
{ src: 'https://assets.codepen.io/4358584/1.300.jpg', sizes: '512x512', type: 'image/png' } | |||
] | |||
}); | |||
navigator.mediaSession.setActionHandler('play', () => { | |||
if(playState === 'play') { | |||
audio.play(); | |||
playAnimation.playSegments([14, 27], true); | |||
requestAnimationFrame(whilePlaying); | |||
playState = 'pause'; | |||
} else { | |||
audio.pause(); | |||
playAnimation.playSegments([0, 14], true); | |||
cancelAnimationFrame(raf); | |||
playState = 'play'; | |||
} | |||
}); | |||
navigator.mediaSession.setActionHandler('pause', () => { | |||
if(playState === 'play') { | |||
audio.play(); | |||
playAnimation.playSegments([14, 27], true); | |||
requestAnimationFrame(whilePlaying); | |||
playState = 'pause'; | |||
} else { | |||
audio.pause(); | |||
playAnimation.playSegments([0, 14], true); | |||
cancelAnimationFrame(raf); | |||
playState = 'play'; | |||
} | |||
}); | |||
navigator.mediaSession.setActionHandler('seekbackward', (details) => { | |||
audio.currentTime = audio.currentTime - (details.seekOffset || 10); | |||
}); | |||
navigator.mediaSession.setActionHandler('seekforward', (details) => { | |||
audio.currentTime = audio.currentTime + (details.seekOffset || 10); | |||
}); | |||
navigator.mediaSession.setActionHandler('seekto', (details) => { | |||
if (details.fastSeek && 'fastSeek' in audio) { | |||
audio.fastSeek(details.seekTime); | |||
return; | |||
} | |||
audio.currentTime = details.seekTime; | |||
}); | |||
navigator.mediaSession.setActionHandler('stop', () => { | |||
audio.currentTime = 0; | |||
seekSlider.value = 0; | |||
audioPlayerContainer.style.setProperty('--seek-before-width', '0%'); | |||
currentTimeContainer.textContent = '0:00'; | |||
if(playState === 'pause') { | |||
playAnimation.playSegments([0, 14], true); | |||
cancelAnimationFrame(raf); | |||
playState = 'play'; | |||
} | |||
}); | |||
} | |||
} | |||
customElements.define('audio-player', AudioPlayer); | |||
/* GreenAudioPlayer.init({*/ | /* GreenAudioPlayer.init({*/ | ||
/* selector: '.player', // inits Green Audio Player on each audio container that has class "player"*/ | /* selector: '.player', // inits Green Audio Player on each audio container that has class "player"*/ | ||
/* stopOthersOnPlay: true*/ | /* stopOthersOnPlay: true*/ | ||
/* });*/ | /* });*/ |
Revision as of 21:50, 12 June 2023
/* Any JavaScript here will be loaded for all users on every page load. */ mw.hook('wikipage.content').add(function($content) { var $bandcamp = $content.find( '.bandcamp:not(.loaded)' ); if ( !$bandcamp.length ) return; $bandcamp.each( function() { var elem = $( this ); var width = elem.attr( 'data-width' ), height = elem.attr( 'data-height' ), data_src = elem.attr( 'data-src' ); if ( !/^https?:\/\/bandcamp\.com\//.test( data_src ) ) return; elem.empty(); var is_px = [ true, true ]; // width, height if ( /%/.test( width ) || !/\d+/.test( width ) ) is_px[ 0 ] = false; if ( /%/.test( height ) ) is_px[ 1 ] = false; var frame_width = parseFloat( width, 10 ) || 100; frame_height = height ? ( parseFloat( height, 10 ) || 'auto' ) : ''; $( '<iframe />', { style: 'border: 0', width: frame_width + ( is_px[ 0 ] ? 'px' : '%' ), height: frame_height + ( frame_height ? ( is_px[ 1 ] ? 'px': '%' ) : '' ), src: data_src }).appendTo( elem ); elem.addClass( 'loaded' ); }); }); import lottieWeb from 'https://cdn.skypack.dev/lottie-web'; class AudioPlayer extends HTMLElement { constructor() { super(); const template = document.querySelector('template'); const templateContent = template.content; const shadow = this.attachShadow({mode: 'open'}); shadow.appendChild(templateContent.cloneNode(true)); } connectedCallback() { everything(this); } } const everything = function(element) { const shadow = element.shadowRoot; const audioPlayerContainer = shadow.getElementById('audio-player-container'); const playIconContainer = shadow.getElementById('play-icon'); const seekSlider = shadow.getElementById('seek-slider'); const volumeSlider = shadow.getElementById('volume-slider'); const muteIconContainer = shadow.getElementById('mute-icon'); const audio = shadow.querySelector('audio'); const durationContainer = shadow.getElementById('duration'); const currentTimeContainer = shadow.getElementById('current-time'); const outputContainer = shadow.getElementById('volume-output'); let playState = 'play'; let muteState = 'unmute'; let raf = null; audio.src = element.getAttribute('data-src'); const playAnimation = lottieWeb.loadAnimation({ container: playIconContainer, path: 'https://maxst.icons8.com/vue-static/landings/animated-icons/icons/pause/pause.json', renderer: 'svg', loop: false, autoplay: false, name: "Play Animation", }); const muteAnimation = lottieWeb.loadAnimation({ container: muteIconContainer, path: 'https://maxst.icons8.com/vue-static/landings/animated-icons/icons/mute/mute.json', renderer: 'svg', loop: false, autoplay: false, name: "Mute Animation", }); playAnimation.goToAndStop(14, true); const whilePlaying = () => { seekSlider.value = Math.floor(audio.currentTime); currentTimeContainer.textContent = calculateTime(seekSlider.value); audioPlayerContainer.style.setProperty('--seek-before-width', `${seekSlider.value / seekSlider.max * 100}%`); raf = requestAnimationFrame(whilePlaying); } const showRangeProgress = (rangeInput) => { if(rangeInput === seekSlider) audioPlayerContainer.style.setProperty('--seek-before-width', rangeInput.value / rangeInput.max * 100 + '%'); else audioPlayerContainer.style.setProperty('--volume-before-width', rangeInput.value / rangeInput.max * 100 + '%'); } const calculateTime = (secs) => { const minutes = Math.floor(secs / 60); const seconds = Math.floor(secs % 60); const returnedSeconds = seconds < 10 ? `0${seconds}` : `${seconds}`; return `${minutes}:${returnedSeconds}`; } const displayDuration = () => { durationContainer.textContent = calculateTime(audio.duration); } const setSliderMax = () => { seekSlider.max = Math.floor(audio.duration); } const displayBufferedAmount = () => { const bufferedAmount = Math.floor(audio.buffered.end(audio.buffered.length - 1)); audioPlayerContainer.style.setProperty('--buffered-width', `${(bufferedAmount / seekSlider.max) * 100}%`); } if (audio.readyState > 0) { displayDuration(); setSliderMax(); displayBufferedAmount(); } else { audio.addEventListener('loadedmetadata', () => { displayDuration(); setSliderMax(); displayBufferedAmount(); }); } playIconContainer.addEventListener('click', () => { if(playState === 'play') { audio.play(); playAnimation.playSegments([14, 27], true); requestAnimationFrame(whilePlaying); playState = 'pause'; } else { audio.pause(); playAnimation.playSegments([0, 14], true); cancelAnimationFrame(raf); playState = 'play'; } }); muteIconContainer.addEventListener('click', () => { if(muteState === 'unmute') { muteAnimation.playSegments([0, 15], true); audio.muted = true; muteState = 'mute'; } else { muteAnimation.playSegments([15, 25], true); audio.muted = false; muteState = 'unmute'; } }); audio.addEventListener('progress', displayBufferedAmount); seekSlider.addEventListener('input', (e) => { showRangeProgress(e.target); currentTimeContainer.textContent = calculateTime(seekSlider.value); if(!audio.paused) { cancelAnimationFrame(raf); } }); seekSlider.addEventListener('change', () => { audio.currentTime = seekSlider.value; if(!audio.paused) { requestAnimationFrame(whilePlaying); } }); volumeSlider.addEventListener('input', (e) => { const value = e.target.value; showRangeProgress(e.target); outputContainer.textContent = value; audio.volume = value / 100; }); if('mediaSession' in navigator) { navigator.mediaSession.metadata = new MediaMetadata({ title: 'Komorebi', artist: 'Anitek', album: 'MainStay', artwork: [ { src: 'https://assets.codepen.io/4358584/1.300.jpg', sizes: '96x96', type: 'image/png' }, { src: 'https://assets.codepen.io/4358584/1.300.jpg', sizes: '128x128', type: 'image/png' }, { src: 'https://assets.codepen.io/4358584/1.300.jpg', sizes: '192x192', type: 'image/png' }, { src: 'https://assets.codepen.io/4358584/1.300.jpg', sizes: '256x256', type: 'image/png' }, { src: 'https://assets.codepen.io/4358584/1.300.jpg', sizes: '384x384', type: 'image/png' }, { src: 'https://assets.codepen.io/4358584/1.300.jpg', sizes: '512x512', type: 'image/png' } ] }); navigator.mediaSession.setActionHandler('play', () => { if(playState === 'play') { audio.play(); playAnimation.playSegments([14, 27], true); requestAnimationFrame(whilePlaying); playState = 'pause'; } else { audio.pause(); playAnimation.playSegments([0, 14], true); cancelAnimationFrame(raf); playState = 'play'; } }); navigator.mediaSession.setActionHandler('pause', () => { if(playState === 'play') { audio.play(); playAnimation.playSegments([14, 27], true); requestAnimationFrame(whilePlaying); playState = 'pause'; } else { audio.pause(); playAnimation.playSegments([0, 14], true); cancelAnimationFrame(raf); playState = 'play'; } }); navigator.mediaSession.setActionHandler('seekbackward', (details) => { audio.currentTime = audio.currentTime - (details.seekOffset || 10); }); navigator.mediaSession.setActionHandler('seekforward', (details) => { audio.currentTime = audio.currentTime + (details.seekOffset || 10); }); navigator.mediaSession.setActionHandler('seekto', (details) => { if (details.fastSeek && 'fastSeek' in audio) { audio.fastSeek(details.seekTime); return; } audio.currentTime = details.seekTime; }); navigator.mediaSession.setActionHandler('stop', () => { audio.currentTime = 0; seekSlider.value = 0; audioPlayerContainer.style.setProperty('--seek-before-width', '0%'); currentTimeContainer.textContent = '0:00'; if(playState === 'pause') { playAnimation.playSegments([0, 14], true); cancelAnimationFrame(raf); playState = 'play'; } }); } } customElements.define('audio-player', AudioPlayer); /* GreenAudioPlayer.init({*/ /* selector: '.player', // inits Green Audio Player on each audio container that has class "player"*/ /* stopOthersOnPlay: true*/ /* });*/