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
