From 3abec3cb33e751076d26ecac1712fcf22276734f Mon Sep 17 00:00:00 2001 From: XPoet Date: Thu, 7 Jan 2021 15:15:39 +0800 Subject: [PATCH] feat: add lazyload image --- _config.yml | 7 +++ layout/_partial/scripts.ejs | 4 ++ scripts/filters/lazyload-handle.js | 24 ++++++++++ scripts/helpers/export-config.js | 5 +- source/css/common/basic.styl | 47 ++++++++++++------- source/css/common/markdown.styl | 1 + source/css/common/variables.styl | 30 +++++++----- source/css/layout/_partial/archive-list.styl | 2 +- .../css/layout/_partial/comment/comment.styl | 2 +- source/css/layout/article-content.styl | 6 +-- source/css/layout/category-content.styl | 2 +- source/css/layout/page.styl | 20 ++++---- source/css/layout/tag-content.styl | 2 +- source/images/loading.svg | 17 +++++++ source/js/lazyload.js | 41 ++++++++++++++++ source/js/main.js | 4 ++ 16 files changed, 166 insertions(+), 48 deletions(-) create mode 100644 scripts/filters/lazyload-handle.js create mode 100644 source/images/loading.svg create mode 100644 source/js/lazyload.js diff --git a/_config.yml b/_config.yml index 7fdd554..02e865b 100644 --- a/_config.yml +++ b/_config.yml @@ -176,6 +176,13 @@ rss: enable: false +# --------------------------------------------------------------------------------------- +# lazyload image +# --------------------------------------------------------------------------------------- +lazyload: + enable: false + + # --------------------------------------------------------------------------------------- # CDN # --------------------------------------------------------------------------------------- diff --git a/layout/_partial/scripts.ejs b/layout/_partial/scripts.ejs index 3306667..1c71a8f 100644 --- a/layout/_partial/scripts.ejs +++ b/layout/_partial/scripts.ejs @@ -14,6 +14,10 @@ <%- __js('js/code-copy.js') %> <% } %> +<% if (theme.lazyload.enable) { %> + <%- __js('js/lazyload.js') %> +<% } %> +
<% if (theme.toc.enable && is_post()) { %> <%- __js([ diff --git a/scripts/filters/lazyload-handle.js b/scripts/filters/lazyload-handle.js new file mode 100644 index 0000000..8e63e35 --- /dev/null +++ b/scripts/filters/lazyload-handle.js @@ -0,0 +1,24 @@ +'use strict' +hexo.extend.filter.register( + 'after_post_render', + function (data) { + const theme = hexo.theme.config; + if (!theme.lazyload || !theme.lazyload.enable) return; + data.content = data.content.replace( + // Match 'img' tags width the src attribute. + /]*)src="([^"]*)"([^>]*)>/gim, + function (match, attrBegin, src, attrEnd) { + // Exit if the src doesn't exists. + if (!src) return match; + + return `` + } + ) + }, + 1 +); diff --git a/scripts/helpers/export-config.js b/scripts/helpers/export-config.js index 63d641f..95eb017 100644 --- a/scripts/helpers/export-config.js +++ b/scripts/helpers/export-config.js @@ -14,7 +14,7 @@ hexo.extend.helper.register('export_config', function () { let {config, theme} = this; - // ------------ export language to js ------------ + // ------------------------ export language to js ------------------------ const languageDir = path.join(__dirname, '../../languages'); let file = fs.readdirSync(languageDir).find(v => v === `${config.language}.yml`); file = languageDir + '/' + (file ? file : 'en.yml'); @@ -24,7 +24,7 @@ hexo.extend.helper.register('export_config', function () { } catch (e) { console.log(e); } - // ----------------------------------------------- + // ----------------------------------------------------------------------- let hexo_config = { @@ -43,6 +43,7 @@ hexo.extend.helper.register('export_config', function () { code_copy: theme.code_copy, side_tools: theme.side_tools, pjax: theme.pjax, + lazyload: theme.lazyload, version: theme.version, } diff --git a/source/css/common/basic.styl b/source/css/common/basic.styl index 66d5272..162aae2 100644 --- a/source/css/common/basic.styl +++ b/source/css/common/basic.styl @@ -3,9 +3,9 @@ @require 'keep-theme.styl' -// ============================ +// ====================================================================== // html, body -// ============================ +// ====================================================================== html, body { margin: 0; padding: 0; @@ -29,9 +29,9 @@ html, body { } -// ============================ +// ====================================================================== // scrollbar -// ============================ +// ====================================================================== * { &::-webkit-scrollbar { width: 6px; @@ -48,18 +48,18 @@ html, body { } -// ============================ +// ====================================================================== // selection -// ============================ +// ====================================================================== ::selection { background: var(--selection-color); color: #fff; } -// ============================ +// ====================================================================== // ul, ol, li -// ============================ +// ====================================================================== ul, ol, li { padding: 0; margin: 0; @@ -67,9 +67,9 @@ ul, ol, li { } -// ============================ +// ====================================================================== // a -// ============================ +// ====================================================================== a { text-decoration: none; color: var(--default-text-color); @@ -82,9 +82,24 @@ a { } -// ============================ +// ====================================================================== +// img +// ====================================================================== +img { + &[lazyload] { + padding: 20px; + cursor: not-allowed; + pointer-events: none; + margin: 20px auto !important; + background: var(--lazyload-background-color); + transition(); + } +} + + +// ====================================================================== // button -// ============================ +// ====================================================================== button { padding: 0; margin: 0; @@ -113,9 +128,9 @@ button { } -// ============================ +// ====================================================================== // flex center -// ============================ +// ====================================================================== .flex-center { display: flex; justify-content: center; @@ -123,9 +138,9 @@ button { } -// ============================ +// ====================================================================== // clear float -// ============================ +// ====================================================================== .clear { clear: both; } diff --git a/source/css/common/markdown.styl b/source/css/common/markdown.styl index 364ecb7..18a40e7 100644 --- a/source/css/common/markdown.styl +++ b/source/css/common/markdown.styl @@ -172,6 +172,7 @@ cursor: zoom-in; display: block; box-shadow: 0 0 2px var(--shadow-color); + transition(); if (hexo-config('style.article_img_align') == 'center') { margin: 10px auto 2px; diff --git a/source/css/common/variables.styl b/source/css/common/variables.styl index 4291982..b642f83 100644 --- a/source/css/common/variables.styl +++ b/source/css/common/variables.styl @@ -6,24 +6,25 @@ // ======================================================================================== // layout // ======================================================================================== -$header-height = 76px; // 头部默认高度 -$header-shrink-height = $header-height * 0.72; // 头部收缩高度 -$scroll-progress-bar-height = 2px; // 头部进度条高度 -$main-content-width = 80%; // 中间内容区域宽度(PC) -$main-content-width-tablet = 85%; // 中间内容区域宽度(平板) -$main-content-width-mobile = 90%; // 中间内容区域宽度(手机) -$circle-button-width = 40px; // tools 圆形工具按钮宽度 -$tools-button-width = 32px; // tools 方形工具按钮宽度 -$component-interspace = 30px; // 组件/模块的间隔值(PC) +$header-height = 76px; // header height +$header-shrink-height = $header-height * 0.72; // header shrink height +$scroll-progress-bar-height = 2px; // scroll progress bar height +$main-content-width = 80%; // main content width (tablet) +$main-content-width-tablet = 86%; // main content width (PC) +$main-content-width-mobile = 90%; // main content width (mobile) +$circle-button-width = 38px; // post tool button width +$component-spacing-value = 30px; // component-spacing-value (PC) + +// main content max width $temp-content-max-width = hexo-config('style.content_max_width'); $content-max-width = $temp-content-max-width ? convert($temp-content-max-width) : 1000px; // ======================================================================================== -// 媒体查询 +// media query // ======================================================================================== -$media-max-width = 780px; // 媒体查询最大宽度 (平板) -$media-max-width-mobile = 500px; // 媒体查询最大宽度(手机) +$media-max-width = 780px; // media query max width (tablet) +$media-max-width-mobile = 500px; // media query max width (mobile) keep-tablet() @media (max-width: $media-max-width) @@ -76,6 +77,7 @@ $link-color = darken($default-text-color, 10%); $copyright-info-color = #CC0033; $avatar-background-color = #0066CC; $loading-progress-bar-color = #990000; +$lazyload-background-color = rgba(200, 200, 200, 0.5); // ======================================================================================== @@ -99,6 +101,7 @@ $dark-link-color = lighten($dark-default-text-color, 10%); $dark-copyright-info-color = darken($copyright-info-color, 20%); $dark-avatar-background-color = darken($avatar-background-color, 10%); $dark-loading-progress-bar-color = lighten($loading-progress-bar-color, 50%); +$dark-lazyload-background-color = rgba(50, 50, 50, 0.5); // ======================================================================== @@ -131,7 +134,8 @@ root-color(mode) { --link-color: mode == 'light' ? $link-color : $dark-link-color; --copyright-info-color: mode == 'light' ? $copyright-info-color : $dark-copyright-info-color; --avatar-background-color: mode == 'light' ? $avatar-background-color : $dark-avatar-background-color; - --loading-progress-bar-color: mode == 'light' ? $loading-progress-bar-color : $dark-loading-progress-bar-color; + --loading-progress-bar-color : mode == 'light' ? $loading-progress-bar-color : $dark-loading-progress-bar-color; + --lazyload-background-color : mode == 'light' ? $lazyload-background-color : $dark-lazyload-background-color; } diff --git a/source/css/layout/_partial/archive-list.styl b/source/css/layout/_partial/archive-list.styl index 530fd07..c52dfd2 100644 --- a/source/css/layout/_partial/archive-list.styl +++ b/source/css/layout/_partial/archive-list.styl @@ -6,7 +6,7 @@ $article-date-font-size = 1rem; .archive-list-container { .archive-item { - margin-bottom: $component-interspace; + margin-bottom: $component-spacing-value; &:last-child { margin-bottom: 0; diff --git a/source/css/layout/_partial/comment/comment.styl b/source/css/layout/_partial/comment/comment.styl index ce87332..d577269 100644 --- a/source/css/layout/_partial/comment/comment.styl +++ b/source/css/layout/_partial/comment/comment.styl @@ -12,7 +12,7 @@ if (hexo-config('comment.valine.enable') && hexo-config('comment.gitalk.enable') .comments-container { display: inline-block; - margin-top: $component-interspace; + margin-top: $component-spacing-value; width: 100%; #comment-anchor { diff --git a/source/css/layout/article-content.styl b/source/css/layout/article-content.styl index 7af6e2b..6acb42e 100644 --- a/source/css/layout/article-content.styl +++ b/source/css/layout/article-content.styl @@ -91,7 +91,7 @@ $article-title-font-size = 1.6rem; .article-content { - margin-top: $component-interspace; + margin-top: $component-spacing-value; padding-bottom: 10px; border-bottom: 1px solid var(--border-color); word-wrap: break-word; @@ -99,13 +99,13 @@ $article-title-font-size = 1.6rem; } .post-copyright-info { - margin-top: $component-interspace; + margin-top: $component-spacing-value; width: 100%; } .article-nav { height: 40px; - margin-top: $component-interspace; + margin-top: $component-spacing-value; .article-prev, .article-next { max-width: $post-nav-max-width; diff --git a/source/css/layout/category-content.styl b/source/css/layout/category-content.styl index 30e4b94..2556b6d 100644 --- a/source/css/layout/category-content.styl +++ b/source/css/layout/category-content.styl @@ -18,7 +18,7 @@ $category-name-font-size = 1.6rem; font-weight: 600; padding-bottom: 20px; - margin-bottom: $component-interspace; + margin-bottom: $component-spacing-value; border-bottom: 1px solid var(--border-color); } } diff --git a/source/css/layout/page.styl b/source/css/layout/page.styl index a3b17b6..0ed39ea 100644 --- a/source/css/layout/page.styl +++ b/source/css/layout/page.styl @@ -78,14 +78,14 @@ $page-aside-width = $temp-width ? convert($temp-width) : 260px; width: 100%; display: flex; justify-content: center; - padding: $component-interspace 0; + padding: $component-spacing-value 0; +keep-tablet() { - padding: $component-interspace * 0.8 0; + padding: $component-spacing-value * 0.8 0; } +keep-mobile() { - padding: $component-interspace * 0.6 0; + padding: $component-spacing-value * 0.6 0; } .main-content { @@ -137,25 +137,25 @@ $page-aside-width = $temp-width ? convert($temp-width) : 260px; .post-tools { position: fixed; - top: $header-height + $component-interspace; - right: $component-interspace; + top: $header-height + $component-spacing-value; + right: $component-spacing-value; transition(); .header-shrink & { - top: $header-shrink-height + $component-interspace; + top: $header-shrink-height + $component-spacing-value; +keep-tablet() { - top: $header-shrink-height * 0.9 + $component-interspace; + top: $header-shrink-height * 0.9 + $component-spacing-value; } +keep-mobile() { - top: $header-shrink-height * 0.8 + $component-interspace; + top: $header-shrink-height * 0.8 + $component-spacing-value; } } +keep-tablet() { - top: $header-height * 0.9 + $component-interspace; + top: $header-height * 0.9 + $component-spacing-value; right: 10px; transform: scale(0.82); transform-origin: right top; @@ -163,7 +163,7 @@ $page-aside-width = $temp-width ? convert($temp-width) : 260px; +keep-mobile() { - top: $header-height * 0.8 + $component-interspace; + top: $header-height * 0.8 + $component-spacing-value; right: 5px; transform: scale(0.72); } diff --git a/source/css/layout/tag-content.styl b/source/css/layout/tag-content.styl index e568312..9c608e6 100644 --- a/source/css/layout/tag-content.styl +++ b/source/css/layout/tag-content.styl @@ -18,7 +18,7 @@ $tag-name-font-size = 1.6rem; font-weight: 600; padding-bottom: 20px; - margin-bottom: $component-interspace; + margin-bottom: $component-spacing-value; border-bottom: 1px solid var(--border-color); } } diff --git a/source/images/loading.svg b/source/images/loading.svg new file mode 100644 index 0000000..004a356 --- /dev/null +++ b/source/images/loading.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/source/js/lazyload.js b/source/js/lazyload.js new file mode 100644 index 0000000..40ead8e --- /dev/null +++ b/source/js/lazyload.js @@ -0,0 +1,41 @@ +KEEP.initLazyLoad = () => { + const imgs = document.querySelectorAll('img'); + let now = Date.now(); + let needLoad = true; + + function lazyload(imgs) { + now = Date.now(); + needLoad = Array.from(imgs).some(i => i.hasAttribute('lazyload')); + + const h = window.innerHeight; + const s = document.documentElement.scrollTop || document.body.scrollTop; + + imgs.forEach(img => { + if (img.hasAttribute('lazyload') && !img.hasAttribute('loading')) { + + if ((h + s) > img.offsetTop) { + img.setAttribute('loading', true); + const loadImageTimeout = setTimeout(() => { + const temp = new Image(); + const src = img.getAttribute('data-src'); + temp.src = src; + temp.onload = () => { + img.src = src; + img.removeAttribute('lazyload'); + img.removeAttribute('loading'); + clearTimeout(loadImageTimeout); + } + }, 1000); + } + } + }); + } + + lazyload(imgs); + + window.onscroll = () => { + if (Date.now() - now > 100 && needLoad) { + lazyload(imgs); + } + } +} diff --git a/source/js/main.js b/source/js/main.js index 6c42b24..9477663 100644 --- a/source/js/main.js +++ b/source/js/main.js @@ -20,6 +20,10 @@ window.addEventListener('DOMContentLoaded', () => { if (KEEP.theme_config.code_copy.enable === true) { KEEP.initCodeCopy(); } + + if (KEEP.theme_config.lazyload.enable === true) { + KEEP.initLazyLoad(); + } } KEEP.refresh();