1
0
mirror of https://github.com/journey-ad/Moe-Counter.git synced 2025-01-31 20:15:24 +01:00

refactor(web): Improve performance

This commit is contained in:
roffy3051 2024-10-23 03:59:07 +08:00
parent 2922452f9d
commit 74cad48f28
4 changed files with 347 additions and 233 deletions

View File

@ -3,117 +3,114 @@
const img = document.getElementById('result');
const code = document.getElementById('code');
btn.addEventListener('click', throttle(() => {
const $name = document.getElementById('name'),
$theme = document.getElementById('theme'),
$padding = document.getElementById('padding'),
$offset = document.getElementById('offset'),
$scale = document.getElementById('scale'),
$pixelated = document.getElementById('pixelated'),
$darkmode = document.getElementById('darkmode')
const elements = {
name: document.getElementById('name'),
theme: document.getElementById('theme'),
padding: document.getElementById('padding'),
offset: document.getElementById('offset'),
scale: document.getElementById('scale'),
pixelated: document.getElementById('pixelated'),
darkmode: document.getElementById('darkmode'),
};
const name = $name.value.trim();
if (!name) {
alert('Please input counter name.');
return;
}
party.confetti(btn, { count: party.variation.range(20, 40) });
const params = {
name,
theme: $theme.value || 'moebooru',
padding: $padding.value || '7',
offset: $offset.value || '0',
scale: $scale.value || '1',
pixelated: $pixelated.checked ? '1' : '0',
darkmode: $darkmode.value || 'auto',
}
const query = new URLSearchParams(params).toString();
const imgSrc = `${__global_data.site}/@${name}?${query}`;
img.src = imgSrc
img.onload = () => {
img.scrollIntoView({ block: 'start', behavior: 'smooth' });
code.textContent = imgSrc;
code.style.visibility = 'visible';
}
img.onerror = () => {
fetch(imgSrc)
.then(async (res) => {
if (!res.ok) {
const { message } = await res.json();
alert(message);
}
})
};
}, 500));
code.addEventListener('click', (e) => {
e.preventDefault()
e.stopPropagation()
const target = e.target;
if (document.body.createTextRange) {
const range = document.body.createTextRange();
range.moveToElementText(target);
range.select();
} else if (window.getSelection) {
const selection = window.getSelection();
const range = document.createRange();
range.selectNodeContents(target);
selection.removeAllRanges();
selection.addRange(range);
}
})
btn.addEventListener('click', throttle(handleButtonClick, 500));
code.addEventListener('click', selectCodeText);
const mainTitle = document.querySelector('#main_title i');
const themes = document.querySelector('#themes');
const moreTheme = document.querySelector('#more_theme');
mainTitle.addEventListener('click', throttle(() => {
party.sparkles(document.documentElement, { count: party.variation.range(40, 100) });
}, 1000));
mainTitle.addEventListener('click', throttle(() => party.sparkles(document.documentElement, { count: party.variation.range(40, 100) }), 1000));
moreTheme.addEventListener('click', scrollToThemes);
moreTheme.addEventListener('click', () => {
function handleButtonClick() {
const { name, theme, padding, offset, scale, pixelated, darkmode } = elements;
const nameValue = name.value.trim();
if (!nameValue) {
alert('Please input counter name.');
return;
}
const params = {
name: nameValue,
theme: theme.value || 'moebooru',
padding: padding.value || '7',
offset: offset.value || '0',
scale: scale.value || '1',
pixelated: pixelated.checked ? '1' : '0',
darkmode: darkmode.value || 'auto'
};
const query = new URLSearchParams(params).toString();
const imgSrc = `${__global_data.site}/@${nameValue}?${query}`;
img.src = `${imgSrc}&_=${Math.random()}`;
btn.setAttribute('disabled', '');
img.onload = () => {
img.scrollIntoView({ block: 'start', behavior: 'smooth' });
code.textContent = imgSrc;
code.style.visibility = 'visible';
party.confetti(btn, { count: party.variation.range(20, 40) });
btn.removeAttribute('disabled');
};
img.onerror = async () => {
try {
const res = await fetch(imgSrc);
if (!res.ok) {
const { message } = await res.json();
alert(message);
}
} finally {
btn.removeAttribute('disabled');
}
};
}
function selectCodeText(e) {
e.preventDefault();
e.stopPropagation();
const target = e.target;
const range = document.createRange();
const selection = window.getSelection();
range.selectNodeContents(target);
selection.removeAllRanges();
selection.addRange(range);
}
function scrollToThemes() {
if (!themes.hasAttribute('open')) {
party.sparkles(moreTheme.querySelector('h3'), { count: party.variation.range(20, 40) });
themes.scrollIntoView({ block: 'start', behavior: 'smooth' });
}
});
}
function throttle(fn, threshold = 250) {
let last;
let deferTimer;
let last, deferTimer;
return function (...args) {
const context = this;
const now = Date.now();
if (last && now < last + threshold) {
clearTimeout(deferTimer);
deferTimer = setTimeout(() => {
last = now;
fn.apply(context, args);
fn.apply(this, args);
}, threshold);
} else {
last = now;
fn.apply(context, args);
fn.apply(this, args);
}
};
}
})();
// Lazy Load
(() => {
function lazyLoad(options = {}) {
const {
selector = 'img[data-src]:not([src])',
loading = '',
failed = '',
rootMargin = '200px',
threshold = 0.01
} = options;
const { selector = 'img[data-src]:not([src])', loading = '', failed = '', rootMargin = '200px', threshold = 0.01 } = options;
const images = document.querySelectorAll(selector);
@ -123,17 +120,9 @@
const img = entry.target;
observer.unobserve(img);
if (failed) {
const handleError = () => {
img.onerror = null;
img.src = failed;
img.setAttribute('data-failed', '');
};
img.onerror = handleError;
}
img.removeAttribute('data-loading');
img.onerror = failed ? () => { img.src = failed; img.setAttribute('data-failed', ''); } : null;
img.src = img.getAttribute('data-src');
img.removeAttribute('data-loading');
}
});
}, { rootMargin, threshold });
@ -154,32 +143,27 @@
rootMargin: '200px',
threshold: 0.01
};
if (document.readyState === 'loading') {
document.addEventListener("DOMContentLoaded", () => lazyLoad(lazyLoadOptions));
} else {
lazyLoad(lazyLoadOptions);
}
document.readyState === 'loading'
? document.addEventListener("DOMContentLoaded", () => lazyLoad(lazyLoadOptions))
: lazyLoad(lazyLoadOptions);
})();
// back to top
// Back to top
(() => {
let isShow = false;
let lock = false;
let isShow = false, lock = false;
const btn = document.querySelector('.back-to-top');
const handleScroll = () => {
if (lock) return;
if (document.body.scrollTop >= 1000) {
if (!isShow) {
btn.classList.add('load');
isShow = true;
}
} else {
if (isShow) {
btn.classList.remove('load');
isShow = false;
}
} else if (isShow) {
btn.classList.remove('load');
isShow = false;
}
};
@ -206,3 +190,8 @@
window.addEventListener('scroll', handleScroll);
btn.addEventListener('click', handleClick);
})();
// Prevent gesture
(() => {
document.addEventListener('gesturestart', e => e.preventDefault());
})();

