You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
158 lines
3.5 KiB
158 lines
3.5 KiB
<script lang="ts">
|
|
/**
|
|
* Vídeo de YouTube con carga diferida.
|
|
*
|
|
* Hasta que alguien pulsa, aquí no se carga NADA de YouTube: se ve la
|
|
* portada del propio tema con un botón encima. Solo al pulsar se inserta el
|
|
* reproductor incrustado.
|
|
*
|
|
* Es deliberado por tres motivos: un `<iframe>` de YouTube pesa más que el
|
|
* resto de la página junta, deja cookies de terceros antes de que nadie haya
|
|
* pedido ver nada —lo que en la UE exige consentimiento— y la miniatura
|
|
* oficial se sirve desde los servidores de Google, así que usar la portada
|
|
* propia evita también esa petición.
|
|
*
|
|
* Se usa `youtube-nocookie.com`, que es el mismo reproductor sin las cookies
|
|
* de seguimiento salvo que se llegue a reproducir.
|
|
*/
|
|
import Icono from './Icono.svelte';
|
|
import { reproductor } from '$lib/player/player.svelte';
|
|
|
|
interface Props {
|
|
/** Id de YouTube, once caracteres. */
|
|
youtube: string;
|
|
titulo: string;
|
|
/** Imagen propia que hace de portada mientras no se carga el vídeo. */
|
|
poster: string;
|
|
}
|
|
|
|
let { youtube, titulo, poster }: Props = $props();
|
|
|
|
let cargado = $state(false);
|
|
|
|
function abrir() {
|
|
// Dos cosas sonando a la vez, no: manda lo que se acaba de pedir.
|
|
reproductor.pausar();
|
|
cargado = true;
|
|
}
|
|
</script>
|
|
|
|
<div class="marco">
|
|
{#if cargado}
|
|
<iframe
|
|
src="https://www.youtube-nocookie.com/embed/{youtube}?autoplay=1&rel=0"
|
|
title={titulo}
|
|
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
|
|
referrerpolicy="strict-origin-when-cross-origin"
|
|
allowfullscreen
|
|
></iframe>
|
|
{:else}
|
|
<button type="button" class="portada" onclick={abrir}>
|
|
<img src={poster} alt="" />
|
|
<span class="velo"></span>
|
|
<span class="play"><Icono nombre="play" tamano={28} /></span>
|
|
<span class="etiqueta">
|
|
<span class="titulo">{titulo}</span>
|
|
<span class="aviso">Ver en YouTube</span>
|
|
</span>
|
|
</button>
|
|
{/if}
|
|
</div>
|
|
|
|
<style>
|
|
.marco {
|
|
position: relative;
|
|
aspect-ratio: 16 / 9;
|
|
overflow: hidden;
|
|
border: 1px solid var(--c-border);
|
|
border-radius: var(--radius-tarjeta);
|
|
background: var(--c-bg-sunken);
|
|
}
|
|
|
|
.marco iframe {
|
|
width: 100%;
|
|
height: 100%;
|
|
border: 0;
|
|
display: block;
|
|
}
|
|
|
|
.portada {
|
|
position: relative;
|
|
display: block;
|
|
width: 100%;
|
|
height: 100%;
|
|
padding: 0;
|
|
border: none;
|
|
background: none;
|
|
cursor: pointer;
|
|
text-align: left;
|
|
}
|
|
|
|
.portada img {
|
|
width: 100%;
|
|
height: 100%;
|
|
object-fit: cover;
|
|
transition: transform 0.4s ease;
|
|
}
|
|
|
|
.portada:hover img,
|
|
.portada:focus-visible img {
|
|
transform: scale(1.03);
|
|
}
|
|
|
|
/* El velo mantiene legible el texto sobre cualquier portada. */
|
|
.velo {
|
|
position: absolute;
|
|
inset: 0;
|
|
background: linear-gradient(to top, rgb(0 0 0 / 0.6), rgb(0 0 0 / 0.05) 55%);
|
|
}
|
|
|
|
.play {
|
|
position: absolute;
|
|
inset: 0;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
color: #fff;
|
|
transition: transform 0.25s cubic-bezier(0.22, 1, 0.36, 1);
|
|
}
|
|
|
|
.portada:hover .play,
|
|
.portada:focus-visible .play {
|
|
transform: scale(1.12);
|
|
}
|
|
|
|
.etiqueta {
|
|
position: absolute;
|
|
left: var(--sp-4);
|
|
right: var(--sp-4);
|
|
bottom: var(--sp-3);
|
|
display: flex;
|
|
flex-direction: column;
|
|
color: #fff;
|
|
}
|
|
|
|
.titulo {
|
|
font-weight: 600;
|
|
}
|
|
|
|
.aviso {
|
|
font-size: var(--fs-xs);
|
|
opacity: 0.85;
|
|
}
|
|
|
|
@media (prefers-reduced-motion: reduce) {
|
|
.portada img,
|
|
.play {
|
|
transition: none;
|
|
}
|
|
|
|
.portada:hover img,
|
|
.portada:focus-visible img,
|
|
.portada:hover .play,
|
|
.portada:focus-visible .play {
|
|
transform: none;
|
|
}
|
|
}
|
|
</style>
|