Hexo美化:自适应切换渐进式加载首页图

1. 效果预览

2. 代码实现

2.1 首页顶部图渐进式加载

  • Path: 新建文件 themes/anzhiyu/source/js/imgloaded.js 新增以下内容,并按照注释调整照片路径
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    // 首页头图加载优化
    /**
    * @description 实现medium的渐进加载背景的效果
    */
    class ProgressiveLoad {
    constructor(smallSrc, largeSrc) {
    this.smallSrc = smallSrc;
    this.largeSrc = largeSrc;
    this.initTpl();
    }

    /**
    * @description 生成ui模板
    */
    initTpl() {
    this.container = document.createElement('div');
    this.smallStage = document.createElement('div');
    this.largeStage = document.createElement('div');
    this.smallImg = new Image();
    this.largeImg = new Image();
    this.container.className = 'pl-container';
    this.smallStage.className = 'pl-img pl-blur';
    this.largeStage.className = 'pl-img';
    this.container.appendChild(this.smallStage);
    this.container.appendChild(this.largeStage);
    this.smallImg.onload = this._onSmallLoaded.bind(this);
    this.largeImg.onload = this._onLargeLoaded.bind(this);
    }

    /**
    * @description 加载背景
    */
    progressiveLoad() {
    this.smallImg.src = this.smallSrc;
    this.largeImg.src = this.largeSrc;
    }

    /**
    * @description 大图加载完成
    */
    _onLargeLoaded() {
    this.largeStage.classList.add('pl-visible');
    this.largeStage.style.backgroundImage = `url('${this.largeSrc}')`;
    }

    /**
    * @description 小图加载完成
    */
    _onSmallLoaded() {
    this.smallStage.classList.add('pl-visible');
    this.smallStage.style.backgroundImage = `url('${this.smallSrc}')`;
    }
    }

    const executeLoad = (config, target) => {
    console.log('执行渐进背景替换');
    const isMobile = window.matchMedia('(max-width: 767px)').matches;
    const loader = new ProgressiveLoad(
    isMobile ? config.mobileSmallSrc : config.smallSrc,
    isMobile ? config.mobileLargeSrc : config.largeSrc
    );
    // 和背景图颜色保持一致,防止高斯模糊后差异较大
    if (target.children[0]) {
    target.insertBefore(loader.container, target.children[0]);
    }
    loader.progressiveLoad();
    };

    const config = {
    smallSrc: '/img/xiaotu.jpg', // 小图链接 尽可能配置小于100k的图片
    largeSrc: '/img/tu.jpg', // 大图链接 最终显示的图片
    mobileSmallSrc: '/img/sjxt.jpg', // 手机端小图链接 尽可能配置小于100k的图片
    mobileLargeSrc: '/img/sjdt.jpg', // 手机端大图链接 最终显示的图片
    enableRoutes: ['/'],
    };

    function initProgressiveLoad(config) {
    const target = document.getElementById('page-header');
    if (target && target.classList.contains('full_page')) {
    executeLoad(config, target);
    }
    }

    function onPJAXComplete(config) {
    const target = document.getElementById('page-header');
    if (target && target.classList.contains('full_page')) {
    initProgressiveLoad(config);
    }
    }

    document.addEventListener("DOMContentLoaded", function() {
    initProgressiveLoad(config);
    });

    document.addEventListener("pjax:complete", function() {
    onPJAXComplete(config);
    });
  • Path: 新建文件 themes/anzhiyu/source/css/imgloaded.css 新增以下内容,并按照注释自行决定调整内容
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
/* 首页头图加载 */  
.pl-container {
width: 100%;
height: 100%;
position: relative; /* 一图流这里改fixed */
/* 一图流这里加z-index: -2; */
overflow: hidden;
will-change: transform; /* 添加性能优化 */
/* blur-to-clear模糊动画2s */
animation: blur-to-clear 2s cubic-bezier(.62,.21,.25,1) 0s 1 normal backwards running, scale 1.5s cubic-bezier(.62,.21,.25,1) 0s 1 both;
}
.pl-img {
width: 100%;
height: 100%;
position: absolute;
background-position: center;
background-size: cover;
background-repeat: no-repeat;
opacity: 0;
transition: opacity 1s;
}

@keyframes blur-to-clear {
0% {
filter: blur(50px);
opacity: 1;
}
100% {
filter: blur(0);
opacity: 1;
}
}

@keyframes scale {
0% {
transform: scale(1.5) translateZ(0);
opacity: 0;
}
to {
transform: scale(1) translateZ(0);
opacity: 1;
}
}

