1715
This commit is contained in:
1 parent
ad65bfbb4c
commit
0cc176e867
3 files changed
+233
-72
No files matched your search
+122
-40
@@ -5708,62 +5708,157 @@ a.link-card:hover .link-card-arrow {
|
|||||||
color: var(--accent, #3b82f6) !important;
|
color: var(--accent, #3b82f6) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ==================== 视频样式 ==================== */
|
/* ==================== 视频样式(简约风格) ==================== */
|
||||||
|
|
||||||
.post-video-wrapper {
|
.video-wrap {
|
||||||
margin: 1.5rem 0;
|
margin: 1.5rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.video-player {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
background: #000;
|
background: #000;
|
||||||
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.1);
|
cursor: pointer;
|
||||||
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
[data-theme="dark"] .post-video-wrapper {
|
.video-player video {
|
||||||
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.4);
|
|
||||||
}
|
|
||||||
|
|
||||||
.video-container {
|
|
||||||
position: relative;
|
|
||||||
width: 100%;
|
|
||||||
max-width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.video-container video {
|
|
||||||
display: block;
|
display: block;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: auto;
|
height: auto;
|
||||||
max-height: 80vh;
|
max-height: 80vh;
|
||||||
border-radius: 12px;
|
object-fit: contain;
|
||||||
outline: none;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.video-container video::-webkit-media-controls-panel {
|
.video-play-icon {
|
||||||
background: linear-gradient(transparent, rgba(0, 0, 0, 0.4));
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
width: 56px;
|
||||||
|
height: 56px;
|
||||||
|
background: rgba(0, 0, 0, 0.5);
|
||||||
|
border-radius: 50%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
color: #fff;
|
||||||
|
transition: opacity 0.3s, transform 0.3s;
|
||||||
|
backdrop-filter: blur(8px);
|
||||||
|
-webkit-backdrop-filter: blur(8px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.video-play-icon svg {
|
||||||
|
width: 28px;
|
||||||
|
height: 28px;
|
||||||
|
margin-left: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.video-player.playing .video-play-icon {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translate(-50%, -50%) scale(0.8);
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.video-player:hover .video-play-icon {
|
||||||
|
transform: translate(-50%, -50%) scale(1.08);
|
||||||
|
}
|
||||||
|
|
||||||
|
.video-controls {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
padding: 8px 12px;
|
||||||
|
background: linear-gradient(transparent, rgba(0, 0, 0, 0.65));
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.video-player:hover .video-controls,
|
||||||
|
.video-player.playing .video-controls {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.video-ctrl-btn {
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
color: #fff;
|
||||||
|
font-size: 18px;
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 4px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
flex-shrink: 0;
|
||||||
|
opacity: 0.85;
|
||||||
|
transition: opacity 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.video-ctrl-btn:hover {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.video-ctrl-btn svg {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.video-progress {
|
||||||
|
flex: 1;
|
||||||
|
height: 4px;
|
||||||
|
background: rgba(255, 255, 255, 0.3);
|
||||||
|
border-radius: 2px;
|
||||||
|
cursor: pointer;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.video-progress-bar {
|
||||||
|
height: 100%;
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 2px;
|
||||||
|
width: 0;
|
||||||
|
transition: width 0.1s linear;
|
||||||
|
}
|
||||||
|
|
||||||
|
.video-time {
|
||||||
|
color: #fff;
|
||||||
|
font-size: 12px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
min-width: 36px;
|
||||||
|
text-align: center;
|
||||||
|
font-variant-numeric: tabular-nums;
|
||||||
}
|
}
|
||||||
|
|
||||||
.video-caption {
|
.video-caption {
|
||||||
padding: 0.75rem 1rem;
|
padding: 0.6rem 0;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-size: 0.85rem;
|
font-size: 0.82rem;
|
||||||
color: #6b7280;
|
color: #9ca3af;
|
||||||
background: #f9fafb;
|
}
|
||||||
border-top: 1px solid #e5e7eb;
|
|
||||||
|
[data-theme="dark"] .video-player {
|
||||||
|
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.4);
|
||||||
}
|
}
|
||||||
|
|
||||||
[data-theme="dark"] .video-caption {
|
[data-theme="dark"] .video-caption {
|
||||||
color: #9ca3af;
|
color: #6b7280;
|
||||||
background: #1f2937;
|
|
||||||
border-top-color: #374151;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Bilibili / YouTube 嵌入 */
|
/* Bilibili / YouTube 嵌入 */
|
||||||
.video-embed {
|
.video-embed {
|
||||||
position: relative;
|
position: relative;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding-bottom: 56.25%; /* 16:9 */
|
padding-bottom: 56.25%;
|
||||||
height: 0;
|
height: 0;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
|
background: #000;
|
||||||
}
|
}
|
||||||
|
|
||||||
.video-embed iframe {
|
.video-embed iframe {
|
||||||
@@ -5775,16 +5870,3 @@ a.link-card:hover .link-card-arrow {
|
|||||||
border: none;
|
border: none;
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 文章内直接写 <video> 标签的样式 */
|
|
||||||
.post-content video {
|
|
||||||
display: block;
|
|
||||||
max-width: 100%;
|
|
||||||
margin: 1.5rem auto;
|
|
||||||
border-radius: 12px;
|
|
||||||
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-theme="dark"] .post-content video {
|
|
||||||
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.4);
|
|
||||||
}
|
|
||||||
@@ -1,22 +1,38 @@
|
|||||||
{{/*
|
{{/*
|
||||||
Render hook for HTML video tags in Markdown
|
Render hook for HTML video tags in Markdown
|
||||||
让 Markdown 中的 <video> 标签自动包裹 figure 容器
|
|
||||||
*/}}
|
*/}}
|
||||||
<figure class="post-video-wrapper">
|
<div class="video-wrap">
|
||||||
<div class="video-container">
|
<figure class="video-player" onclick="toggleVideo(this)">
|
||||||
<video
|
<video
|
||||||
controls
|
|
||||||
preload="metadata"
|
preload="metadata"
|
||||||
playsinline
|
playsinline
|
||||||
webkit-playsinline
|
webkit-playsinline
|
||||||
{{ with .Attributes.poster }}poster="{{ . }}"{{ end }}
|
{{ with .Attributes.poster }}poster="{{ . }}"{{ end }}
|
||||||
{{ with .Attributes.class }}class="{{ . }}"{{ end }}
|
|
||||||
>
|
>
|
||||||
<source src="{{ .Destination | safeURL }}" type="{{ .MediaType.Type }}">
|
<source src="{{ .Destination | safeURL }}" type="{{ .MediaType.Type }}">
|
||||||
你的浏览器不支持视频播放。
|
|
||||||
</video>
|
</video>
|
||||||
</div>
|
<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 .Text }}
|
{{ with .Text }}
|
||||||
<figcaption class="video-caption">{{ . }}</figcaption>
|
<figcaption class="video-caption">{{ . }}</figcaption>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
</figure>
|
</div>
|
||||||
@@ -1,39 +1,34 @@
|
|||||||
{{/*
|
{{/*
|
||||||
视频嵌入 shortcode
|
视频嵌入 shortcode(简约风格)
|
||||||
用法:
|
用法:
|
||||||
{{< video src="/video/example.mp4" >}}
|
{{< video src="xxx.mp4" >}}
|
||||||
{{< video src="https://example.com/video.mp4" poster="/image/poster.jpg" >}}
|
{{< video src="xxx.mp4" poster="cover.jpg" caption="说明" >}}
|
||||||
{{< video src="/video/example.mp4" caption="视频说明文字" >}}
|
|
||||||
{{< bilibili BV1xx411c7mD >}}
|
{{< bilibili BV1xx411c7mD >}}
|
||||||
{{< youtube dQw4w9WgXcQ >}}
|
{{< youtube dQw4w9WgXcQ >}}
|
||||||
*/}}
|
*/}}
|
||||||
|
|
||||||
{{ if .Get "bvid" | or (.Get 0) }}
|
{{ if .Get "bvid" | or (.Get 0) }}
|
||||||
{{/* Bilibili 嵌入 */}}
|
{{/* Bilibili */}}
|
||||||
{{ $bvid := .Get "bvid" | default (.Get 0) }}
|
{{ $bvid := .Get "bvid" | default (.Get 0) }}
|
||||||
<div class="post-video-wrapper">
|
<div class="video-wrap">
|
||||||
<div class="video-embed bilibili-embed">
|
<div class="video-embed">
|
||||||
<iframe
|
<iframe
|
||||||
src="//player.bilibili.com/player.html?bvid={{ $bvid }}&high_quality=1&danmaku=0"
|
src="//player.bilibili.com/player.html?bvid={{ $bvid }}&high_quality=1&danmaku=0"
|
||||||
scrolling="no"
|
scrolling="no" frameborder="0" allowfullscreen="true" loading="lazy"
|
||||||
frameborder="0"
|
|
||||||
allowfullscreen="true"
|
|
||||||
loading="lazy"
|
|
||||||
></iframe>
|
></iframe>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{ else if .Get "id" | or (and (.Get 0) (not (.Get "src"))) }}
|
{{ else if .Get "id" | or (and (.Get 0) (not (.Get "src"))) }}
|
||||||
{{/* YouTube 嵌入 */}}
|
{{/* YouTube */}}
|
||||||
{{ $id := .Get "id" | default (.Get 0) }}
|
{{ $id := .Get "id" | default (.Get 0) }}
|
||||||
<div class="post-video-wrapper">
|
<div class="video-wrap">
|
||||||
<div class="video-embed youtube-embed">
|
<div class="video-embed">
|
||||||
<iframe
|
<iframe
|
||||||
src="https://www.youtube.com/embed/{{ $id }}"
|
src="https://www.youtube.com/embed/{{ $id }}"
|
||||||
frameborder="0"
|
frameborder="0"
|
||||||
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
|
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
|
||||||
allowfullscreen="true"
|
allowfullscreen="true" loading="lazy"
|
||||||
loading="lazy"
|
|
||||||
></iframe>
|
></iframe>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -43,26 +38,94 @@
|
|||||||
{{ $src := .Get "src" }}
|
{{ $src := .Get "src" }}
|
||||||
{{ $poster := .Get "poster" }}
|
{{ $poster := .Get "poster" }}
|
||||||
{{ $caption := .Get "caption" }}
|
{{ $caption := .Get "caption" }}
|
||||||
{{/* 自动补全 Page Bundle 路径 */}}
|
|
||||||
{{ $videoSrc := $src }}
|
{{ $videoSrc := $src }}
|
||||||
{{ if not (hasPrefix $src "http") }}
|
{{ if not (hasPrefix $src "http") }}
|
||||||
{{ $videoSrc = printf "/%s/%s" .Page.Slug $src }}
|
{{ $videoSrc = printf "/%s/%s" .Page.Slug $src }}
|
||||||
{{ end }}
|
{{ end }}
|
||||||
<figure class="post-video-wrapper">
|
|
||||||
<div class="video-container">
|
<div class="video-wrap">
|
||||||
|
<figure class="video-player" onclick="toggleVideo(this)">
|
||||||
<video
|
<video
|
||||||
controls
|
|
||||||
preload="metadata"
|
preload="metadata"
|
||||||
{{ with $poster }}poster="{{ . }}"{{ end }}
|
|
||||||
playsinline
|
playsinline
|
||||||
webkit-playsinline
|
webkit-playsinline
|
||||||
|
{{ with $poster }}poster="{{ . }}"{{ end }}
|
||||||
>
|
>
|
||||||
<source src="{{ $videoSrc }}">
|
<source src="{{ $videoSrc }}">
|
||||||
你的浏览器不支持视频播放。
|
|
||||||
</video>
|
</video>
|
||||||
</div>
|
<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 }}
|
{{ with $caption }}
|
||||||
<figcaption class="video-caption">{{ . }}</figcaption>
|
<figcaption class="video-caption">{{ . }}</figcaption>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
</figure>
|
</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>
|
||||||
Reference in new issue
Block a user