88 lines
1.9 KiB
Vue
88 lines
1.9 KiB
Vue
<template>
|
|
<Teleport to="body">
|
|
<div :class="$style.container">
|
|
<YouTube ref="yt" v-if="youtubeID" :id="youtubeID" />
|
|
<video
|
|
v-else-if="videoURL"
|
|
controls
|
|
:src="videoURL"
|
|
ref="video"
|
|
@error="onVideoNotFound"
|
|
/>
|
|
</div>
|
|
</Teleport>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { useData } from "vitepress";
|
|
import { computed, ref, useTemplateRef, watch } from "vue";
|
|
import YouTube from "./YouTube.vue";
|
|
|
|
const { frontmatter } = useData<{ video?: string }>();
|
|
const videoURL = computed(() => frontmatter.value?.video);
|
|
const videoEl = useTemplateRef("video");
|
|
const ytEl = useTemplateRef("yt");
|
|
|
|
const youtubeID = ref("");
|
|
|
|
function setImgClick() {
|
|
document.removeEventListener("click", imgClickListener);
|
|
document.addEventListener("click", imgClickListener);
|
|
|
|
document.querySelectorAll("img").forEach((img) => {
|
|
const [, min, sec] = /^(\d+)\:(\d+)$/.exec(img.alt) || [];
|
|
const t = min && sec ? parseInt(min) * 60 + parseInt(sec) : null;
|
|
if (!t) return;
|
|
|
|
img.title = img.title || img.alt;
|
|
});
|
|
}
|
|
|
|
function imgClickListener(ev: PointerEvent) {
|
|
if (!(ev.target instanceof HTMLImageElement)) return;
|
|
const img = ev.target;
|
|
|
|
const [, min, sec] = /^(\d+)\:(\d+)$/.exec(img.alt) || [];
|
|
const t = min && sec ? parseInt(min) * 60 + parseInt(sec) : null;
|
|
if (!t) return;
|
|
|
|
if (videoEl.value) {
|
|
videoEl.value.currentTime = t;
|
|
} else if (ytEl.value) {
|
|
ytEl.value.seekTo(t);
|
|
}
|
|
}
|
|
|
|
watch(videoURL, () => {
|
|
setImgClick();
|
|
});
|
|
|
|
function onVideoNotFound() {
|
|
const m = /\[(.+?)\]\.\S+$/.exec(videoURL.value || "") || [];
|
|
youtubeID.value = m[1] || "";
|
|
}
|
|
onVideoNotFound();
|
|
|
|
//@ts-ignore
|
|
const { SSR } = import.meta.env;
|
|
|
|
if (!SSR) {
|
|
setTimeout(() => setImgClick(), 500);
|
|
}
|
|
</script>
|
|
|
|
<style module>
|
|
.container {
|
|
width: 400px;
|
|
overflow: visible;
|
|
position: fixed;
|
|
bottom: 10px;
|
|
left: 10px;
|
|
z-index: 100;
|
|
|
|
> * {
|
|
width: 100%;
|
|
}
|
|
}
|
|
</style>
|