Significantly improve the media player UX

This commit is contained in:
Michael Fabian 'Xaymar' Dirks
2022-11-27 00:53:36 +01:00
parent 445d5f45f5
commit 60aaaf7132
4 changed files with 224 additions and 184 deletions
+73 -77
View File
@@ -1,85 +1,81 @@
{% assign file_info = site.static_files | where: "path", include.url %}
{% capture content %} {% capture content %}
{% if include.player %} {% if include.player %}
<div class="top"> <div class="top">
<select class="variant"></select> <select class="variant"></select>
</div> </div>
<div class="bottom"> <div class="bottom">
<span class="play"></span> <span class="play"></span>
<div class="playback">
<span class="time">0:00 / 0:00</span> <span class="time">0:00 / 0:00</span>
<input type="range" class="progress"> <input type="range" class="progress">
</div>
<div class="audio">
<span class="mute"></span> <span class="mute"></span>
<input type="range" class="volume"> <input type="range" class="volume">
<span class="fullscreen"></span>
</div> </div>
<span class="fullscreen"></span> {% endif %}
</div> {% if include.link %}<a href="{% if include.link == true %}{{ include.url }}{% else %}{{ include.link }}{% endif %}" {% if include.caption %}alt="{{ include.caption }}"{% endif %} target="_blank">{% endif %}
{% endif %} {% if include.type == "image" %}
{% if include.link %}<a href="{% if include.link == true %}{{ include.url }}{% else %}{{ include.link }}{% endif %}" {% if include.caption %}alt="{{ include.caption }}"{% endif %} target="_blank">{% endif %} <picture
{% assign file_info = site.static_files | where: "path", include.url %} class="content"
{% if include.type == "image" %} {% if include.width %}width="{{ include.width }}"{% endif %}
<picture {% if include.height %}height="{{ include.height }}"{% endif %}
class="content" {% if include.lazyload %}data-lazyload=""{% endif %}
{% if include.lazyload %}data-lazyload=""{% endif %} >
{% comment %} {% if include.lazyload %}<noscript>{% endif %}
src="{{ include.url | absolute_url }}" <img src="{{ include.url | absolute_url }}"
{% endcomment %} {% if include.alt %}alt="{{ include.alt }}"{% endif %}
{% if include.width %}width="{{ include.width }}"{% endif %} {% if include.width %}width="{{ include.width }}"{% endif %}
{% if include.height %}height="{{ include.height }}"{% endif %}> {% if include.height %}height="{{ include.height }}"{% endif %}>
{% if include.lazyload %}<noscript>{% endif %} {% if include.lazyload %}</noscript>{% endif %}
<img src="{{ include.url | absolute_url }}"
{% if include.alt %}alt="{{ include.alt }}"{% endif %} {% if include.content %}{{include.content}}{% endif %}
{% if include.width %}width="{{ include.width }}"{% endif %} </picture>
{% if include.height %}height="{{ include.height }}"{% endif %}> {% elsif include.type == "video" %}
{% if include.lazyload %}</noscript>{% endif %} <video
</picture> {% if include.player %}{% else %}controls{% endif %}
{% elsif include.type == "video" %} class="content"
<video {% if include.player %}{% else %}controls{% endif %}
class="content" {% if include.width %}width="{{ include.width }}"{% endif %}
{% if include.lazyload %}data-lazyload=""{% endif %} {% if include.height %}height="{{ include.height }}"{% endif %}
{% if include.poster %}poster="{{ include.poster | absolute_url }}"{% endif %}
{% if include.preload %}preload="{{ include.preload }}"{% endif %} {% if include.autoplay %}autoplay="true"{% endif %}
{% comment %} {% if include.muted %}muted="true"{% endif %}
src="{{ include.url | absolute_url }}" {% if include.loop %}loop="true"{% endif %}
{% endcomment %} {% if include.poster %}poster="{{ include.poster | absolute_url }}"{% endif %}
{% if include.width %}width="{{ include.width }}"{% endif %}
{% if include.height %}height="{{ include.height }}"{% endif %} {% if include.preload %}preload="{{ include.preload }}"{% endif %}
{% if include.autoplay %}autoplay="true"{% endif %} {% if include.lazyload %}data-lazyload=""{% endif %}
{% if include.muted %}muted="true"{% endif %} >
{% if include.loop %}loop="true"{% endif %}> {% if include.lazyload %}<noscript>{% endif %}
{% if include.lazyload %}<noscript>{% endif %} {% if include.url %}<source src="{{ include.url | absolute_url }}">{% endif %}
<source src="{{ include.url | absolute_url }}"> {% if include.lazyload %}</noscript>{% endif %}
{% if include.lazyload %}</noscript>{% endif %}
</video> {% if include.content %}{{include.content}}{% endif %}
{% elsif include.type == "audio" %} </video>
<audio controls {% elsif include.type == "audio" %}
class="content" <audio
{% if include.lazyload %}data-lazyload=""{% endif %} {% if include.player %}{% else %}controls{% endif %}
{% if include.preload %}preload="{{ include.preload }}"{% endif %} class="content"
{% comment %}
src="{{ include.url | absolute_url }}" {% if include.width %}width="{{ include.width }}"{% endif %}
{% endcomment %} {% if include.height %}height="{{ include.height }}"{% endif %}
{% if include.width %}width="{{ include.width }}"{% endif %}
{% if include.height %}height="{{ include.height }}"{% endif %} {% if include.autoplay %}autoplay="true"{% endif %}
{% if include.autoplay %}autoplay="true"{% endif %} {% if include.muted %}muted="true"{% endif %}
{% if include.muted %}muted="true"{% endif %} {% if include.loop %}loop="true"{% endif %}
{% if include.loop %}loop="true"{% endif %}>
{% if include.lazyload %}<noscript>{% endif %} {% if include.preload %}preload="{{ include.preload }}"{% endif %}
<source src="{{ include.url | absolute_url }}"> {% if include.lazyload %}data-lazyload=""{% endif %}
{% if include.lazyload %}</noscript>{% endif %} >
</audio> {% if include.lazyload %}<noscript>{% endif %}
{% endif %} {% if include.url %}<source src="{{ include.url | absolute_url }}">{% endif %}
{% if include.link %} {% if include.lazyload %}</noscript>{% endif %}
</a>
{% endif %} {% if include.content %}{{include.content}}{% endif %}
{% if include.caption %} </audio>
<figcaption>{{ include.caption }}</figcaption> {% endif %}
{% endif %} {% if include.link %}</a>{% endif %}
{% if include.caption %}<figcaption>{{ include.caption }}</figcaption>{% endif %}
{% endcapture %} {% endcapture %}
{% if include.player %} {% if include.player %}{% assign class = include.type | append: " player" %}{% else %}{% assign class = include.type %}{% endif %}
{% assign class = include.type | append: " player" %}
{% else %}
{% assign class = include.type %}
{% endif %}
{% include blocks/_base.liquid type="media" class=class content=content float=include.float align=include.align id=include.id %} {% include blocks/_base.liquid type="media" class=class content=content float=include.float align=include.align id=include.id %}
@@ -68,16 +68,17 @@ videos:
{% for video in page.videos.videos %} {% for video in page.videos.videos %}
{% capture capture_video %} {% capture capture_video %}
{% include blocks/media.liquid type="video" caption=caption poster=video.poster muted=true preload="none" id=video.id player=true %} {% capture video_sources %}
<script> {% for bitrate in page.videos.bitrates %}
document.getElementById("{{ video.id }}").variants = { {% for resolution in page.videos.resolutions %}
{% for bitrate in page.videos.bitrates %}"{{ bitrate }}kbit/s" : { {% for encoder in page.videos.encoders %}
{% for resolution in page.videos.resolutions %}"{{ resolution }}" : { {% assign real_url = encoder.url | replace: ":video", video.id | replace: ":res", resolution | replace: ":rate", bitrate | absolute_url%}
{% for encoder in page.videos.encoders %} <source src="{{ real_url }}" title="{{ bitrate }} kbit/s, {{ resolution }}, {{ encoder.name }}">
{% assign real_url = encoder.url | replace: ":video", video.id | replace: ":res", resolution | replace: ":rate", bitrate | absolute_url%}"{{ encoder.name }}" : "{{ real_url }}",{% endfor %} {% endfor %}
},{% endfor %} {% endfor %}
},{% endfor %} {% endfor %}
}; {% endcapture %}
{% include blocks/media.liquid type="video" caption=caption poster=video.poster muted=true preload="none" player=true content=video_sources %}
</script> </script>
{% endcapture %} {% endcapture %}
{% include blocks/details.liquid title=video.name content=capture_video level=1 open=true %} {% include blocks/details.liquid title=video.name content=capture_video level=1 open=true %}
+99 -59
View File
@@ -8,6 +8,8 @@
.block-media > a > .content, .block-media > a > .content,
.block-media > .content { .block-media > .content {
object-fit: contain;
object-position: center center;
} }
.block-media > figcaption { .block-media > figcaption {
@@ -22,6 +24,7 @@
// -------------------------------------------------------------------------------- // --------------------------------------------------------------------------------
// Block: Media Player // Block: Media Player
// -------------------------------------------------------------------------------- // --------------------------------------------------------------------------------
$media-player-button-size: 1.5rem;
.block-media.hide { .block-media.hide {
cursor: none; cursor: none;
@@ -29,144 +32,181 @@
.block-media > .top, .block-media > .top,
.block-media > .bottom { .block-media > .bottom {
display: block;
margin: 0; margin: 0;
padding: 0.5rem .5rem; padding: 0.2rem 0.5rem;
} }
.block-media > .top { .block-media > .top {
background: linear-gradient(to top, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 0.8) 80%);
position: absolute; position: absolute;
top: 0; top: 0;
left: 0; left: 0;
right: 0; right: 0;
display: grid;
grid-template-columns: 1; padding-bottom: 1rem;
grid-template-rows: 1;
justify-content: stretch; display: flex;
flex-flow: row nowrap;
justify-content: space-between;
align-items: stretch; align-items: stretch;
gap: 0; align-content: stretch;
transition: top ease-in-out 333ms, opacity ease-in-out 333ms; gap: 0.5rem;
background: linear-gradient(to top, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 0.8) 80%);
transition: top ease-in-out 100ms, opacity ease-in-out 100s;
opacity: 1.0; opacity: 1.0;
overflow: hidden;
user-select: none;
} }
.block-media.hide > .top { .block-media.hide > .top {
top: -100%; top: -100%;
opacity: 0.0; opacity: 0.0;
transition: top ease-in-out 500ms, opacity ease-in-out 500s;
} }
.block-media > .top > .variant { .block-media > .top > .variant {
// ToDo: Style this flex-grow: 1;
} }
.block-media > .bottom { .block-media > .bottom {
background: linear-gradient(to bottom, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 0.8) 80%);
position: absolute; position: absolute;
bottom: 0; bottom: 0;
left: 0; left: 0;
right: 0; right: 0;
display: grid;
grid-template-columns: max-content 1fr max-content max-content; padding-top: 1rem;
justify-content: stretch;
display: flex;
flex-flow: row nowrap;
justify-content: space-between;
align-items: stretch; align-items: stretch;
align-content: stretch;
gap: 0.5rem; gap: 0.5rem;
background: linear-gradient(to bottom, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 0.8) 80%);
transition: top ease-in-out 100ms, opacity ease-in-out 100s;
opacity: 1.0;
overflow: hidden; overflow: hidden;
user-select: none; user-select: none;
transition: bottom ease-in-out 333ms, opacity ease-in-out 333ms;
opacity: 1.0;
} }
.block-media.hide > .bottom { .block-media.hide > .bottom {
bottom: -100%; bottom: -100%;
opacity: 0.0;
transition: top ease-in-out 500ms, opacity ease-in-out 500s;
} }
.block-media > .bottom > .play { .block-media > .bottom > .play {
width: 2rem; flex-grow: 0;
height: 2rem; display: block;
margin: 0; margin: 0;
padding: 0; padding: 0;
font-size: 2rem; width: $media-player-button-size;
line-height: 2rem; height: $media-player-button-size;
font-size: $media-player-button-size;
line-height: $media-player-button-size;
text-align: center; text-align: center;
user-select: none; user-select: none;
} }
.block-media > .bottom > .play.playing { .block-media > .bottom > .play.playing {
} }
.block-media > .bottom > .playback { .block-media > .bottom > .time {
margin: 0; margin: 0;
padding: 0; padding: 0;
display: grid; width: auto;
grid-template-columns: max-content 1fr max-content; height: $media-player-button-size;
justify-content: stretch; flex-grow: 0;
align-items: stretch; flex-shrink: 0;
gap: 0.5rem; flex-basis: auto;
user-select: none;
}
.block-media > .bottom > .playback > .time {
display: block; display: block;
height: 2rem;
margin: 0;
padding: 0;
text-align: right; text-align: right;
line-height: 2rem; line-height: $media-player-button-size;
user-select: auto; user-select: auto;
} }
.block-media > .bottom > .playback > .progress { .block-media > .bottom > .progress {
display: block;
margin: 0; margin: 0;
padding: 0; padding: 0;
width: auto; width: auto;
min-width: 5rem;
height: 100%; height: 100%;
user-select: none; flex-grow: 5;
} flex-shrink: 1;
.block-media > .bottom > .audio {
margin: 0;
padding: 0;
display: grid;
grid-template-columns: max-content 1fr;
justify-content: stretch;
align-items: stretch;
gap: 0.5rem;
user-select: none;
}
.block-media > .bottom > .audio > .mute {
display: block; display: block;
user-select: none;
}
.block-media > .bottom > .mute {
margin: 0; margin: 0;
padding: 0; padding: 0;
width: 2rem; width: $media-player-button-size;
height: 2rem; height: $media-player-button-size;
font-size: 2rem; flex-grow: 0;
line-height: 2rem; flex-shrink: 0;
flex-basis: auto;
display: block;
font-size: $media-player-button-size;
line-height: $media-player-button-size;
text-align: center; text-align: center;
user-select: none; user-select: none;
} }
.block-media > .bottom > .audio > .mute.muted { .block-media > .bottom > .mute.muted {
} }
.block-media > .bottom > .audio > .volume { .block-media > .bottom > .volume {
display: block;
margin: 0; margin: 0;
padding: 0; padding: 0;
width: auto; width: auto;
min-width: 3rem;
max-width: 6rem;
height: 100%; height: 100%;
flex-grow: 1;
flex-shrink: 5;
flex-basis: auto;
display: block;
user-select: none; user-select: none;
} }
.block-media > .bottom > .fullscreen { .block-media > .bottom > .fullscreen {
width: 2rem;
height: 2rem;
margin: 0; margin: 0;
padding: 0; padding: 0;
font-size: 2rem; width: $media-player-button-size;
line-height: 2rem; height: $media-player-button-size;
flex-grow: 0;
flex-shrink: 0;
flex-basis: auto;
display: block;
font-size: $media-player-button-size;
line-height: $media-player-button-size;
text-align: center; text-align: center;
user-select: none; user-select: none;
} }
@media (max-width: 500px) {
.block-media > .bottom > .time {
display: none;
}
}
@media (max-width: 350px) {
.block-media > .bottom > .volume {
display: none;
}
}
@media (max-width: 250px) {
.block-media > .bottom > .play,
.block-media > .bottom > .mute,
.block-media > .bottom > .fullscreen {
display: none;
}
}
// -------------------------------------------------------------------------------- // --------------------------------------------------------------------------------
// Block: Media > Audio // Block: Media > Audio
+41 -38
View File
@@ -23,7 +23,7 @@ async function xmr_initialize_player(el) {
el.querySelector("audio"); el.querySelector("audio");
// Play/Pause // Play/Pause
let play_pause = el.querySelector(".bottom > .play"); let play_pause = el.querySelector(".play");
play_pause.update = function() { play_pause.update = function() {
if (media.paused) { if (media.paused) {
play_pause.classList.remove("playing"); play_pause.classList.remove("playing");
@@ -50,7 +50,7 @@ async function xmr_initialize_player(el) {
play_pause.update(); play_pause.update();
// Mute // Mute
let audio_mute = el.querySelector(".bottom > .audio > .mute"); let audio_mute = el.querySelector(".mute");
audio_mute.update = function() { audio_mute.update = function() {
if (media.muted) { if (media.muted) {
audio_mute.classList.add("muted"); audio_mute.classList.add("muted");
@@ -75,7 +75,7 @@ async function xmr_initialize_player(el) {
audio_mute.update(); audio_mute.update();
// Volume // Volume
let audio_volume = el.querySelector(".bottom > .audio > .volume"); let audio_volume = el.querySelector(".volume");
audio_volume.min = 0; audio_volume.min = 0;
audio_volume.max = 2147483647; audio_volume.max = 2147483647;
audio_volume.update = function() { audio_volume.update = function() {
@@ -102,7 +102,7 @@ async function xmr_initialize_player(el) {
audio_volume.update(); audio_volume.update();
// Time // Time
let playback_time = el.querySelector(".bottom > .playback > .time"); let playback_time = el.querySelector(".time");
playback_time.update = function() { playback_time.update = function() {
let duration_text = ""; let duration_text = "";
let need_hour = false; let need_hour = false;
@@ -153,7 +153,7 @@ async function xmr_initialize_player(el) {
playback_time.update(); playback_time.update();
// Progress // Progress
let playback_progress = el.querySelector(".bottom > .playback > .progress"); let playback_progress = el.querySelector(".progress");
playback_progress.update = function() { playback_progress.update = function() {
playback_progress.disabled = !media.seekable; playback_progress.disabled = !media.seekable;
playback_progress.min = 0; playback_progress.min = 0;
@@ -188,39 +188,6 @@ async function xmr_initialize_player(el) {
}); });
fullscreen.update(); fullscreen.update();
// Variants
let variants = el.querySelector(".variant");
variants.addEventListener("input", () => {
media.pause();
play_pause.update();
media.src = variants.value;
});
variants.addEventListener("value", () => {
media.pause();
play_pause.update();
media.src = variants.value;
});
function createVariants(el, key, value) {
if (typeof(value) === "object") {
for (let k in value) {
let v = value[k];
createVariants(el, `${key} / ${k}`, v);
}
} else if (typeof(value) === "string") {
let option = document.createElement("option");
option.value = value;
option.textContent = key;
el.appendChild(option);
} else {
el.appendChild(document.createElement(typeof(value)));
}
}
for (let k in el.variants) {
let v = el.variants[k];
createVariants(variants, k, v);
}
variants.value = media.src = variants.querySelectorAll("option")[0].value;
// Media Play/Pause by click // Media Play/Pause by click
media.addEventListener("click", () => { media.addEventListener("click", () => {
play_pause.click(); play_pause.click();
@@ -257,6 +224,42 @@ async function xmr_initialize_player(el) {
} }
}); });
el.hideOverlay(); el.hideOverlay();
// Variants
let variant = el.querySelector(".variant");
variant.update = function() {
let paused = media.paused;
let muted = media.muted;
let volume = media.volume;
let time = media.currentTime;
media.pause();
media.currentSrc = variant.value;
media.play();
media.currentTime = time;
media.volume = volume;
media.muted = muted;
if (paused) {
media.pause();
} else {
media.play();
}
play_pause.update();
}
variant.addEventListener("input", () => { variant.update(); });
variant.addEventListener("value", () => { variant.update(); });
// Add options from available sources.
let sources = el.querySelectorAll("source");
for (let k of sources) {
let option = document.createElement("option");
option.value = k.src;
option.textContent = k.title;
variant.appendChild(option);
}
variant.value = media.currentSrc;
} }
async function xmr_initialize_players() { async function xmr_initialize_players() {