一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第14天,点击查看活动详情。
title: Fluid -11- 封面视频背景顺滑加载 mathjax: false date: 2021-09-16 14:08:14 tags: [Hexo, Theme, Fluid] categories: [Hexo, Theme, Fluid]
在Fluid -2- 随机视频背景切换 中记录了 Fluid 主题背景随机切换的实现方法,但存在加载视频覆盖原始图像背景的情况,本文记录顺滑加载解决方案 。
当前问题
- 当前问题为背景图像加载较快,视频稍慢
- 导致背景加载时会有先出现图像,再覆盖另一个视频的尴尬场景
解决思路
放弃图像加载
- 放弃图像加载是一种解决方案,这样就只会加载视频,没有图像的闪动
- 但手机端需要加载图像,不能放弃图像背景
- 更重要的原因是图像加载快,用户可以更早地感受到网页在加载
- 因此不能放弃加载图像
加载更小的视频第一帧图像
- 又需要图像,同时又让视频覆盖起来顺滑
- 于是就有了使用视频第一帧图像作为背景图像加载的思路
实现思路
- 实现思路为在加载视频路径json时顺带加载相应的第一帧图像
- 动态替换原始背景的 style background 链接地址,实现顺滑加载
- 该方案不会影响手机端的原始背景图像正常加载
解决方案
实现动态背景视频加载
获取视频图像第一帧
- 获取视频第一帧:Python 从视频中提取图像
- 调整保存图像的质量:Python 图像保存质量设置
保存质量可以低一些,使得图像文件小,更快加载
- 上传图像,获取和视频对应的图像链接
修改 video_url.json
-
修改
video_url.json文件 -
之前的
视频链接改为[视频链接, 图像链接] -
示例:
"https://cdn.jsdelivr.net/gh/zywvvd/HexoFiles/vvd-dell-2021-win-10/20210808220318.mp4"
# 改为
["https://cdn.jsdelivr.net/gh/zywvvd/HexoFiles/vvd-dell-2021-win-10/20210808220318.mp4", "https://cdn.jsdelivr.net/gh/zywvvd/HexoFiles/vvd-dell-2021-win-10/20210808220318.jpg"]
修改 layout.ejs 文件
-
修改
Hexo -> themes -> fluid -> layout -> layout.ejs文件: -
将之前的
<% if(banner_video){ %>段改为:
<% if(banner_video){ %>
<script>
var ua = navigator.userAgent;
var ipad = ua.match(/(iPad).*OS\s([\d_]+)/),
isIphone = !ipad && ua.match(/(iPhone\sOS)\s([\d_]+)/),
isAndroid = ua.match(/(Android)\s+([\d.]+)/),
isMobile = isIphone || isAndroid;
function set_video_attr(id){
var height = document.body.children[0].clientHeight
var width = document.body.children[0].clientWidth
var video_item = document.getElementById(id);
if (height / width < 0.56){
video_item.setAttribute('width', '100%');
video_item.setAttribute('height', 'auto');
} else {
video_item.setAttribute('height', '100%');
video_item.setAttribute('width', 'auto');
}
}
$.getJSON('/vvd_js/video_url.json', function(data){
if (!isMobile){
var video_list_length = data.length
var seed = Math.random()
index = Math.floor(seed * video_list_length)
video_url = data[index][0]
pre_show_image_url = data[index][1]
banner_obj = document.getElementById("banner")
banner_obj.style.cssText = "background: url('" + pre_show_image_url + "') no-repeat; background-size: cover;"
video_html_res = "<video id='video_item' style='position: absolute;' muted='muted' src=" + video_url + " autoplay='autoplay' loop='loop'></video>"
document.getElementById("banner_video_insert").innerHTML = video_html_res;
set_video_attr('video_item')
}
});
if (!isMobile){
window.onresize = function(){
set_video_attr('video_item')
}
}
</script>
<% } %>
- 完整的
layout.ejs文件
<%
var subtitle = page.subtitle || page.title
var banner_img = page.banner_img || theme.index.banner_img
var banner_img_height = page.banner_img_height || theme.index.banner_img_height
var banner_mask_alpha = page.banner_mask_alpha || theme.index.banner_mask_alpha
var colorSchema = theme.dark_mode && theme.dark_mode.enable && theme.dark_mode.default ? theme.dark_mode.default : ''
var banner_video = theme.index.banner_video
%>
<!DOCTYPE html>
<html lang="<%= config.language %>" <%= colorSchema ? `data-default-color-scheme=${colorSchema}` : '' %>>
<script type="text/javascript" src="/vvd_js/jquery.js"></script>
<%- partial('_partial/head') %>
<body>
<header style="height: <%- banner_img_height %>vh;">
<%- partial('_partial/nav') %>
<div class="banner" id="banner" <%- theme.banner && theme.banner.parallax && 'parallax=true' %>
style="background: url('<%- url_for(banner_img) %>') no-repeat center center;
background-size: cover;">
<div class="full-bg-img" >
<div id="banner_video_insert">
</div>
<% if(banner_video){ %>
<script>
var ua = navigator.userAgent;
var ipad = ua.match(/(iPad).*OS\s([\d_]+)/),
isIphone = !ipad && ua.match(/(iPhone\sOS)\s([\d_]+)/),
isAndroid = ua.match(/(Android)\s+([\d.]+)/),
isMobile = isIphone || isAndroid;
function set_video_attr(id){
var height = document.body.children[0].clientHeight
var width = document.body.children[0].clientWidth
var video_item = document.getElementById(id);
if (height / width < 0.56){
video_item.setAttribute('width', '100%');
video_item.setAttribute('height', 'auto');
} else {
video_item.setAttribute('height', '100%');
video_item.setAttribute('width', 'auto');
}
}
$.getJSON('/vvd_js/video_url.json', function(data){
if (!isMobile){
var video_list_length = data.length
var seed = Math.random()
index = Math.floor(seed * video_list_length)
video_url = data[index][0]
pre_show_image_url = data[index][1]
banner_obj = document.getElementById("banner")
banner_obj.style.cssText = "background: url('" + pre_show_image_url + "') no-repeat; background-size: cover;"
video_html_res = "<video id='video_item' style='position: absolute;' muted='muted' src=" + video_url + " autoplay='autoplay' loop='loop'></video>"
document.getElementById("banner_video_insert").innerHTML = video_html_res;
set_video_attr('video_item')
}
});
if (!isMobile){
window.onresize = function(){
set_video_attr('video_item')
}
}
</script>
<% } %>
<div class="mask flex-center" style="background-color: rgba(0, 0, 0, <%= parseFloat(banner_mask_alpha) %>)">
<div class="page-header text-center fade-in-up">
<span class="h2" id="subtitle" title="<%= subtitle %>">
<% if(!theme.fun_features.typing.enable) { %>
<%- subtitle %>
<% } %>
</span>
<% if(is_post() && page.meta !== false) { %>
<%- partial('_partial/post-meta') %>
<% } %>
</div>
<% if (theme.scroll_down_arrow.enable && theme.scroll_down_arrow.banner_height_limit <= banner_img_height && page.layout !== '404') { %>
<div class="scroll-down-bar">
<i class="iconfont icon-arrowdown"></i>
</div>
<% } %>
</div>
</div>
</div>
</header>
<main>
<% if(is_post() || page.layout === '404') { %>
<%- body %>
<% } else { %>
<div class="container nopadding-x-md">
<div class="py-5" id="board"
<%- banner_img_height >= 100 && theme.banner && theme.banner.parallax ? 'style=margin-top:0' : '' %>>
<% if(page.layout === 'about') { %>
<div class="about-avatar">
<img src="<%= url_for(theme.about.avatar) %>"
class="img-fluid" alt="avatar">
</div>
<% } %>
<div class="container">
<div class="row">
<div class="col-12 col-md-10 m-auto">
<%- body %>
</div>
</div>
</div>
</div>
</div>
<% } %>
<% if (theme.scroll_top_arrow.enable) { %>
<a id="scroll-top-button" aria-label="TOP" href="#" role="button">
<i class="iconfont icon-arrowup" aria-hidden="true"></i>
</a>
<% } %>
<% if (theme.search.enable) { %>
<%- partial('_partial/search') %>
<% } %>
<% if (theme.custom_html) { %>
<div class="col-lg-7 mx-auto nopadding-x-md">
<div class="container custom mx-auto">
<%- theme.custom_html %>
</div>
</div>
<% } %>
</main>
<%- partial('_partial/footer', { params: { subtitle: subtitle } }) %>
<!-- SCRIPTS -->
<%- partial('_partial/scripts') %>
</body>
</html>