feat: add shimmer background when image loads

This commit is contained in:
Cotes Chung 2022-12-13 21:41:32 +08:00
parent bffaf6374f
commit ab16fdc7fc
No known key found for this signature in database
GPG key ID: 0D9E54843167A808
10 changed files with 207 additions and 115 deletions

View file

@ -81,21 +81,22 @@
{% continue %} {% continue %}
{% endunless %} {% endunless %}
{% assign _left = _left | remove: ' /' | replace: ' w=', ' width=' | replace: ' h=', ' height=' %} {% assign _left = _left | remove: ' /' | replace: ' w=', ' width=' | replace: ' h=', ' height=' %}
{% assign _attrs = _left | split: ' ' %} {% assign _attrs = _left | split: '" ' %}
{% assign _width = nil %} {% assign _width = nil %}
{% assign _height = nil %} {% assign _height = nil %}
{% assign _lqip = nil %} {% assign _lqip = nil %}
{% assign _class = nil %}
{% for _attr in _attrs %} {% for _attr in _attrs %}
{% assign _pair = _attr | split: '="' %} {% unless _attr contains '=' %}
{% if _pair.size < 2 %}
{% continue %} {% continue %}
{% endif %} {% endunless %}
{% assign _pair = _attr | remove: '"' | split: '=' %}
{% capture _key %}{{ _pair | first }}{% endcapture %} {% capture _key %}{{ _pair | first }}{% endcapture %}
{% capture _value %}{{ _pair | last | remove: '"' }}{% endcapture %} {% capture _value %}{{ _pair | last }}{% endcapture %}
{% case _key %} {% case _key %}
{% when 'width' %} {% when 'width' %}
@ -106,10 +107,20 @@
{% assign _src = _value %} {% assign _src = _value %}
{% when 'lqip' %} {% when 'lqip' %}
{% assign _lqip = _value %} {% assign _lqip = _value %}
{% when 'class' %}
{% assign _class = _value %}
{% endcase %} {% endcase %}
{% endfor %} {% endfor %}
<!-- take out classes -->
{% if _class %}
{% capture _old_class %}class="{{ _class }}"{% endcapture %}
{% assign _left = _left | remove: _old_class %}
{% endif %}
{% assign _final_src = nil %}
{% unless _src contains '//' %} {% unless _src contains '//' %}
{% assign _final_src = _path_prefix | append: _src %} {% assign _final_src = _path_prefix | append: _src %}
{% capture _src_from %}"{{ _src }}"{% endcapture %} {% capture _src_from %}"{{ _src }}"{% endcapture %}
@ -127,31 +138,51 @@
{% endif %} {% endif %}
<!-- lazy-load images <https://github.com/aFarkas/lazysizes#readme> --> <!-- lazy-load images <https://github.com/aFarkas/lazysizes#readme> -->
{% assign _left = _left | replace: 'src=', 'data-src=' %}
{% if _left contains 'class=' %} {% if _left contains 'class=' %}
{% assign _left = _left | replace: 'class="', 'class="lazyload ' %} {% assign _left = _left | replace: 'class="', 'class="lazyload '%}
{% else %} {% else %}
{% assign _left = _left | replace: 'src=', 'class="lazyload" src=' %} {% assign _left = _left | append: ' class="lazyload"' %}
{% endif %} {% endif %}
{% assign _left = _left | replace: 'src=', 'data-src=' %} <!-- add image placeholder -->
<!-- add placeholder -->
{% if _lqip %} {% if _lqip %}
{% assign _left = _left | replace: ' lqip=', ' data-lqip="true" src=' %} {% assign _left = _left | replace: ' lqip=', ' data-lqip="true" src=' %}
{% else %} {% else %}
{% if _width and _height %} {% if _width and _height %}
<!-- Add SVG placehoder to prevent layout reflow --> <!-- add SVG placehoder -->
{%- capture _svg -%} {%- capture _svg -%}
src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 {{ _width }} {{ _height }}'%3E%3C/svg%3E" src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 {{ _width }} {{ _height }}'%3E%3C/svg%3E"
{%- endcapture -%} {%- endcapture -%}
{% assign _left = _svg | append: ' ' | append: _left %} {% assign _left = _svg | append: ' ' | append: _left %}
{% assign _class = _class | append: ' shimmer' %}
{% endif %} {% endif %}
{% endif %} {% endif %}
<!-- Bypass the HTML-proofer test --> <!-- Bypass the HTML-proofer test -->
{% assign _left = _left | append: ' data-proofer-ignore' %} {% assign _left = _left | append: ' data-proofer-ignore' %}
<!-- Combine --> <!-- make sure the `<img>` is wrapped by `<a>` -->
{% assign _parent = _right | slice: 1, 4 %}
{% if _parent == '</a>' %}
<!-- add class to exist <a> tag -->
{% assign _size = _img_content | size | minus: 1 %}
{% capture _class %}
class="img-link{% unless _lqip %} shimmer{% endunless %}"
{% endcapture %}
{% assign _img_content = _img_content | slice: 0, _size | append: _class | append: '>' %}
{% else %}
<!-- create the image wrapper -->
{%- capture _wrapper_start -%}
<a href="{{ _final_src | default: _src }}" class="popup img-link {{ _class }}">
{%- endcapture -%}
{% assign _img_content = _img_content | append: _wrapper_start %}
{% assign _right = _right | prepend: '></a' %}
{% endif %}
<!-- combine -->
{% assign _img_content = _img_content | append: IMG_TAG | append: _left | append: _right %} {% assign _img_content = _img_content | append: IMG_TAG | append: _left | append: _right %}
{% endfor %} {% endfor %}