View File

@ -1,113 +0,0 @@
html {
scroll-padding: 50px 0;
}
img[data-loading],
img[data-failed] {
width: 40px;
}
@media screen and (min-width: 800px) {
body {
max-width: min(90%, 800px);
}
}
details>summary {
list-style: none
}
details>summary::-webkit-details-marker,details>summary::marker {
display: none
}
summary:before {
border-bottom: 6px solid transparent;
border-left: 10px solid var(--b-txt);
border-top: 6px solid transparent;
content: "";
display: inline-block;
height: 0;
margin-right: 10px;
position: relative;
transition: .2s;
width: 0
}
details[open] summary:before {
transform: rotate(90deg)
}
h2, h3, h4, h5 {
margin-top: 1.5em;
margin-bottom: .6em;
}
.back-to-top {
position: fixed;
z-index: 2;
right: -108px;
bottom: 0;
width: 108px;
height: 150px;
background: url('./img/back-to-top.png?v=1') no-repeat 0 0; /* artwork from https://www.pixiv.net/artworks/83996495 */
background-size: 108px 450px;
opacity: 0.6;
transition: opacity 0.3s, right 0.8s;
cursor: pointer;
}
.back-to-top:hover {
background-position: 0 -150px;
opacity: 1;
}
.back-to-top.load {
right: 0;
}
.back-to-top.ani-leave {
background-position: 0 -150px;
animation: ani-leave 390ms ease-in-out;
animation-fill-mode: forwards;
}
.back-to-top.leaved {
pointer-events: none;
background: none;
transition: none;
}
.back-to-top.ending {
pointer-events: none;
}
.back-to-top.ending::after {
opacity: 1;
transition-delay: 0.35s;
}
.back-to-top::after {
content: '';
position: fixed;
z-index: 2;
right: 0;
bottom: 0;
width: 108px;
height: 150px;
background: url('./img/back-to-top.png?v=1') no-repeat 0 0;
background-size: 108px 450px;
background-position: 0 -300px;
transition: opacity 0.3s;
opacity: 0;
pointer-events: none;
}
@keyframes ani-leave {
0% {
transform: translateX(0);
}
100% {
transform: translateX(108px);
}
}
@media screen and (max-width: 900px) {
iframe {
display: none;
}
}