.pl-visible {
opacity: 1;
}

.pl-blur {
/* 小图锯齿多,增加高斯模糊 */
filter: blur(50px);
}

2.2 首页一图流渐进式加载

  • Path: 新建文件 themes/anzhiyu/source/js/imgloaded.js 新增以下内容,并按照注释调整照片路径
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
// 首页一图流加载优化
/**
* @description 实现medium的渐进加载背景的效果
*/
(function() {
class ProgressiveLoad {
constructor(smallSrc, largeSrc) {
this.smallSrc = smallSrc;
this.largeSrc = largeSrc;
this.initTpl();
this.container.addEventListener('animationend', () => {
this.smallStage.style.display = 'none';
}, {once: true});
}

initTpl() {
this.container = document.createElement('div');
this.smallStage = document.createElement('div');
this.largeStage = document.createElement('div');
this.smallImg = new Image();
this.largeImg = new Image();
this.container.className = 'pl-container';
this.smallStage.className = 'pl-img pl-blur';
this.largeStage.className = 'pl-img';
this.container.appendChild(this.smallStage);
this.container.appendChild(this.largeStage);
this.smallImg.onload = this._onSmallLoaded.bind(this);
this.largeImg.onload = this._onLargeLoaded.bind(this);
}

progressiveLoad() {
this.smallImg.src = this.smallSrc;
this.largeImg.src = this.largeSrc;
}

_onLargeLoaded() {
this.largeStage.classList.add('pl-visible');
this.largeStage.style.backgroundImage = `url('${this.largeSrc}')`;
}

_onSmallLoaded() {
this.smallStage.classList.add('pl-visible');
this.smallStage.style.backgroundImage = `url('${this.smallSrc}')`;
}
}

const executeLoad = (config, target) => {
console.log('执行渐进背景替换');
const isMobile = window.matchMedia('(max-width: 767px)').matches;
const loader = new ProgressiveLoad(
isMobile ? config.mobileSmallSrc : config.smallSrc,
isMobile ? config.mobileLargeSrc : config.largeSrc
);
if (target.children[0]) {
target.insertBefore(loader.container, target.children[0]);
}
loader.progressiveLoad();
};

const ldconfig = {
light: {
smallSrc: '/img/bg2_80kbver.jpg', //浅色模式 小图链接 尽可能配置小于100k的图片
largeSrc: '/img/bg2.jpg', //浅色模式 大图链接 最终显示的图片
mobileSmallSrc: '/img/bg2_80kbver.jpg', //手机端浅色小图链接 尽可能配置小于100k的图片
mobileLargeSrc: '/img/bg2.jpg', //手机端浅色大图链接 最终显示的图片
enableRoutes: ['/'],
},
dark: {
smallSrc: '/img/bg1_80kbver.jpg', //深色模式 小图链接 尽可能配置小于100k的图片
largeSrc: '/img/bg1.jpg', //深色模式 大图链接 最终显示的图片
mobileSmallSrc: '/img/bg1_80kbver.jpg', //手机端深色模式小图链接 尽可能配置小于100k的图片
mobileLargeSrc: '/img/bg1.jpg', //手机端深色大图链接 最终显示的图片
enableRoutes: ['/'],
},
};

const getCurrentTheme = () => {
return document.documentElement.getAttribute('data-theme');
}

const onThemeChange = () => {
const currentTheme = getCurrentTheme();
const config = ldconfig[currentTheme];
initProgressiveLoad(config);
document.addEventListener("DOMContentLoaded", function() {
initProgressiveLoad(config);
});

document.addEventListener("pjax:complete", function() {
onPJAXComplete(config);
});
}

let initTheme = getCurrentTheme();
let initConfig = ldconfig[initTheme];
initProgressiveLoad(initConfig);

const observer = new MutationObserver(mutations => {
mutations.forEach(mutation => {
if (mutation.attributeName === "data-theme" && location.pathname === '/') {
onThemeChange();
}
});
});

observer.observe(document.documentElement, {
attributes: true,
attributeFilter: ["data-theme"]
});

function initProgressiveLoad(config) {
const container = document.querySelector('.pl-container');
if (container) {
container.remove();
}
const target = document.getElementById('page-header');
if (target && target.classList.contains('full_page')) {
executeLoad(config, target);
}
}

function onPJAXComplete(config) {
const target = document.getElementById('page-header');
if (target && target.classList.contains('full_page')) {
initProgressiveLoad(config);
}
}

})();
  • Path:新建文件 themes/anzhiyu/source/css/imgloaded.css 新增以下内容,并按照注释自行决定调整内容
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
/* 首页头图加载 */
.pl-container {
width: 100%;
height: 100%;
z-index: -2;
position: fixed;
overflow: hidden;
will-change: transform; /* 添加性能优化 */
animation: blur-to-clear 2s cubic-bezier(.62,.21,.25,1) 0s 1 normal backwards running, scale 1.5s cubic-bezier(.62,.21,.25,1) 0s 1 both;
}
.pl-img {
width: 100%;
height: 100%;
position: absolute;
background-position: center;
background-size: cover;
background-repeat: no-repeat;
opacity: 0;
transition: opacity 1s;
}