View file

@ -1,26 +1,13 @@
/** /**
Set up image popup stuff (https://github.com/dimsemenov/Magnific-Popup) * Set up image stuff
*/ */
$(function () { (function() {
const IMG_SCOPE = '#main > div.row:first-child > div:first-child'; if ($('#core-wrapper img[data-src]') <= 0) {
if ($(`${IMG_SCOPE} img`).length <= 0) {
return; return;
} }
/* popup */ /* See: <https://github.com/dimsemenov/Magnific-Popup> */
$(`${IMG_SCOPE} p > img[data-src], ${IMG_SCOPE} img[data-src].preview-img`).each(
function () {
let nextTag = $(this).next();
const title = nextTag.prop('tagName') === 'EM' ? nextTag.text() : '';
const src = $(this).attr('data-src'); // created by lozad.js
$(this).wrap(`<a href="${src}" title="${title}" class="popup"></a>`);
}
);
$('.popup').magnificPopup({ $('.popup').magnificPopup({
type: 'image', type: 'image',
closeOnContentClick: true, closeOnContentClick: true,
@ -32,8 +19,10 @@ $(function () {
} }
}); });
/* markup the image links */ /* Stop shimmer when image loaded */
document.addEventListener('lazyloaded', function(e) {
const $img = $(e.target);
$img.parent().removeClass('shimmer');
});
$(`${IMG_SCOPE} a`).has('img').addClass('img-link'); })();
});

View file

@ -27,32 +27,37 @@ tail_includes:
{% endif %} {% endif %}
{% if page.image %} {% if page.image %}
<div class="mt-3 mb-3"> {% capture src %}src="{{ page.image.path | default: page.image }}"{% endcapture %}
<img src="{{ page.image.path | default: page.image }}" {% capture class %}class="preview-img{% if page.image.no_bg %}{{ ' no-bg' }}{% endif %}"{% endcapture %}
class="preview-img{% if page.image.no_bg %}{{ ' no-bg' }}{% endif %}" {% capture alt %}alt="{{ page.image.alt | default: "Preview Image" }}"{% endcapture %}
alt="{{ page.image.alt | default: "Preview Image" }}"
{% capture w %}
{% if page.image.width %} {% if page.image.width %}
width="{{ page.image.width }}" width="{{ page.image.width }}"
{% elsif page.image.w %} {% elsif page.image.w %}
width="{{ page.image.w }}" width="{{ page.image.w }}"
{% endif %} {% endif %}
{% endcapture %}
{% capture h %}
{% if page.image.height %} {% if page.image.height %}
height="{{ page.image.height }}" height="{{ page.image.height }}"
{% elsif page.image.h %} {% elsif page.image.h %}
height="{{ page.image.h }}" h="{{ page.image.h }}"
{% endif %} {% endif %}
{% endcapture %}
{% capture lqip %}
{% if page.image.lqip %} {% if page.image.lqip %}
lqip="{{ page.image.lqip }}" lqip="{{ page.image.lqip }}"
{% endif %} {% endif %}
{% endcapture %}
><!-- endof img tag --> <div class="mt-3 mb-3">
<img {{ src }} {{ class }} {{ w | strip }} {{ h | strip }} {{ lqip | strip }} {{ alt }}>
{% if page.image.alt %} {%- if page.image.alt -%}
<figcaption class="text-center pt-2 pb-2">{{ page.image.alt }}</figcaption> <figcaption class="text-center pt-2 pb-2">{{ page.image.alt }}</figcaption>
{% endif %} {%- endif -%}
</div> </div>
{% endif %} {% endif %}

