|
|
(91 intermediate revisions by the same user not shown) |
Line 1: |
Line 1: |
| <noinclude>__NOTOC__ | | <noinclude>__NOTOC__ |
| | {{#widget:AudioPlayer2|mp3=/images/b/be/Lost_boy_v2_80_.mp3}} |
| | {{#widget:AudioPlayer2|mp3=/images/5/5a/Gloria_rough_08.12.12_guide_.mp3}} |
|
| |
|
| == Sample result == | | == Sample result == |
| {{#widget:AudioPlayer2|mp3=https://themidnight.wiki/images/9/9e/Carl_Sagan_Show_Intro.mp3}}
| | |
| </noinclude> | | </noinclude> |
|
| |
|
| <includeonly> | | <includeonly> |
| <html> | | <html> |
| | <style> |
| | .player { |
| | background: var(--tmw-after-gradient); |
| | width: 330px; |
| | height: 190px; |
| | position: relative; |
| | border-radius: 8px; |
| | box-shadow: -1px 4px 10px 5px hsl(216,100%,8%); |
| | z-index: 1; |
| | } |
| | .record { |
| | height: 175px; |
| | width: 175px; |
| | background-color: #181312; |
| | border-radius: 50%; |
| | position: absolute; |
| | top: 7px; |
| | left: 28px; |
| | display: flex; |
| | align-items: center; |
| | justify-content: center; |
| | } |
| | .record:before, |
| | .record:after { |
| | position: absolute; |
| | content: ""; |
| | border: 5px solid transparent; |
| | border-top-color: #2c2424; |
| | border-bottom-color: #2c2424; |
| | border-radius: 50%; |
| | } |
| | .record:before { |
| | height: 135px; |
| | width: 135px; |
| | } |
| | .record:after { |
| | height: 95px; |
| | width: 95px; |
| | } |
| | .player-label { |
| | background-color: #181312; |
| | height: 15px; |
| | width: 15px; |
| | border: 20px solid #fd184e; |
| | border-radius: 50%; |
| | } |
|
| |
|
| | .tone-arm { |
| | height: 90px; |
| | width: 6px; |
| | background-color: #ffffff; |
| | position: absolute; |
| | top: 25px; |
| | right: 95px; |
| | transition: 1s; |
| | transform-origin: top; |
| | } |
| | .control { |
| | background-color: #181312; |
| | height: 17px; |
| | width: 17px; |
| | border: 10px solid #2c2c2c; |
| | border-radius: 50%; |
| | position: absolute; |
| | top: -16px; |
| | left: -16px; |
| | } |
| | .tone-arm:before { |
| | content: ""; |
| | height: 40px; |
| | width: 6px; |
| | background-color: #ffffff; |
| | position: absolute; |
| | transform: rotate(30deg); |
| | bottom: -36px; |
| | right: 10px; |
| | } |
| | .tone-arm:after { |
| | content: ""; |
| | position: absolute; |
| | height: 0; |
| | width: 10px; |
| | border-top: 18px solid #b2aea6; |
| | border-left: 2px solid transparent; |
| | border-right: 2px solid transparent; |
| | top: 108px; |
| | right: 12.5px; |
| | transform: rotate(30deg); |
| | } |
| | .btn { |
| | height: 28px; |
| | width: 28px; |
| | background-color: #d11b66; |
| | border-radius: 50%; |
| | position: absolute; |
| | bottom: 5px; |
| | left: 7px; |
| | border: 3.5px solid #95003d; |
| | outline: none; |
| | padding: 0 !important; |
| | cursor: pointer; |
| | } |
| | .slider { |
| | -webkit-appearance: none; |
| | appearance: none; |
| | transform: rotate(-90deg); |
| | width: 90px; |
| | height: 7px; |
| | position: absolute; |
| | left: 252px; |
| | top: 60px; |
| | background-color: #d11b66; |
| | outline: none; |
| | border-radius: 3px; |
| | border: 6px solid #95003d; |
| | } |
| | .slider::-webkit-slider-thumb { |
| | -webkit-appearance: none; |
| | appearance: none; |
| | width: 10px; |
| | height: 12px; |
| | background-color: #ffffff; |
| | cursor: pointer; |
| | } |
| | .play { |
| | transform: rotate(30deg); |
| | transform-origin: top; |
| | transition: 1s; |
| | } |
| | .on { |
| | animation: spin 3s 1s linear infinite; |
| | } |
| | @keyframes spin { |
| | 100% { |
| | transform: rotate(360deg); |
| | } |
| | } |
|
| |
|
| <style>
| | .player-wrapper .sign-logo { |
| button {
| | width: 120px; |
| padding: 0;
| | height: 50.14px; |
| border: 0;
| | right: 6px; |
| background: transparent;
| | top: 131px; |
| cursor: pointer;
| | filter: none; |
| outline: none;
| |
| width: 40px;
| |
| height: 40px;
| |
| float: left;
| |
| }
| |
| #audio-player-container {
| |
| position: relative;
| |
| margin: 100px 2.5% auto 2.5%;
| |
| width: 95%;
| |
| max-width: 500px;
| |
| height: 132px;
| |
| background: #fff;
| |
| font-family: Arial, Helvetica, sans-serif;
| |
| --seek-before-width: 0%;
| |
| --volume-before-width: 100%;
| |
| --buffered-width: 0%;
| |
| letter-spacing: -0.5px;
| |
| }
| |
| #audio-player-container::before {
| |
| position: absolute;
| |
| content: '';
| |
| width: calc(100% + 4px);
| |
| height: calc(100% + 4px);
| |
| left: -2px;
| |
| top: -2px;
| |
| background: linear-gradient(to left, #007db5, #ff8a00);
| |
| z-index: -1;
| |
| }
| |
| p {
| |
| position: absolute;
| |
| top: -18px;
| |
| right: 5%;
| |
| padding: 0 5px;
| |
| margin: 0;
| |
| font-size: 28px;
| |
| background: #fff;
| |
| }
| |
| #play-icon {
| |
| margin: 20px 2.5% 10px 2.5%;
| |
| }
| |
| path {
| |
| stroke: #007db5;
| |
| }
| |
| .time {
| |
| display: inline-block;
| |
| width: 37px;
| |
| text-align: center;
| |
| font-size: 20px;
| |
| margin: 28.5px 0 18.5px 0;
| |
| float: left;
| |
| }
| |
| output {
| |
| display: inline-block;
| |
| width: 32px;
| |
| text-align: center;
| |
| font-size: 20px;
| |
| margin: 10px 2.5% 0 5%;
| |
| float: left;
| |
| clear: left;
| |
| }
| |
| #volume-slider {
| |
| margin: 10px 2.5%;
| |
| width: 58%;
| |
| }
| |
| #volume-slider::-webkit-slider-runnable-track {
| |
| background: rgba(0, 125, 181, 0.6);
| |
| }
| |
| #volume-slider::-moz-range-track {
| |
| background: rgba(0, 125, 181, 0.6);
| |
| }
| |
| #volume-slider::-ms-fill-upper {
| |
| background: rgba(0, 125, 181, 0.6);
| |
| }
| |
| #volume-slider::before {
| |
| width: var(--volume-before-width);
| |
| }
| |
| #mute-icon {
| |
| margin: 0 2.5%;
| |
| }
| |
| input[type="range"] {
| |
| position: relative;
| |
| -webkit-appearance: none;
| |
| width: 48%;
| |
| margin: 0;
| |
| padding: 0;
| |
| height: 19px;
| |
| margin: 30px 2.5% 20px 2.5%;
| |
| float: left;
| |
| outline: none;
| |
| }
| |
| input[type="range"]::-webkit-slider-runnable-track {
| |
| width: 100%;
| |
| height: 3px;
| |
| cursor: pointer;
| |
| background: linear-gradient(to right, rgba(0, 125, 181, 0.6) var(--buffered-width), rgba(0, 125, 181, 0.2) var(--buffered-width));
| |
| }
| |
| input[type="range"]::before {
| |
| position: absolute;
| |
| content: "";
| |
| top: 8px;
| |
| left: 0;
| |
| width: var(--seek-before-width);
| |
| height: 3px;
| |
| background-color: #007db5;
| |
| cursor: pointer;
| |
| }
| |
| input[type="range"]::-webkit-slider-thumb {
| |
| position: relative;
| |
| -webkit-appearance: none;
| |
| box-sizing: content-box;
| |
| border: 1px solid #007db5;
| |
| height: 15px;
| |
| width: 15px;
| |
| border-radius: 50%;
| |
| background-color: #fff;
| |
| cursor: pointer;
| |
| margin: -7px 0 0 0;
| |
| }
| |
| input[type="range"]:active::-webkit-slider-thumb {
| |
| transform: scale(1.2);
| |
| background: #007db5;
| |
| }
| |
| input[type="range"]::-moz-range-track {
| |
| width: 100%;
| |
| height: 3px;
| |
| cursor: pointer;
| |
| background: linear-gradient(to right, rgba(0, 125, 181, 0.6) var(--buffered-width), rgba(0, 125, 181, 0.2) var(--buffered-width));
| |
| }
| |
| input[type="range"]::-moz-range-progress {
| |
| background-color: #007db5;
| |
| }
| |
| input[type="range"]::-moz-focus-outer {
| |
| border: 0;
| |
| }
| |
| input[type="range"]::-moz-range-thumb {
| |
| box-sizing: content-box;
| |
| border: 1px solid #007db5;
| |
| height: 15px;
| |
| width: 15px;
| |
| border-radius: 50%;
| |
| background-color: #fff;
| |
| cursor: pointer;
| |
| }
| |
| input[type="range"]:active::-moz-range-thumb {
| |
| transform: scale(1.2);
| |
| background: #007db5;
| |
| }
| |
| input[type="range"]::-ms-track {
| |
| width: 100%;
| |
| height: 3px;
| |
| cursor: pointer;
| |
| background: transparent;
| |
| border: solid transparent;
| |
| color: transparent;
| |
| }
| |
| input[type="range"]::-ms-fill-lower {
| |
| background-color: #007db5;
| |
| }
| |
| input[type="range"]::-ms-fill-upper {
| |
| background: linear-gradient(to right, rgba(0, 125, 181, 0.6) var(--buffered-width), rgba(0, 125, 181, 0.2) var(--buffered-width));
| |
| }
| |
| input[type="range"]::-ms-thumb {
| |
| box-sizing: content-box;
| |
| border: 1px solid #007db5;
| |
| height: 15px;
| |
| width: 15px;
| |
| border-radius: 50%;
| |
| background-color: #fff;
| |
| cursor: pointer;
| |
| }
| |
| input[type="range"]:active::-ms-thumb {
| |
| transform: scale(1.2);
| |
| background: #007db5;
| |
| }
| |
| </style>
| |
| <script>
| |
| 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) {
| | .player-wrapper { |
| const shadow = element.shadowRoot;
| | display: flex; |
| | | flex-direction: column; |
| const audioPlayerContainer = shadow.getElementById('audio-player-container');
| | align-items: center; |
| 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';
| |
| }
| |
| });
| |
| }
| |
| } | | } |
| | | </style> |
| customElements.define('audio-player', AudioPlayer);
| | <div class="player-wrapper"> |
| </script> | | <div class="player"> |
| <div id="audio-player-container"> | | <div class="record"> |
| <audio src="" preload="metadata" loop></audio>
| | <div class="player-label"></div> |
| <p>audio player ish</p>
| | </div> |
| <button id="play-icon"></button>
| | <div class="tone-arm"> |
| <span id="current-time" class="time">0:00</span>
| | <div class="control"></div> |
| <input type="range" id="seek-slider" max="100" value="0">
| | </div> |
| <span id="duration" class="time">0:00</span>
| | <button class="btn"></button> |
| <output id="volume-output">100</output>
| | <div class="slider-container"> |
| <input type="range" id="volume-slider" max="100" value="100">
| | <input |
| <button id="mute-icon"></button>
| | type="range" |
| </div>
| | class="slider" |
| <audio-player data-src="<!--{$mp3|escape:'urlpathinfo'}-->"></audio-player> | | min="0" |
| | max="1" |
| | step="0.1" |
| | value="0.7" |
| | /> |
| | </div> |
| | <img alt="logo" width="347" height="145" class="sign-logo" src="https://themidnight.wiki/images/2/24/The_Midnight_Wiki_Logo_2023.png" /> |
| | </div> |
| | <audio loop class="my-song" src="<!--{$mp3|escape:'urlpathinfo'}-->" preload="auto"></audio> |
| | <p style="margin-bottom: 0;"><em>Press the button to play/pause</em></p> |
| | <p style="margin: 0;"><em>Adjust volume with slider</em></p> |
| | </div> |
| </html> | | </html> |
| </includeonly> | | </includeonly> |