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:
parent
2922452f9d
commit
74cad48f28
201
assets/script.js
201
assets/script.js
@ -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());
|
||||
})();
|
||||
|
113
assets/style.css
113
assets/style.css
@ -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
235
assets/style.less
Normal 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;
|
||||
}
|
||||
}
|
@ -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')
|
||||
|
Loading…
x
Reference in New Issue
Block a user