Files
blog/themes/Ying/layouts/shortcodes/video.html
T
2026-06-22 18:35:51 +08:00

142 lines
6.1 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{{/*
视频嵌入 shortcode(Amigo 风格)
用法:
{{< video src="xxx.mp4" >}}
{{< video src="xxx.mp4" poster="cover.jpg" caption="说明" >}}
{{< video src="xxx.gif" loop="true" muted="true" >}} ← GIF 模式
{{< bilibili BV1xx411c7mD >}}
{{< youtube dQw4w9WgXcQ >}}
*/}}
{{ if .Get "bvid" | or (.Get 0) }}
{{/* Bilibili */}}
{{ $bvid := .Get "bvid" | default (.Get 0) }}
<div class="video-wrap">
<div class="video-embed">
<iframe
src="//player.bilibili.com/player.html?bvid={{ $bvid }}&high_quality=1&danmaku=0"
scrolling="no" frameborder="0" allowfullscreen="true" loading="lazy"
></iframe>
</div>
</div>
{{ else if .Get "id" | or (and (.Get 0) (not (.Get "src"))) }}
{{/* YouTube */}}
{{ $id := .Get "id" | default (.Get 0) }}
<div class="video-wrap">
<div class="video-embed">
<iframe
src="https://www.youtube.com/embed/{{ $id }}"
frameborder="0"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
allowfullscreen="true" loading="lazy"
></iframe>
</div>
</div>
{{ else }}
{{/* 本地/外部视频 */}}
{{ $src := .Get "src" }}
{{ $poster := .Get "poster" }}
{{ $caption := .Get "caption" }}
{{ $loop := .Get "loop" | default "false" }}
{{ $muted := .Get "muted" | default "false" }}
{{ $isGifMode := or (eq $loop "true") (eq $muted "true") }}
{{ $videoSrc := $src }}
{{ if not (hasPrefix $src "http") }}
{{ $videoSrc = printf "/%s/%s" .Page.Slug $src }}
{{ end }}
{{ if $isGifMode }}
{{/* GIF 模式:图片标签显示,自动循环 */}}
<div class="video-wrap">
<img src="{{ $videoSrc }}" alt="{{ $caption }}" class="gif-image" loading="lazy">
{{ with $caption }}
<figcaption class="video-caption">{{ . }}</figcaption>
{{ end }}
</div>
{{ else }}
{{/* 视频模式:自定义播放器 */}}
<div class="video-wrap">
<figure class="video-player" onclick="toggleVideo(this)">
<video preload="metadata" playsinline webkit-playsinline {{ with $poster }}poster="{{ . }}"{{ end }}>
<source src="{{ $videoSrc }}">
</video>
<div class="video-play-icon">
<svg viewBox="0 0 24 24" fill="currentColor"><path d="M8 5v14l11-7z"/></svg>
</div>
<div class="video-controls">
<button class="video-ctrl-btn" onclick="event.stopPropagation(); togglePlay(this)">
<svg class="icon-play" viewBox="0 0 24 24" fill="currentColor"><path d="M8 5v14l11-7z"/></svg>
<svg class="icon-pause" viewBox="0 0 24 24" fill="currentColor" style="display:none"><path d="M6 19h4V5H6v14zm8-14v14h4V5h-4z"/></svg>
</button>
<div class="video-progress" onclick="event.stopPropagation(); seekVideo(event, this)">
<div class="video-progress-bar"></div>
</div>
<span class="video-time">0:00</span>
<button class="video-ctrl-btn" onclick="event.stopPropagation(); toggleMute(this)">
<svg class="icon-unmute" viewBox="0 0 24 24" fill="currentColor"><path d="M3 9v6h4l5 5V4L7 9H3zm13.5 3c0-1.77-1.02-3.29-2.5-4.03v8.05c1.48-.73 2.5-2.25 2.5-4.02zM14 3.23v2.06c2.89.86 5 3.54 5 6.71s-2.11 5.85-5 6.71v2.06c4.01-.91 7-4.49 7-8.77s-2.99-7.86-7-8.77z"/></svg>
<svg class="icon-mute" viewBox="0 0 24 24" fill="currentColor" style="display:none"><path d="M16.5 12c0-1.77-1.02-3.29-2.5-4.03v2.21l2.45 2.45c.03-.2.05-.41.05-.63zm2.5 0c0 .94-.2 1.82-.54 2.64l1.51 1.51C20.63 14.91 21 13.5 21 12c0-4.28-2.99-7.86-7-8.77v2.06c2.89.86 5 3.54 5 6.71zM4.27 3L3 4.27 7.73 9H3v6h4l5 5v-6.73l4.25 4.25c-.67.52-1.42.93-2.25 1.18v2.06c1.38-.31 2.63-.95 3.69-1.81L19.73 21 21 19.73l-9-9L4.27 3zM12 4L9.91 6.09 12 8.18V4z"/></svg>
</button>
<button class="video-ctrl-btn" onclick="event.stopPropagation(); fullscreenVideo(this)">
<svg viewBox="0 0 24 24" fill="currentColor"><path d="M7 14H5v5h5v-2H7v-3zm-2-4h2V7h3V5H5v5zm12 7h-3v2h5v-5h-2v3zM14 5v2h3v3h2V5h-5z"/></svg>
</button>
</div>
</figure>
{{ with $caption }}
<figcaption class="video-caption">{{ . }}</figcaption>
{{ end }}
</div>
{{ end }}
{{ end }}
<script>
function toggleVideo(el) {
var video = el.querySelector('video');
if (!video) return;
if (video.paused) { video.play(); el.classList.add('playing'); }
else { video.pause(); el.classList.remove('playing'); }
}
function togglePlay(btn) {
var video = btn.closest('.video-player').querySelector('video');
if (video.paused) { video.play(); btn.closest('.video-player').classList.add('playing'); }
else { video.pause(); btn.closest('.video-player').classList.remove('playing'); }
}
function toggleMute(btn) {
var video = btn.closest('.video-player').querySelector('video');
video.muted = !video.muted;
btn.querySelector('.icon-unmute').style.display = video.muted ? 'none' : '';
btn.querySelector('.icon-mute').style.display = video.muted ? '' : 'none';
}
function seekVideo(e, bar) {
var video = bar.closest('.video-player').querySelector('video');
var rect = bar.getBoundingClientRect();
video.currentTime = ((e.clientX - rect.left) / rect.width) * video.duration;
}
function fullscreenVideo(btn) {
var el = btn.closest('.video-player');
if (el.requestFullscreen) el.requestFullscreen();
else if (el.webkitRequestFullscreen) el.webkitRequestFullscreen();
}
document.addEventListener('timeupdate', function(e) {
var video = e.target;
if (!video.closest) return;
var player = video.closest('.video-player');
if (!player) return;
var bar = player.querySelector('.video-progress-bar');
var time = player.querySelector('.video-time');
if (bar) bar.style.width = (video.currentTime / video.duration * 100) + '%';
if (time) {
var m = Math.floor(video.currentTime / 60);
var s = Math.floor(video.currentTime % 60);
time.textContent = m + ':' + (s < 10 ? '0' : '') + s;
}
var playIcon = player.querySelector('.icon-play');
var pauseIcon = player.querySelector('.icon-pause');
if (playIcon && pauseIcon) {
playIcon.style.display = video.paused ? '' : 'none';
pauseIcon.style.display = video.paused ? 'none' : '';
}
}, true);
</script>