235
assets/style.less Normal file
View File

@ -0,0 +1,235 @@
html {
scroll-padding: 50px 0;
}
#main_title {
margin-top: 0.5em;
}
#themes {
margin-top: 2em;
& > p {
margin: 0;
}
}
#more_theme h3 {
display: inline-block;
margin: 0;
cursor: pointer;
}
#get {
margin-bottom: 1em;
}
#code {
visibility: hidden;
display: inline-block;
margin-bottom: 1em;
}
#result {
display: block;
}
.github {
margin-top: 2em;
}
code {
word-break: break-all;
}
input[type="checkbox"][role="switch"] {
height: 0;
width: 0;
visibility: hidden;
& + label {
cursor: pointer;
width: 3.6em;
height: 1.8em;
margin: 0;
background: grey;
display: block;
border-radius: 1.8em;
position: relative;
transition: 0.3s;
&:after {
content: "";
position: absolute;
top: 0.1em;
left: 0.1em;
width: 1.6em;
height: 1.6em;
background: #fff;
border-radius: 1.6em;
transition: 0.3s;
}
span {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 12.5%;
font-size: 0.54em;
&::before,
&::after {
display: block;
color: #fff;
font-weight: bold;
box-sizing: border-box;
}
&::before {
content: "ON";
}
&::after {
content: "OFF";
color: #ccc;
}
}
}
&:checked + label {
background: var(--b-btn-bg);
&:after {
left: calc(100% - 0.1em);
transform: translateX(-100%);
}
&:active:after {
width: 2.34em;
}
}
}
img[data-loading],
img[data-failed] {
width: 40px;
}
details > summary {
list-style: none;
&::-webkit-details-marker,
&::marker {
display: none;
}
}
summary::before {
border-bottom: 6px solid transparent;
border-left: 10px solid var(--b-txt);
border-top: 6px solid transparent;
content: "";
display: inline-block;
height: 0;
margin-right: 10px;
position: relative;
transition: 0.2s;
width: 0;
}
details[open] summary::before {
transform: rotate(90deg);
}
h2,
h3,
h4,
h5 {
margin: 1.5em 0 0.6em;
}
.back-to-top {
position: fixed;
z-index: 2;
right: -108px;
bottom: 0;
width: 108px;
height: 150px;
background: url("./img/back-to-top.png?v=1") no-repeat 0 0;
background-size: 108px 450px;
opacity: 0.6;
transition: opacity 0.3s, right 0.8s;
cursor: pointer;
&:hover {
background-position: 0 -150px;
opacity: 1;
}
&::after {
content: "";
position: fixed;
z-index: 2;
right: 0;
bottom: 0;
width: 108px;
height: 150px;
background: url("./img/back-to-top.png?v=1") no-repeat 0 0;
background-size: 108px 450px;
background-position: 0 -300px;
transition: opacity 0.3s;
opacity: 0;
pointer-events: none;
}
&.load {
right: 0;
}
&.ani-leave {
background-position: 0 -150px;
animation: ani-leave 390ms ease-in-out forwards;
@keyframes ani-leave {
0% {
transform: translateX(0);
}
100% {
transform: translateX(108px);
}
}
}
&.leaved,
&.ending {
pointer-events: none;
}
&.leaved {
background: none;
transition: none;
}
&.ending::after {
opacity: 1;
transition-delay: 0.35s;
}
}
@media screen and (min-width: 800px) {
body {
max-width: ~"min(90%, 800px)";
}
}
@media screen and (max-width: 900px) {
iframe {
display: none;
}
}

