mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-11-28 14:03:48 +03:00
Added custom audio player UI for audio card
refs https://github.com/TryGhost/Team/issues/1230 - adds custom css and js for custom audio card player - bumps `kg-default-cards` to add custom audio player ui for theme
This commit is contained in:
parent
3c19a8f014
commit
2aa353a1a0
186
core/frontend/src/cards/css/audio.css
Normal file
186
core/frontend/src/cards/css/audio.css
Normal file
@ -0,0 +1,186 @@
|
||||
.kg-audio-card {
|
||||
margin-top: 7vmin;
|
||||
}
|
||||
|
||||
.kg-audio-player-container button {
|
||||
padding: 0;
|
||||
border: 0;
|
||||
background: transparent;
|
||||
cursor: pointer;
|
||||
outline: none;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.kg-audio-player-container {
|
||||
position: relative;
|
||||
/* margin: 0 2.5% auto 2.5%; */
|
||||
width: 100%;
|
||||
/* 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;
|
||||
}
|
||||
.kg-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;
|
||||
}
|
||||
.kg-audio-player-container p {
|
||||
position: absolute;
|
||||
top: -18px;
|
||||
right: 5%;
|
||||
padding: 0 5px;
|
||||
margin: 0;
|
||||
font-size: 24px;
|
||||
background: #fff;
|
||||
}
|
||||
.kg-audio-play-icon {
|
||||
margin: 20px 2.5% 10px 2.5%;
|
||||
}
|
||||
.kg-audio-player-container path {
|
||||
stroke: #007db5;
|
||||
}
|
||||
.kg-audio-duration,
|
||||
.kg-audio-current-time {
|
||||
display: inline-block;
|
||||
width: 37px;
|
||||
text-align: center;
|
||||
font-size: 20px;
|
||||
margin: 28.5px 0 18.5px 0;
|
||||
float: left;
|
||||
}
|
||||
.kg-audio-player-container output {
|
||||
display: inline-block;
|
||||
width: 32px;
|
||||
text-align: center;
|
||||
font-size: 20px;
|
||||
margin: 10px 2.5% 0 5%;
|
||||
float: left;
|
||||
clear: left;
|
||||
}
|
||||
.kg-audio-volume-slider {
|
||||
margin: 10px 2.5% !important;
|
||||
width: 58% !important;
|
||||
}
|
||||
.kg-audio-volume-slider::-webkit-slider-runnable-track {
|
||||
background: rgba(0, 125, 181, 0.6) !important;
|
||||
}
|
||||
.kg-audio-volume-slider::-moz-range-track {
|
||||
background: rgba(0, 125, 181, 0.6) !important;
|
||||
}
|
||||
.kg-audio-volume-slider::-ms-fill-upper {
|
||||
background: rgba(0, 125, 181, 0.6) !important;
|
||||
}
|
||||
.kg-audio-volume-slider::before {
|
||||
width: var(--volume-before-width) !important;
|
||||
}
|
||||
.kg-audio-mute-icon {
|
||||
margin: 0 2.5%;
|
||||
}
|
||||
.kg-audio-player-container 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;
|
||||
}
|
||||
.kg-audio-player-container 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));
|
||||
}
|
||||
.kg-audio-player-container input[type="range"]::before {
|
||||
position: absolute;
|
||||
content: "";
|
||||
top: 8px;
|
||||
left: 0;
|
||||
width: var(--seek-before-width);
|
||||
height: 3px;
|
||||
background-color: #007db5;
|
||||
cursor: pointer;
|
||||
}
|
||||
.kg-audio-player-container 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;
|
||||
}
|
||||
.kg-audio-player-container input[type="range"]:active::-webkit-slider-thumb {
|
||||
transform: scale(1.2);
|
||||
background: #007db5;
|
||||
}
|
||||
.kg-audio-player-container 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));
|
||||
}
|
||||
.kg-audio-player-container input[type="range"]::-moz-range-progress {
|
||||
background-color: #007db5;
|
||||
}
|
||||
.kg-audio-player-container input[type="range"]::-moz-focus-outer {
|
||||
border: 0;
|
||||
}
|
||||
.kg-audio-player-container 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;
|
||||
}
|
||||
.kg-audio-player-container input[type="range"]:active::-moz-range-thumb {
|
||||
transform: scale(1.2);
|
||||
background: #007db5;
|
||||
}
|
||||
|
||||
.kg-audio-player-container input[type="range"]::-ms-track {
|
||||
width: 100%;
|
||||
height: 3px;
|
||||
cursor: pointer;
|
||||
background: transparent;
|
||||
border: solid transparent;
|
||||
color: transparent;
|
||||
}
|
||||
.kg-audio-player-container input[type="range"]::-ms-fill-lower {
|
||||
background-color: #007db5;
|
||||
}
|
||||
.kg-audio-player-container 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));
|
||||
}
|
||||
.kg-audio-player-container 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;
|
||||
}
|
||||
.kg-audio-player-container input[type="range"]:active::-ms-thumb {
|
||||
transform: scale(1.2);
|
||||
background: #007db5;
|
||||
}
|
137
core/frontend/src/cards/js/audio.js
Normal file
137
core/frontend/src/cards/js/audio.js
Normal file
@ -0,0 +1,137 @@
|
||||
const handleAudioPlayer = function (audioElementContainer) {
|
||||
const audioPlayerContainer = audioElementContainer.querySelector('.kg-audio-player-container');
|
||||
const playIconContainer = audioElementContainer.querySelector('.kg-audio-play-icon');
|
||||
const seekSlider = audioElementContainer.querySelector('.kg-audio-seek-slider');
|
||||
const volumeSlider = audioElementContainer.querySelector('.kg-audio-volume-slider');
|
||||
const muteIconContainer = audioElementContainer.querySelector('.kg-audio-mute-icon');
|
||||
const playbackRateContainer = audioElementContainer.querySelector('.kg-audio-playback-rate');
|
||||
const audio = audioElementContainer.querySelector('audio');
|
||||
const durationContainer = audioElementContainer.querySelector('.kg-audio-duration');
|
||||
const currentTimeContainer = audioElementContainer.querySelector('.kg-audio-current-time');
|
||||
const outputContainer = audioElementContainer.querySelector('.kg-audio-volume-output');
|
||||
let playState = 'play';
|
||||
let muteState = 'unmute';
|
||||
let playbackRate = 1.0;
|
||||
let raf = null;
|
||||
|
||||
audio.src = audioElementContainer.getAttribute('data-kg-audio-src');
|
||||
|
||||
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 = () => {
|
||||
if (audio.buffered.length > 0) {
|
||||
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();
|
||||
requestAnimationFrame(whilePlaying);
|
||||
playState = 'pause';
|
||||
playIconContainer.textContent = '||';
|
||||
} else {
|
||||
audio.pause();
|
||||
cancelAnimationFrame(raf);
|
||||
playState = 'play';
|
||||
playIconContainer.textContent = '>';
|
||||
}
|
||||
});
|
||||
|
||||
muteIconContainer.addEventListener('click', () => {
|
||||
if (muteState === 'unmute') {
|
||||
audio.muted = true;
|
||||
muteState = 'mute';
|
||||
muteIconContainer.textContent = 'UM';
|
||||
} else {
|
||||
audio.muted = false;
|
||||
muteState = 'unmute';
|
||||
muteIconContainer.textContent = 'M';
|
||||
}
|
||||
});
|
||||
|
||||
playbackRateContainer.addEventListener('click', () => {
|
||||
if (playbackRate === 1.0) {
|
||||
audio.playbackRate = 2;
|
||||
playbackRate = 2;
|
||||
playbackRateContainer.textContent = '2x';
|
||||
} else {
|
||||
audio.playbackRate = 1.0;
|
||||
playbackRate = 1.0;
|
||||
playbackRateContainer.textContent = '1x';
|
||||
}
|
||||
});
|
||||
|
||||
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;
|
||||
});
|
||||
}
|
||||
|
||||
const audioCardElements = document.querySelectorAll('.kg-audio-card');
|
||||
|
||||
for (let i = 0; i < audioCardElements.length; i++) {
|
||||
handleAudioPlayer(audioCardElements[i]);
|
||||
}
|
||||
|
@ -73,7 +73,7 @@
|
||||
"@tryghost/job-manager": "0.8.16",
|
||||
"@tryghost/kg-card-factory": "3.1.0",
|
||||
"@tryghost/kg-default-atoms": "3.1.0",
|
||||
"@tryghost/kg-default-cards": "5.9.3",
|
||||
"@tryghost/kg-default-cards": "5.9.4",
|
||||
"@tryghost/kg-markdown-html-renderer": "5.1.0",
|
||||
"@tryghost/kg-mobiledoc-html-renderer": "5.3.0",
|
||||
"@tryghost/limit-service": "1.0.6",
|
||||
|
@ -1442,10 +1442,10 @@
|
||||
resolved "https://registry.yarnpkg.com/@tryghost/kg-default-atoms/-/kg-default-atoms-3.1.0.tgz#4daff7104c1f261b1b816dd75ae4104009f9c1e6"
|
||||
integrity sha512-FfROzVgqJWqJ7cVdS9dcALz7rnzNfV8zcrymAJrDTHDsqzAdMfSLe1tNQRm8zas9pyZLsD8zBGmLxG9dr4WFSA==
|
||||
|
||||
"@tryghost/kg-default-cards@5.9.3":
|
||||
version "5.9.3"
|
||||
resolved "https://registry.yarnpkg.com/@tryghost/kg-default-cards/-/kg-default-cards-5.9.3.tgz#3f70ceb0bb24330bc7610b9caab2cf6ae4c53cd8"
|
||||
integrity sha512-LbO7vQN8CDsTe7AcTGk9/C3APJi70gIg9wS670GIr+YvIxwh24aV/OrQL2CgSC8sFO+bwVsPoODbylL0FwXIjg==
|
||||
"@tryghost/kg-default-cards@5.9.4":
|
||||
version "5.9.4"
|
||||
resolved "https://registry.yarnpkg.com/@tryghost/kg-default-cards/-/kg-default-cards-5.9.4.tgz#a09fb13cc3ae2bc47f5763a27cefb92c60df263a"
|
||||
integrity sha512-3uu5iNuvl+7AxIy4gYHmT2osvaRKtC0QqKtXI7ITTMe+XMC7EJ6eCSY7p9e7iY5NDNBpSD6RWY88v4jS57fIQg==
|
||||
dependencies:
|
||||
"@tryghost/kg-markdown-html-renderer" "^5.1.0"
|
||||
"@tryghost/url-utils" "^2.0.0"
|
||||
|
Loading…
Reference in New Issue
Block a user