WordPress × Wavesurfer JS – наконец-то дошли руки поделиться своим опытом использования wavesurfer.js в связке с сайтом на WordPress.
Когда я делал вторую версию своего сайта и решил обновить раздел с музыкой мне захотелось сделать плеер с визуализаций частотной диаграммы, как у SoundCloud. Я достаточно быстро нашел wavesurfer.js и дальше начался процесс сбора информации с разных сайтов о том как его использовать. Постепенно я пришел к желаемому результату и решил им поделиться, возможно, это будет полезно для таких же начинающих разработчиков как и я.
Wavesurfer.js - это библиотека визуализации аудио с открытым исходным кодом для создания интерактивных, настраиваемых форм волны.
Я сделал шаблон страницы WordPress, который можно выбрать при создании страницы и поместил в него сам скрипт, верстку и дополнительные возможности, которые мне были нужны.
Список опций, которые есть в моём примере:
Аудио плеер в целом
Визуализация частотной диаграммы
Поиск по треку (кликом по частотной диаграмме)
Время общее и прошедшее время трека
Изображение обложка
Кнопка play /pause (иконка на кнопке меняется)
Регулятор громкости
Кнопка mute
Музыкальные стили в формате тегов
Кнопка-ссылка купить
Иконки-ссылки на музыкальные платформы
Описание трека и текст песни
Посмотрите пример страницы с плеером на моём сайте в разделе музыка.
Далее я постараюсь подробно рассказать особенности веб-разработки, которую я сделал.
Вот исходный код шаблона страницы WordPress с плеером Wavesurfer JS и дополнительными функциями.
Сразу уточню, что я использую версию скрипта wavesurfer.js 6.6.3 и размещаю её локально на своём веб-сервере. Это не последняя версия, вы можете найти её на GitHub проекта.
Код моего решения также доступен на GitHub.
<?php
/* Template Name: Music - Single */
get_header();
get_sidebar();
?>
<div class="music-single-container">
<div class="audio-player-container-wraper">
<div class="audio-player-container">
<div class="play-track-name">
<div id="playButton" class="play-button">
<img
id="playButtonIcon"
class="play-button-icon"
src="<?php echo get_template_directory_uri(); ?>/img/icons/play-thin.svg"
alt="Play Button"
/>
</div>
<h1 class="track-name"><?php the_title(); ?></h1>
<p class="music-style post-category-main"><?php echo the_tags('',' ',''); ?></p>
</div>
<div id="waveform" class="waveform">
<div id="loading_flag">
<!-- content set by JS -->
</div>
</div>
<span id="currentTime">00:00:00</span>
<span id="totalDuration">00:00:00</span>
<div class="volume-group">
<div class="volume-button">
<img
id="volumeIcon"
class="volume-icon"
src="<?php echo get_template_directory_uri(); ?>/img/icons/speaker-high-thin.svg"
alt="Volume"
/>
</div>
<input
id="volumeSlider"
class="volume-slider"
type="range"
name="volume-slider"
min="0"
max="100"
value="50"
aria-label="Volume"
/>
</div>
<a data-lightbox="post-image" class="music-single-cover" href="<?php echo get_the_post_thumbnail_url( get_the_id(), 'full' ); ?>">
<img src="<?php echo get_the_post_thumbnail_url( get_the_id(), 'medium' ); ?>" alt="<? the_title(); ?>" >
</a>
</div>
</div>
<div class="buy-streaming-wraper">
<div class="buy-streaming-container">
<div class="buy">
<a class="wp-block-button__link wp-element-button" href="<?php echo get_post_meta($post->ID, '%mp3_Buy', true); ?>" target="_blank"><?php include'img/icons/buy-thin.php'; ?>
<?php
$networksiteid = get_current_blog_id();
if( $networksiteid == 1 ){
echo "Купить";
} else {
echo "Buy";
}
?>
</a>
</div>
<div class="streaming">
<a class="streaming-link" id="streaming-link-vk-music" href="<?php echo get_post_meta($post->ID, '%VK_Music_link', true); ?>" target="_blank"><img decoding="async" src="https://tiku.ru/wp-content/uploads/2023/02/social-media-icons-svg-vk-music-01.svg" alt="VK Музыка иконка логотип" class="wp-image-6869"></a>
<a class="streaming-link" id="streaming-link-ya-music" href="<?php echo get_post_meta($post->ID, '%Yandex_Music_link', true); ?>" target="_blank"><img decoding="async" src="https://tiku.ru/wp-content/uploads/2023/02/social-media-icons-svg-ya-music-01.svg" alt="Яндекс Музыка иконка логотип" class="wp-image-6867"></a>
<a class="streaming-link" id="streaming-link-promo-dj" href="<?php echo get_post_meta($post->ID, '%Promo_DJ_link', true); ?>" target="_blank"><img decoding="async" src="https://tiku.ru/wp-content/uploads/2023/05/social-media-icons-svg-promo-dj-01.svg" alt="PromoDJ иконка логотип" class="wp-image-6867"></a>
<a class="streaming-link" id="streaming-link-soundcloud" href="<?php echo get_post_meta($post->ID, '%Soundcloud_link', true); ?>" target="_blank"><img decoding="async" src="https://tiku.ru/wp-content/uploads/2023/02/social-media-icons-svg-soundcloud-01.svg" alt="Soundcloud иконка логотип" class="wp-image-6871"></a>
<a class="streaming-link" id="streaming-link-bandcamp" href="<?php echo get_post_meta($post->ID, '%Bandcamp_link', true); ?>" target="_blank"><img decoding="async" src="https://tiku.ru/wp-content/uploads/2023/03/social-media-icons-svg-bandcamp-01.svg" alt="Bandcamp иконка логотип" class="wp-image-8002"></a>
<a class="streaming-link" id="streaming-link-beatport" href="<?php echo get_post_meta($post->ID, '%Beatport_link', true); ?>" target="_blank"><img decoding="async" src="https://tiku.ru/wp-content/uploads/2023/03/social-media-icons-svg-beatport-01.svg" alt="Beatport иконка логотип" class="wp-image-8161"></a>
<a class="streaming-link" id="streaming-link-apple-music" href="<?php echo get_post_meta($post->ID, '%Apple_Music_link', true); ?>" target="_blank"><img decoding="async" src="https://tiku.ru/wp-content/uploads/2023/02/social-media-icons-svg-apple-01.svg" alt="Apple Music иконка логотип" class="wp-image-7120"></a>
<a class="streaming-link" id="streaming-link-spotify" href="<?php echo get_post_meta($post->ID, '%Spotify_link', true); ?>" target="_blank"><img decoding="async" src="https://tiku.ru/wp-content/uploads/2023/03/social-media-icons-svg-spotify-01.svg" alt="Spotify иконка логотип" class="wp-image-8010"></a>
<a class="streaming-link" id="streaming-link-youtube" href="<?php echo get_post_meta($post->ID, '%YouTube_Music_link', true); ?>" target="_blank"><img decoding="async" src="https://tiku.ru/wp-content/uploads/2023/02/social-media-icons-svg-youtube-01.svg" alt="YouTube Music иконка логотип" class="wp-image-6842"></a>
<a class="streaming-link" id="streaming-link-amazon" href="<?php echo get_post_meta($post->ID, '%Amazon_link', true); ?>" target="_blank"><img decoding="async" src="https://tiku.ru/wp-content/uploads/2023/03/social-media-icons-svg-amazon-music-01.svg" alt="Amazon Music иконка логотип" class="wp-image-8009"></a>
<a class="streaming-link" id="streaming-link-deezer" href="<?php echo get_post_meta($post->ID, '%Deezer_link', true); ?>" target="_blank"><img decoding="async" src="https://tiku.ru/wp-content/uploads/2023/03/social-media-icons-svg-deezer-01.svg" alt="Deezer иконка логотип" class="wp-image-8008"></a>
</div>
</div>
</div>
<div class="music-single-desc">
<p><?php include'template-parts/likes-views.php'; ?> </p>
<?php the_content(); ?>
<p class="work-year"><?php the_date('j F Y'); ?></p>
<?php
include'template-parts/share.php';
include'template-parts/author.php';
?>
<p><?php include'template-parts/next-post.php'; ?></p>
<p><?php include'template-parts/tags.php'; ?></p>
</div>
</div>
<?php
include'template-parts/similar-pages.php';
?>
<?php if ( comments_open() ) { ?>
<div class="page-container width-940">
<div class="page-content-container">
<div><?php comments_template(); ?></div>
</div>
</div>
<?php } ?>
<?php
get_footer();
?>
<?php
$musicfile = get_post_meta($post->ID, '%mp3_URL', true);
$pcm = get_post_meta($post->ID, '%pcm', true);
?>
<script src="<?php echo get_template_directory_uri(); ?>/js/wavesurfer.js"></script>
<script>
/* utiluty functions */
const playButton = document.querySelector("#playButton")
const playButtonIcon = document.querySelector("#playButtonIcon")
const waveform = document.querySelector("#waveform")
const volumeIcon = document.querySelector("#volumeIcon")
const volumeSlider = document.querySelector("#volumeSlider")
const currentTime = document.querySelector("#currentTime")
const totalDuration = document.querySelector("#totalDuration")
// --------------------------------------------------------- //
/**
* Initialize Wavesurfer
* @returns a new Wavesurfer instance
*/
const initializeWavesurfer = () => {
return WaveSurfer.create({
backend: "MediaElement",
container: "#waveform",
responsive: true,
height: 80,
waveColor: "#9999ff",
progressColor: "#3300ff",
barWidth: 2,
barHeight: 1,
barGap: 2,
barRadius: 2,
})
}
// --------------------------------------------------------- //
// Functions
/**
* Toggle play button
*/
const togglePlay = () => {
wavesurfer.playPause()
const isPlaying = wavesurfer.isPlaying()
if (isPlaying) {
playButtonIcon.src = "https://tiku.ru/wp-content/themes/Tiku/img/icons/pause-thin.svg"
} else {
playButtonIcon.src = "https://tiku.ru/wp-content/themes/Tiku/img/icons/play-thin.svg"
}
}
/**
* Handles changing the volume slider input
* @param {event} e
*/
const handleVolumeChange = e => {
// Set volume as input value divided by 100
// NB: Wavesurfer only excepts volume value between 0 - 1
const volume = e.target.value / 100
wavesurfer.setVolume(volume)
// Save the value to local storage so it persists between page reloads
localStorage.setItem("audio-player-volume", volume)
}
/**
* Retrieves the volume value from local storage and sets the volume slider
*/
const setVolumeFromLocalStorage = () => {
// Retrieves the volume from local storage, or falls back to default value of 50
const volume = localStorage.getItem("audio-player-volume") * 100 || 50
volumeSlider.value = volume
}
/**
* Formats time as HH:MM:SS
* @param {number} seconds
* @returns time as HH:MM:SS
*/
const formatTimecode = seconds => {
return new Date(seconds * 1000).toISOString().substr(11, 8)
}
/**
* Toggles mute/unmute of the Wavesurfer volume
* Also changes the volume icon and disables the volume slider
*/
const toggleMute = () => {
wavesurfer.toggleMute()
const isMuted = wavesurfer.getMute()
if (isMuted) {
volumeIcon.src = "https://tiku.ru/wp-content/themes/Tiku/img/icons/speaker-x-thin.svg"
volumeSlider.disabled = true
} else {
volumeSlider.disabled = false
volumeIcon.src = "https://tiku.ru/wp-content/themes/Tiku/img/icons/speaker-high-thin.svg"
}
}
// --------------------------------------------------------- //
// Create a new instance and load the wavesurfer
const wavesurfer = initializeWavesurfer()
var musicFile = '<?php echo $musicfile; ?>'
var pcm = <?php echo $pcm; ?>;
wavesurfer.load(musicFile, pcm)
// --------------------------------------------------------- //
// Javascript Event listeners
window.addEventListener("load", setVolumeFromLocalStorage)
playButton.addEventListener("click", togglePlay)
volumeIcon.addEventListener("click", toggleMute)
volumeSlider.addEventListener("input", handleVolumeChange)
// --------------------------------------------------------- //
// Wavesurfer event listeners
wavesurfer.on("ready", () => {
// Set wavesurfer volume
wavesurfer.setVolume(volumeSlider.value / 100)
// Set audio track total duration
const duration = wavesurfer.getDuration()
totalDuration.innerHTML = formatTimecode(duration)
})
// Sets the timecode current timestamp as audio plays
wavesurfer.on("audioprocess", () => {
const time = wavesurfer.getCurrentTime()
currentTime.innerHTML = formatTimecode(time)
})
// Resets the play button icon after audio ends
wavesurfer.on("finish", () => {
playButtonIcon.src = "https://tiku.ru/wp-content/themes/Tiku/img/icons/play-thin.svg"
})
function UpdateLoadingFlag(Percentage) {
if (document.getElementById("loading_flag")) {
document.getElementById("loading_flag").innerText = "Загрузка " + Percentage + "%";
if (Percentage >= 100) {1
document.getElementById("loading_flag").style.display = "none";
} else {
document.getElementById("loading_flag").style.display = "block";
}
}
}
// show progress while loading sound
wavesurfer.on('loading', function(X, evt) {
UpdateLoadingFlag(X);
});
// clean up etc., when wavesurfer fires the "ready" event
wavesurfer.on('ready', function() {
console.log("ready fired");
});
document.getElementById("sun").onclick = function() {lightToDark()};
function lightToDark() {
wavesurfer.setWaveColor('#ff9999');
wavesurfer.setProgressColor('#ff6666');
}
document.getElementById("moon").onclick = function() {darkToLigh()};
function darkToLigh() {
wavesurfer.setWaveColor('#9999ff');
wavesurfer.setProgressColor('#3300ff');
}
if (document.getElementById("theme-link").href === "https://tiku.ru/wp-content/themes/Tiku/css/dark.css" || document.getElementById("theme-link").href === "https://tiku.ru/en/wp-content/themes/Tiku/css/dark.css") {
wavesurfer.setWaveColor('#ff9999');
wavesurfer.setProgressColor('#ff6666');
} else {
wavesurfer.setWaveColor('#9999ff');
wavesurfer.setProgressColor('#3300ff');
}
if(document.getElementById("streaming-link-amazon").getAttribute("href")!=="") {
document.getElementById("streaming-link-amazon").style.display = "inline-block";
}
if(document.getElementById("streaming-link-vk-music").getAttribute("href")!=="") {
document.getElementById("streaming-link-vk-music").style.display = "inline-block";
}
if(document.getElementById("streaming-link-ya-music").getAttribute("href")!=="") {
document.getElementById("streaming-link-ya-music").style.display = "inline-block";
}
if(document.getElementById("streaming-link-soundcloud").getAttribute("href")!=="") {
document.getElementById("streaming-link-soundcloud").style.display = "inline-block";
}
if(document.getElementById("streaming-link-bandcamp").getAttribute("href")!=="") {
document.getElementById("streaming-link-bandcamp").style.display = "inline-block";
}
if(document.getElementById("streaming-link-beatport").getAttribute("href")!=="") {
document.getElementById("streaming-link-beatport").style.display = "inline-block";
}
if(document.getElementById("streaming-link-apple-music").getAttribute("href")!=="") {
document.getElementById("streaming-link-apple-music").style.display = "inline-block";
}
if(document.getElementById("streaming-link-spotify").getAttribute("href")!=="") {
document.getElementById("streaming-link-spotify").style.display = "inline-block";
}
if(document.getElementById("streaming-link-youtube").getAttribute("href")!=="") {
document.getElementById("streaming-link-youtube").style.display = "inline-block";
}
if(document.getElementById("streaming-link-deezer").getAttribute("href")!=="") {
document.getElementById("streaming-link-deezer").style.display = "inline-block";
}
if(document.getElementById("streaming-link-promo-dj").getAttribute("href")!=="") {
document.getElementById("streaming-link-promo-dj").style.display = "inline-block";
}
/*
wavesurfer.on('waveform-ready', () => {
wavesurfer.exportPCM(1024, 10000, false);
})
*/
</script>
WordPress × Wavesurfer JS: описание решения
Далее я подробно постараюсь описать составные части решения.
Я активно использовал произвольные поля WordPress, например чтобы получить URL для .mp3 файла или PCM данные для построения визуализации звуковой волны.
В этой части кода создаются переменные в которые записываются дынные из произвольных полей WordPress.
<?php
$musicfile = get_post_meta($post->ID, '%mp3_URL', true);
$pcm = get_post_meta($post->ID, '%pcm', true);
?>
Вот так это выглядит в админке WordPress:
Вот этот блок кода настраивает сам wavesurfer, например то как будет выглядеть визуализация волны:
const initializeWavesurfer = () => {
return WaveSurfer.create({
backend: "MediaElement",
container: "#waveform",
responsive: true,
height: 80,
waveColor: "#9999ff",
progressColor: "#3300ff",
barWidth: 2,
barHeight: 1,
barGap: 2,
barRadius: 2,
})
}
В этом блоке кода происходит подключение .mp3 файла и PCM данных для быстрого рендеринга визуализации (об этом подробнее позже):
// --------------------------------------------------------- //
// Create a new instance and load the wavesurfer
const wavesurfer = initializeWavesurfer()
var musicFile = '<?php echo $musicfile; ?>'
var pcm = <?php echo $pcm; ?>;
wavesurfer.load(musicFile, pcm)
Далее важный момент, который я доделал уже после первого запуска. Одной из проблем было то, что визуализация звуковой волны отображалась только после того как аудио файл полностью загрузится (вы можете видеть в коде разметку и скрипт индикации загрузки). Часто это занимало значительное время для музыкальных треков, а когда я решил разместить также DJ-миксы, проблема стала очень серьезной – долгая загрузка.
Я уже знал, что это можно решить заранее указав PCM данные, но не знал, как эти данные получить. В итоге вот этот кусок кода помогает это сделать:
/*
wavesurfer.on('waveform-ready', () => {
wavesurfer.exportPCM(1024, 10000, false);
})
*/
Он закомментирован т. к. требуется только при размещении нового трека. Процесс немного кривой, но это подходит лично для меня:
Cначала я отключаю PCM данные вот тут:
Было:
wavesurfer.load(musicFile, pcm)
Стало:
wavesurfer.load(musicFile)
После этого плеер начинает работать в режиме загрузки аудио для построения визуализации звуковой волны. Я также убираю пометки комментария с этого кода:
wavesurfer.on('waveform-ready', () => {
wavesurfer.exportPCM(1024, 10000, false);
})
Заполняю всю прочую информацию, которую требует шаблон и запускаю предварительный просмотр страницы WordPress, начинается индикация загрузки трека и после её завершения автоматически открывается новая вкладка браузера в которой и содержаться PCM данные.
Далее я просто копирую их и вставляю в произвольное поле WordPress для этой страницы. Дальше надо вернуть PCM данные в коде:
wavesurfer.load(musicFile, pcm)
И закомментировать этот блок кода:
/*
wavesurfer.on('waveform-ready', () => {
wavesurfer.exportPCM(1024, 10000, false);
})
*/
После этого визуализация отображается моментально.
CSS-стили выглядят вот так и по ним, я думаю, комментарии излишни. Но если у вас возникнут вопросы, пожалуйста, напишите в комментариях, я постараюсь помочь.
/* Audio Player */
.music-single-container {
}
.music-single-desc {
margin: 0 auto;
max-width: 1600px;
padding: 2%;
}
.audio-player-container {
display: grid;
gap: 0;
grid-template-areas:
"play-track-name play-track-name volume-group cover"
"wave wave wave cover"
"currentTime currentTime totalDuration cover";
grid-template-rows: repeat(3, auto);
grid-template-columns: repeat(4, 1fr);
column-gap: 2%;
row-gap: 0;
justify-items: stretch;
align-items: start;
justify-content: space-evenly;
align-content: space-evenly;
padding: 2%;
margin: 0 auto;
max-width: 1920px;
background: linear-gradient(#eee, #fff);
}
.audio-player-container-wraper {
background: linear-gradient(#eee, #fff);
}
.music-single-cover, .music-single-cover:hover {
grid-area: cover;
width: 100%;
border: 0;
}
.music-single-cover img {
width: 100%;
height: auto;
aspect-ratio: 1 / 1;
box-shadow: var(--shadow-elevation-medium);
}
.play-track-name {
grid-area: play-track-name;
display: grid;
gap: 0;
grid-template-areas:
"play-pause track-name"
"play-pause track-name"
"play-pause music-style";
grid-template-rows: repeat(3, auto);
grid-template-columns: 80px auto;
column-gap: 2%;
row-gap: 0;
align-self : center;
}
.play-track-name h1, .play-track-name .music-style {
margin: 8px 0;
padding: 0;
line-height: 1.3;
}
.track-name {
grid-area: track-name;
text-align: start;
align-self : end;
}
.music-style {
grid-area: music-style;
}
.play-button {
grid-area: play-pause;
align-self : center;
width: 80px;
height: 80px;
padding: 20px;
}
#loading_flag {
padding: 25px 0;
text-align: center;
background: url(../img/icons/waveform-thin.svg) center center;
color: var(--accent-color);
font-weight: bold;
display: none;
}
#waveform {
height: 120px;
}
.play-button-icon, .volume-icon {
width: 100%;
height: auto;
aspect-ratio: 1 / 1;
}
.play-button, .volume-button {
background: var(--accent-color);
line-height: 0;
margin: 0;
border-radius: 50%;
cursor: pointer;
transition: 0.2s linear;
}
.play-button:hover, .volume-button:hover {
background: #3300cc;
transform: scale(1.1);
}
.volume-group {
grid-area: volume-group;
display: grid;
gap: 0;
grid-template-areas:
"volume-button volume-slider";
grid-template-rows: repeat(1, auto);
grid-template-columns: repeat(2, auto);
column-gap: 20px;
row-gap: 0;
justify-items: stretch;
align-items: start;
justify-content: space-evenly;
align-content: space-evenly;
align-self : center;
justify-self: end;
}
.volume-button {
grid-area: volume-button;
align-self : center;
width: 40px;
height: 40px;
padding: 8px;
}
.volume-slider {
grid-area: volume-slider;
align-self : center;
}
#currentTime {
grid-area: currentTime;
color: #999;
}
#totalDuration {
grid-area: totalDuration;
justify-self: end;
color: #999;
}
#waveform {
grid-area: wave;
align-self : end;
padding: 20px 0;
border-radius: 8px;
}
wave{
overflow-x: hidden!important;
border: 0!important;
cursor: pointer!important;
}
@media only screen and (max-width: 940px) {
.audio-player-container {
padding: 8vw 4vw;
display: grid;
gap: 0;
grid-template-areas:
"cover cover cover cover"
"play-track-name play-track-name play-track-name play-track-name"
"wave wave wave wave"
"currentTime volume-group volume-group totalDuration";
grid-template-rows: repeat(4, auto);
grid-template-columns: repeat(4, 1fr);
column-gap: 2%;
row-gap: 2%;
justify-items: stretch;
align-items: start;
justify-content: space-evenly;
align-content: space-evenly;
}
#currentTime, #totalDuration {
align-self : center;
font-size: 0.8em;
}
.volume-group {
column-gap: 2vw;
justify-self: center;
padding: 20px 0;
}
.music-single-desc {
padding: 4%;
}
.play-track-name {
column-gap: 2vw;
}
.play-track-name h1 {
font-size: 1.2em;
}
#waveform {
margin-left: -4vw;
margin-right: -4vw;
}
.play-track-name .music-style {
line-height: 1.9;
}
}
/* Input Range */
input[type=range] {
width: 100%;
margin: 7.6px 0;
background-color: transparent;
-webkit-appearance: none;
}
input[type=range]:focus {
outline: none;
}
input[type=range]::-webkit-slider-runnable-track {
background: rgba(153, 153, 255, 0.78);
border: 0;
border-radius: 25px;
width: 100%;
height: 4.8px;
cursor: pointer;
}
input[type=range]::-webkit-slider-thumb {
margin-top: -7.6px;
width: 20px;
height: 20px;
background: var(--accent-color);
border: 0;
border-radius: 11px;
cursor: pointer;
-webkit-appearance: none;
}
input[type=range]:focus::-webkit-slider-runnable-track {
background: #9e9eff;
}
input[type=range]::-moz-range-track {
background: rgba(153, 153, 255, 0.78);
border: 0;
border-radius: 25px;
width: 100%;
height: 4.8px;
cursor: pointer;
}
input[type=range]::-moz-range-thumb {
width: 20px;
height: 20px;
background: var(--accent-color);
border: 0;
border-radius: 11px;
cursor: pointer;
}
input[type=range]::-ms-track {
background: transparent;
border-color: transparent;
border-width: 8.5px 0;
color: transparent;
width: 100%;
height: 4.8px;
cursor: pointer;
}
input[type=range]::-ms-fill-lower {
background: #9494ff;
border: 0;
border-radius: 50px;
}
input[type=range]::-ms-fill-upper {
background: rgba(153, 153, 255, 0.78);
border: 0;
border-radius: 50px;
}
input[type=range]::-ms-thumb {
width: 20px;
height: 20px;
background: var(--accent-color);
border: 0;
border-radius: 11px;
cursor: pointer;
margin-top: 0px;
/*Needed to keep the Edge thumb centred*/
}
input[type=range]:focus::-ms-fill-lower {
background: rgba(153, 153, 255, 0.78);
}
input[type=range]:focus::-ms-fill-upper {
background: #9e9eff;
}
/*TODO: Use one of the selectors from https://stackoverflow.com/a/20541859/7077589 and figure out
how to remove the virtical space around the range input in IE*/
@supports (-ms-ime-align:auto) {
/* Pre-Chromium Edge only styles, selector taken from hhttps://stackoverflow.com/a/32202953/7077589 */
input[type=range] {
margin: 0;
/*Edge starts the margin from the thumb, not the track as other browsers do*/
}
}
/* Input Range */
.buy-streaming-wraper {
background: #f5f5f5;
}
.buy-streaming-container {
margin: 0 auto;
padding: 1vw 2vw;
max-width: 1600px;
display: grid;
gap: 0;
grid-template-areas:
"buy streaming-link";
grid-template-rows: repeat(1, auto);
grid-template-columns: repeat(2, 1fr);
column-gap: 20px;
row-gap: 0;
justify-items: stretch;
align-items: start;
justify-content: space-evenly;
align-content: space-evenly;
}
.streaming {
grid-area: streaming-link;
line-height: 0;
align-self : center;
}
.streaming-link {
width: 28px;
display: none;
border: 0;
filter:progid:DXImageTransform.Microsoft.Alpha(opacity=60);
-moz-opacity: 0.60;
-khtml-opacity: 0.60;
/* opacity: 0.60; */
}
.streaming-link:after {
content: ''!important;
}
.streaming-link:hover {
border: 0;
}
.streaming-link img {
width: 100%;
height: auto;
aspect-ratio: 1 / 1;
line-height: 0;
}
.buy {
grid-area: buy;
}
.buy svg {
width: 22px;
vertical-align: text-bottom;
}
.buy .button, .buy .wp-block-button__link {
margin: 0;
}
@media only screen and (max-width: 940px) {
.buy-streaming-container {
margin: 0 auto;
padding: 4vw;
display: grid;
gap: 0;
grid-template-areas:
"buy"
"streaming-link";
grid-template-rows: repeat(2, auto);
grid-template-columns: repeat(1, 1fr);
column-gap: 20px;
row-gap: 4vw;
justify-items: stretch;
align-items: start;
justify-content: space-evenly;
align-content: space-evenly;
}
.buy .button, .buy .wp-block-button__link {
width: 100%;
}
.streaming {
justify-self: center;
}
.streaming-link {
width: 28px;
}
}
/* Audio Player */
WordPress × Wavesurfer JS: заключение
Надеюсь эта статья будет полезна таким же начинающим разработчикам как и я.
Если у вас возникли какие-либо вопросы или предложения по моей реализации, пожалуйста, напишите мне или оставьте комментарий.
Автор: Тимофей Кузнецов aka Tiku Digital
winkyBrain
Я wp и php не использовал, но вам точно обязательно сначала брать ссылку на трек в стриминговых сервисах(видимо) через php, класть её в атрибут href, а потом искать все ссылки через document.getElementById(), чтобы в них проверить отличие href от пустой строки? Будто бы где-то по дороге можно было было создать к примеру словарь по тем же id стримингов, с boolean флагами о том, была ли получена ссылка или пустая строка.
Скрытый текст
Ну и само собой вот такого быть не должно, можно же вынести все id в массив(или document.getElementsByClassName() в вашем случае прекрасно сработает), пройтись по нему циклом и выполнить все те совершенно одинаковые проверки и операции. Зачем копипастить код, что случилось с DRY? Кстати подход массив + цикл позволил бы вам и сами ссылки таким же образом отрисовать
Tiku Автор
Здравствуйте! Спасибо за комментарий!
Я mp3 файлы треков загружаю через админку WP в медиа библиотеку, копирую URL и его уже вставляю как произвольное поле при редактировании страницы трека в админке WP. А оттуда оно уже попадает в переменную. Т. е. тут нет никакой зависимости от сторонних сервисов.
Да, это ужас, я понимаю, есть в планах нормально переделать через массив и циклом по нему.