View File

@ -1,11 +1,12 @@
html
head
title='Moe Counter!'
meta(name='viewport', content='width=device-width, initial-scale=1')
meta(name='viewport', content='width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no')
link(rel='icon', type='image/png', href='favicon.png')
link(rel='stylesheet', href='https://unpkg.com/normalize.css')
link(rel='stylesheet', href='https://unpkg.com/bamboo.css')
link(rel='stylesheet', href='style.css')
link(rel='stylesheet', href='https://cdn.jsdelivr.net/npm/normalize.css')
link(rel='stylesheet', href='https://cdn.jsdelivr.net/npm/bamboo.css')
link(rel='stylesheet/less', href='style.less')
script(less, src='https://cdn.jsdelivr.net/npm/less')
<!-- Global site tag (gtag.js) - Google Analytics -->
script(async, src='https://www.googletagmanager.com/gtag/js?id=G-2RLWN5JXRL')
script.
@ -26,7 +27,7 @@ html
var __global_data = { site: "#{site}" };
body
h1#main_title(style='margin-top: 0.5em;')
h1#main_title
i Moe Counter!
h3 How to use
@ -46,10 +47,10 @@ html
h5 e.g.
img(src=`${site}/@index` alt="Moe Counter!")
details#themes(style='margin-top: 2em;')
details#themes
summary#more_theme(onclick='_evt_push("click", "normal", "more_theme")')
h3(style='display: inline-block; margin: 0; cursor: pointer;') More theme✨
p(style='margin: 0;') Just use the query parameters <code>theme</code>, like this: <code>#{site}/@:name?theme=moebooru</code>
h3 More theme✨
p Just use the query parameters <code>theme</code>, like this: <code>#{site}/@:name?theme=moebooru</code>
each theme in Object.keys(themeList)
div.item(data-theme=theme)
h5 #{theme}
@ -107,7 +108,9 @@ html
td: code pixelated
td Enable pixelated mode, Enum 0/1, default is
code 1
td: input#pixelated(type='checkbox', checked, style='margin: .5rem .75rem;')
td
input#pixelated(type='checkbox', role='switch', checked)
label(for='pixelated'): span
tr
td: code darkmode
td Enable dark mode, Enum 0/1/auto, default is
@ -117,16 +120,16 @@ html
option(value="1") yes
option(value="0") no
button#get(style='margin-bottom: 1em;', onclick='_evt_push("click", "normal", "get_counter")') Generate
button#get(onclick='_evt_push("click", "normal", "get_counter")') Generate
div
code#code(style='visibility: hidden; display: inline-block; margin-bottom: 1em;')
img#result(style='display: block;')
code#code
img#result
p(style='margin-top: 2em;')
p.github
a(href='https://github.com/journey-ad/Moe-Counter', target='_blank', onclick='_evt_push("click", "normal", "go_github")') source code
div.back-to-top
script(async, src='https://unpkg.com/party-js@2/bundle/party.min.js')
script(async, src='https://cdn.jsdelivr.net/npm/party-js@2/bundle/party.min.js')
script(async, src='script.js')