View file

@ -83,6 +83,47 @@ a {
img { img {
max-width: 100%; max-width: 100%;
height: auto; height: auto;
&[data-src] {
&.lazyloaded {
z-index: 1;
-webkit-animation: fade-in 0.4s ease-in;
animation: fade-in 0.4s ease-in;
}
&[data-lqip="true"] {
&.lazyload,
&.lazyloading {
-webkit-filter: blur(20px);
filter: blur(20px);
}
}
&:not([data-lqip="true"]) {
&.lazyload,
&.lazyloading {
background: var(--img-bg);
}
}
&.shadow {
-webkit-filter: drop-shadow(2px 4px 6px rgba(0, 0, 0, 0.08));
filter: drop-shadow(2px 4px 6px rgba(0, 0, 0, 0.08));
box-shadow: none !important; /* cover the Bootstrap 4.6.1 styles */
}
@extend %img-caption;
}
@-webkit-keyframes fade-in {
from { opacity: 0; }
to { opacity: 1; }
}
@keyframes fade-in {
from { opacity: 0; }
to { opacity: 1; }
}
} }
blockquote { blockquote {
@ -183,49 +224,6 @@ i { /* fontawesome icons */
} }
} }
img[data-src] {
@at-root #main #{&} {
margin: 0.5rem 0;
}
&[data-lqip="true"] {
&.lazyload,
&.lazyloading {
filter: blur(20px);
}
}
&:not([data-lqip="true"]) {
&.lazyload,
&.lazyloading {
opacity: 0;
}
&.lazyloaded {
opacity: 1;
transition: opacity 0.5s;
}
}
&.left {
float: left;
margin: 0.75rem 1rem 1rem 0;
}
&.right {
float: right;
margin: 0.75rem 0 1rem 1rem;
}
&.shadow {
-webkit-filter: drop-shadow(2px 4px 6px rgba(0, 0, 0, 0.08));
filter: drop-shadow(2px 4px 6px rgba(0, 0, 0, 0.08));
box-shadow: none !important; /* cover the Bootstrap 4.6.1 styles */
}
@extend %img-caption;
}
/* --- Panels --- */ /* --- Panels --- */
.access { .access {
@ -406,26 +404,14 @@ img[data-src] {
margin-bottom: 1.5rem; margin-bottom: 1.5rem;
} }
img[data-src]:not(.normal):not(.left):not(.right) { p {
@include align-center; > img[data-src],
} > a.popup {
&:not(.normal):not(.left):not(.right) {
a { @include align-center;
&.img-link {
@extend %no-cursor;
}
/* created by `_includes/img-extra.html` */
&.popup {
cursor: zoom-in;
}
&:hover {
code {
@extend %link-hover;
} }
} }
} /* a */ }
} }
.pageviews .fa-spinner { .pageviews .fa-spinner {
@ -457,6 +443,14 @@ img[data-src] {
overflow-wrap: break-word; overflow-wrap: break-word;
a { a {
&.popup {
@extend %no-cursor;
@extend %img-caption;
@include mt-mb(0.5rem);
cursor: zoom-in;
}
&:not(.img-link) { &:not(.img-link) {
@extend %link-underline; @extend %link-underline;
@ -464,10 +458,6 @@ img[data-src] {
@extend %link-hover; @extend %link-hover;
} }
} }
&.img-link {
@extend %img-caption;
}
} }
ol, ol,
@ -553,6 +543,39 @@ img[data-src] {
} }
} }
.img-link {
color: transparent;
display: inline-flex;
@extend %img-rounded;
}
.shimmer {
overflow: hidden;
position: relative;
background: var(--img-bg);
&::before {
content: "";
position: absolute;
background: var(--shimmer-bg);
height: 100%;
width: 100%;
-webkit-animation: shimmer 1s infinite;
animation: shimmer 1s infinite;
}
@-webkit-keyframes shimmer {
0% { -webkit-transform: translateX(-100%); transform: translateX(-100%); }
100% { -webkit-transform: translateX(100%); transform: translateX(100%); }
}
@keyframes shimmer {
0% { -webkit-transform: translateX(-100%); transform: translateX(-100%); }
100% { -webkit-transform: translateX(100%); transform: translateX(100%); }
}
}
/* --- buttons --- */ /* --- buttons --- */
.btn-lang { .btn-lang {
border: 1px solid !important; border: 1px solid !important;
@ -622,6 +645,16 @@ img[data-src] {
transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out; transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out;
} }
.left {
float: left;
margin: 0.75rem 1rem 1rem 0 !important;
}
.right {
float: right;
margin: 0.75rem 0 1rem 1rem !important;
}
/* --- Overriding --- */ /* --- Overriding --- */
/* magnific-popup */ /* magnific-popup */

View file

@ -90,6 +90,10 @@
font-style: normal; font-style: normal;
} }
%img-rounded {
border-radius: 10px;
}
%img-caption { %img-caption {
+ em { + em {
display: block; display: block;
@ -114,6 +118,11 @@
text-decoration: none; text-decoration: none;
} }
@mixin mt-mb($value) {
margin-top: $value;
margin-bottom: $value;
}
@mixin ml-mr($value) { @mixin ml-mr($value) {
margin-left: $value; margin-left: $value;
margin-right: $value; margin-right: $value;

View file

@ -26,6 +26,14 @@
--label-color: rgb(108, 117, 125); --label-color: rgb(108, 117, 125);
--checkbox-color: rgb(118, 120, 121); --checkbox-color: rgb(118, 120, 121);
--checkbox-checked-color: var(--link-color); --checkbox-checked-color: var(--link-color);
--img-bg: radial-gradient(circle, rgb(22, 22, 24) 0%, rgb(32, 32, 32) 100%);
--shimmer-bg:
linear-gradient(
90deg,
rgba(255, 255, 255, 0) 0%,
rgba(58, 55, 55, 0.4) 50%,
rgba(255, 255, 255, 0) 100%
);
/* Sidebar */ /* Sidebar */
--sidebar-bg: radial-gradient(circle, #242424 0%, #1d1f27 100%); --sidebar-bg: radial-gradient(circle, #242424 0%, #1d1f27 100%);
@ -65,7 +73,6 @@
--card-bg: rgb(39, 40, 43); --card-bg: rgb(39, 40, 43);
--card-border-color: rgb(53, 53, 60); --card-border-color: rgb(53, 53, 60);
--card-box-shadow: var(--main-bg); --card-box-shadow: var(--main-bg);
--preview-img-bg: radial-gradient(circle, rgb(22, 22, 24) 0%, rgb(32, 32, 32) 100%);
--kbd-wrap-color: #6a6a6a; --kbd-wrap-color: #6a6a6a;
--kbd-text-color: #d3d3d3; --kbd-text-color: #d3d3d3;
--kbd-bg-color: #242424; --kbd-bg-color: #242424;

View file

@ -24,6 +24,14 @@
--btn-box-shadow: #eaeaea; --btn-box-shadow: #eaeaea;
--checkbox-color: #c5c5c5; --checkbox-color: #c5c5c5;
--checkbox-checked-color: #07a8f7; --checkbox-checked-color: #07a8f7;
--img-bg: radial-gradient(circle, rgb(255, 255, 255) 0%, rgb(249, 249, 249) 100%);
--shimmer-bg:
linear-gradient(
90deg,
rgba(250, 250, 250, 0) 0%,
rgba(232, 230, 230, 1) 50%,
rgba(250, 250, 250, 0) 100%
);
/* Sidebar */ /* Sidebar */
--sidebar-bg: #eeeeee; --sidebar-bg: #eeeeee;
@ -64,7 +72,6 @@
--tb-odd-bg: #fbfcfd; --tb-odd-bg: #fbfcfd;
--tb-border-color: #eaeaea; --tb-border-color: #eaeaea;
--dash-color: silver; --dash-color: silver;
--preview-img-bg: radial-gradient(circle, rgb(255, 255, 255) 0%, rgb(249, 249, 249) 100%);
--kbd-wrap-color: #bdbdbd; --kbd-wrap-color: #bdbdbd;
--kbd-text-color: var(--text-color); --kbd-text-color: var(--text-color);
--kbd-bg-color: white; --kbd-bg-color: white;

View file

@ -26,12 +26,23 @@
color: var(--text-color); color: var(--text-color);
} }
img.preview-img { %preview-margin {
margin: 0; margin: 0;
border-radius: 6px; }
.preview-img {
@include align-center;
@extend %preview-margin;
&:not(.no-bg) { &:not(.no-bg) {
background: var(--preview-img-bg); img.lazyloaded {
background: var(--img-bg);
}
}
img {
@extend %preview-margin;
@extend %img-rounded;
} }
} }

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long