@keyframes blur-to-clear {
0% {
filter: blur(50px);
opacity: 1;
}
100% {
filter: blur(0);
opacity: 1;
}
}

@keyframes scale {
0% {
transform: scale(1.5) translateZ(0);
opacity: 0;
}
to {
transform: scale(1) translateZ(0);
opacity: 1;
}
}

.pl-visible {
opacity: 1;
}

.pl-blur {
/* 小图锯齿多,增加高斯模糊 */
filter: blur(50px);
}

3. 引入文件

  • _config.anzhiyu.yml 主题配置文件下 inject 配置项中 headbottom
  • 分别引入 imgloaded.cssimgloaded.js 文件
1
2
3
4
5
6
inject:  
head:
- <link rel="stylesheet" href="/css/imgloaded.css?1">

bottom:
- <script async data-pjax src="/js/imgloaded.js?1"></script> # 首页图片渐进式加载
  1. 配置图片
  • 为了使用顶部图功能,你必须在主题配置文件中设置 top_image 为 true。然后,你可以在 top_image_url 中留空或者填写任意字符串,因为这个值不会影响图片的显示。图片的加载和渲染是由 js 文件实现的,所以你不需要在配置文件中提供图片的源地址。
1
2
# The banner image of home page
index_img: "background: url() top / cover no-repeat"
  • imgloaded.js 中的 73 到 76 行(或是 83 到 86 行)修改以下示例的部分
  • 若是首页一图流渐进式加载的 imgloaded.js 则修改 56 到 71 行
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const ldconfig = {
light: {
smallSrc: 'https://blog.bornforthis.cn/img/bg2_80kbver.jpg', //浅色模式 小图链接 尽可能配置小于100k的图片
largeSrc: 'https://blog.bornforthis.cn/img/bg2.jpg', //浅色模式 大图链接 最终显示的图片
mobileSmallSrc: 'https://blog.bornforthis.cn/img/bg2_80kbver.jpg', //手机端浅色小图链接 尽可能配置小于100k的图片
mobileLargeSrc: 'https://blog.bornforthis.cn/img/bg2.jpg', //手机端浅色大图链接 最终显示的图片
enableRoutes: ['/'],
},
dark: {
smallSrc: 'https://blog.bornforthis.cn/img/bg1_80kbver.jpg', //深色模式 小图链接 尽可能配置小于100k的图片
largeSrc: 'https://blog.bornforthis.cn/img/bg1.jpg', //深色模式 大图链接 最终显示的图片
mobileSmallSrc: 'https://blog.bornforthis.cn/img/bg1_80kbver.jpg', //手机端深色模式小图链接 尽可能配置小于100k的图片
mobileLargeSrc: 'https://blog.bornforthis.cn/img/bg1.jpg', //手机端深色大图链接 最终显示的图片
enableRoutes: ['/'],
},
};

4. 图片懒加载配置修改

1
2
3
4
5
6
lazyload:
enable: true
field: post # site/post
placeholder:
blur: true
progressive: true

5. 大功告成

到这时候,如果你的图片文件配置正确,可以执行 Hexo 的三连命令来查看效果了!

🪧
公众号:AI悦创【二维码】

AI悦创·编程一对一

AI悦创·推出辅导班啦,包括「Python 语言辅导班、C++ 辅导班、java 辅导班、算法/数据结构辅导班、少儿编程、pygame 游戏开发、Web、Linux」,全部都是一对一教学:一对一辅导 + 一对一答疑 + 布置作业 + 项目实践等。当然,还有线下线上摄影课程、Photoshop、Premiere 一对一教学、QQ、微信在线,随时响应!微信:Jiabcdefh

C++ 信息奥赛题解,长期更新!长期招收一对一中小学信息奥赛集训,莆田、厦门地区有机会线下上门,其他地区线上。微信:Jiabcdefh

方法一:QQ

方法二:微信:Jiabcdefh