修复动画掉帧的问题,增加昼夜切换的过渡动画

This commit is contained in:
zqlit committed 2026-05-24 14:00:12 +08:00
1 parent c1658c0cc6
commit 0135299e55
7 files changed
+246 -131

No files matched your search

+99
View File
@@ -41,6 +41,32 @@
--card-shadow-dark: 0 18px 45px rgba(0, 0, 0, 0.9), 0 0 0 1px rgba(148, 163, 184, 0.45);
}
/* ── Theme switch transition ── */
::view-transition-old(root),
::view-transition-new(root) {
animation-duration: 0.45s;
animation-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
}
::view-transition-old(root) {
animation-name: theme-fade-out;
}
::view-transition-new(root) {
animation-name: theme-fade-in;
animation-delay: 0.04s;
}
@keyframes theme-fade-out {
from { opacity: 1; }
to { opacity: 0; }
}
@keyframes theme-fade-in {
from { opacity: 0; }
to { opacity: 1; }
}
[data-theme="dark"] body {
color: var(--text-content-dark-color);
background-color: var(--background-dark-color);
@@ -3736,6 +3762,79 @@ input[type=submit] {
}
}
/* ── Toast ── */
.ying-toast-container {
position: fixed;
top: 18px;
left: 50%;
transform: translateX(-50%);
z-index: 10000;
display: flex;
flex-direction: column;
align-items: center;
gap: 8px;
pointer-events: none;
}
.ying-toast {
padding: 9px 18px;
border-radius: 6px;
font-size: 13px;
font-weight: 500;
line-height: 1.3;
opacity: 0;
transform: translateY(-10px);
transition: opacity 0.28s ease, transform 0.28s ease;
pointer-events: auto;
-webkit-backdrop-filter: blur(20px) saturate(180%);
backdrop-filter: blur(20px) saturate(180%);
will-change: transform, opacity;
}
.ying-toast--visible {
opacity: 1;
transform: translateY(0);
}
.ying-toast--success {
background: rgba(34, 197, 94, 0.88);
color: #fff;
box-shadow: 0 8px 32px rgba(34, 197, 94, 0.18);
}
.ying-toast--error {
background: rgba(239, 68, 68, 0.88);
color: #fff;
box-shadow: 0 8px 32px rgba(239, 68, 68, 0.18);
}
.ying-toast--warning {
background: rgba(245, 158, 11, 0.88);
color: #fff;
box-shadow: 0 8px 32px rgba(245, 158, 11, 0.18);
}
.ying-toast--info {
background: rgba(29, 61, 108, 0.85);
color: #f0f4fb;
box-shadow: 0 8px 32px rgba(29, 61, 108, 0.18);
}
[data-theme="dark"] .ying-toast--info {
background: rgba(159, 196, 244, 0.22);
color: #f3f7fd;
-webkit-backdrop-filter: blur(24px) saturate(180%);
backdrop-filter: blur(24px) saturate(180%);
}
@media screen and (max-width: 480px) {
.ying-toast {
padding: 8px 14px;
font-size: 12px;
border-radius: 5px;
}
}
.floating-tools a {
width: 40px;
height: 40px;
@@ -63,17 +63,21 @@ window.initInfiniteScroll = function() {
// 1. Get new posts
const newPosts = doc.querySelectorAll(`${containerSelector} ${itemSelector}`);
// 2. Append posts
// 2. Append posts with batch layout read/write to avoid thrashing
if (newPosts.length > 0) {
newPosts.forEach(post => {
// Remove UIkit scrollspy attributes to prevent interference and use manual animation
// Phase 1: append all and set initial opacity
newPosts.forEach(function(post) {
post.removeAttribute('uk-scrollspy-class');
post.style.opacity = 0;
container.appendChild(post);
// Trigger reflow
void post.offsetWidth;
});
// Phase 2: force single layout read for all new posts
var postArr = Array.prototype.slice.call(newPosts);
postArr.forEach(function(post) { void post.offsetWidth; });
// Phase 3: batch animate in
postArr.forEach(function(post) {
post.style.transition = 'opacity 0.5s ease';
post.style.opacity = 1;
});
+22 -10
View File
@@ -5,6 +5,16 @@ window.initSearch = function() {
var searchLoadingPromise = null;
function debounce(fn, delay) {
var timer;
return function() {
var context = this;
var args = arguments;
clearTimeout(timer);
timer = setTimeout(function() { fn.apply(context, args); }, delay);
};
}
function loadSearchIndex() {
if (window.searchIndex) {
return Promise.resolve(window.searchIndex);
@@ -91,7 +101,7 @@ function initSearchModal() {
}
});
searchInput.addEventListener('input', function() {
searchInput.addEventListener('input', debounce(function() {
var query = this.value.trim().toLowerCase();
if (query.length === 0) {
@@ -135,7 +145,7 @@ function initSearchModal() {
} else {
searchResults.innerHTML = '<p class="search-tip">未找到相关文章</p>';
}
});
}), 200);
}
}
}
@@ -224,7 +234,7 @@ function initInlineSearch() {
});
}
input.addEventListener('input', function() {
input.addEventListener('input', debounce(function() {
var query = this.value.trim().toLowerCase();
var inputGroup = this.closest('.search-input-group');
@@ -285,19 +295,21 @@ function initInlineSearch() {
resultsContainer.innerHTML = '<div class="search-result-item no-result">暂无相关内容</div>';
resultsContainer.classList.add('show');
}
});
}), 200);
// Close on click outside
if (!window.inlineSearchClickOutside) {
window.inlineSearchClickOutside = function(e) {
var header = document.getElementById('essay-header');
var searchContainer = document.getElementById('search-container');
if (!searchContainer || searchContainer.style.display !== 'block') return;
var header = document.getElementById('essay-header');
var resultsContainer = document.getElementById('inline-search-results');
if (header && searchContainer && resultsContainer && searchContainer.style.display === 'block') {
if (!searchContainer.contains(e.target) && !header.contains(e.target) && !resultsContainer.contains(e.target)) {
window.toggleSearchMode(false);
}
if (header && resultsContainer &&
!searchContainer.contains(e.target) &&
!header.contains(e.target) &&
!resultsContainer.contains(e.target)) {
window.toggleSearchMode(false);
}
};
document.addEventListener('click', window.inlineSearchClickOutside);
+23 -34
View File
@@ -1,35 +1,24 @@
function checkParent(element, classNames) {
while (element) {
if (element.classList && classNames.some(cn => element.classList.contains(cn))) {
return true;
}
element = element.parentElement;
}
return false;
}
var excludedClasses = ['talks_comments','tiaozhuan-button', 'floating-tools']; // 添加需要排除的a标签类名class
window.addEventListener('load', (event) => {
document.body.addEventListener('click', function(e) {
let target = e.target;
while (target && target.nodeName !== 'A') {
target = target.parentNode;
}
if (target && target.nodeName === 'A' &&
!checkParent(target, excludedClasses) &&
!target.href.startsWith('javascript:') &&
!target.href.includes('usj.cc') &&
!target.href.includes('douban.com') &&
!target.href.includes('bilibili.com') &&
!target.href.includes('github.com') &&
!target.href.includes('zql.im') &&
!target.href.includes('5b2.cn') &&
!target.href.includes('https://beian.miit.gov.cn') &&
!target.href.includes('https://www.upyun.com') &&
target.hostname !== window.location.hostname) {
var excludedClasses = ['talks_comments','tiaozhuan-button', 'floating-tools'];
window.addEventListener('load', function() {
document.body.addEventListener('click', function(e) {
var target = e.target.closest('a');
if (!target) return;
if (target.href.startsWith('javascript:') ||
target.hostname === window.location.hostname ||
target.href.includes('usj.cc') ||
target.href.includes('douban.com') ||
target.href.includes('bilibili.com') ||
target.href.includes('github.com') ||
target.href.includes('zql.im') ||
target.href.includes('5b2.cn') ||
target.href.includes('beian.miit.gov.cn') ||
target.href.includes('upyun.com')) return;
if (excludedClasses.some(function(cn) { return target.classList.contains(cn); })) return;
e.preventDefault();
let encodedUrl = btoa(target.href); // Base64 encode the URL
let url = '/tiaozhuan.html?target=' + encodedUrl;
window.open(url, '_blank');
}
});
});
var encodedUrl = btoa(target.href);
window.open('/tiaozhuan.html?target=' + encodedUrl, '_blank');
});
});
+63
View File
@@ -0,0 +1,63 @@
/**
* Toast — lightweight notification
* Usage: window.Toast.success('msg'), Toast.error('msg'), Toast.warning('msg'), Toast.info('msg')
* window.Toast.show('msg', { type: 'info', duration: 2500 })
*/
(function() {
var container = null;
var counter = 0;
function ensureContainer() {
if (container) return;
container = document.createElement('div');
container.className = 'ying-toast-container';
document.body.appendChild(container);
}
function remove(el, delay) {
setTimeout(function() {
el.classList.remove('ying-toast--visible');
el.addEventListener('transitionend', function onEnd(e) {
if (e.propertyName !== 'opacity') return;
el.removeEventListener('transitionend', onEnd);
if (el.parentNode) el.parentNode.removeChild(el);
});
}, delay);
}
window.Toast = {
show: function(text, options) {
options = options || {};
var type = options.type || 'info';
var duration = options.duration || 2200;
ensureContainer();
var el = document.createElement('div');
el.className = 'ying-toast ying-toast--' + type;
el.textContent = text;
el.setAttribute('role', 'status');
el.setAttribute('aria-live', 'polite');
container.appendChild(el);
/* Force layout then animate in */
void el.offsetWidth;
el.classList.add('ying-toast--visible');
remove(el, duration);
},
success: function(text, duration) {
this.show(text, { type: 'success', duration: duration });
},
error: function(text, duration) {
this.show(text, { type: 'error', duration: duration });
},
warning: function(text, duration) {
this.show(text, { type: 'warning', duration: duration });
},
info: function(text, duration) {
this.show(text, { type: 'info', duration: duration });
}
};
})();
+26 -77
View File
@@ -9,69 +9,13 @@ window.isMobile = function() {
// Toast Message Helper
window.showToast = function(text, type) {
type = type || 'info'; // default to info
if (typeof Qmsg !== 'undefined') {
// Map common types to Qmsg methods
if (typeof Qmsg[type] === 'function') {
Qmsg[type](text);
if (window.Toast) {
var t = type || 'info';
if (typeof window.Toast[t] === 'function') {
window.Toast[t](text);
} else {
Qmsg.info(text);
window.Toast.info(text);
}
} else {
// Fallback custom toast
let toast = document.querySelector('.custom-toast');
if (!toast) {
toast = document.createElement('div');
toast.className = 'custom-toast';
toast.style.cssText = `
position: fixed;
top: 20px;
left: 50%;
transform: translateX(-50%);
background: rgba(0,0,0,0.8);
color: white;
padding: 8px 16px;
border-radius: 4px;
z-index: 9999;
font-size: 14px;
opacity: 0;
transition: opacity 0.3s, background-color 0.3s;
pointer-events: none;
display: flex;
align-items: center;
gap: 8px;
`;
// Add dark mode support class
toast.setAttribute('data-toast', 'true');
document.body.appendChild(toast);
}
// Update toast style based on theme
const isDark = document.documentElement.getAttribute('data-theme') === 'dark';
if (isDark) {
toast.style.background = 'rgba(255, 255, 255, 0.9)';
toast.style.color = '#333';
toast.style.boxShadow = '0 4px 12px rgba(0,0,0,0.3)';
} else {
toast.style.background = 'rgba(0,0,0,0.8)';
toast.style.color = 'white';
toast.style.boxShadow = '0 4px 12px rgba(0,0,0,0.1)';
}
// Simple icon mapping for fallback
let icon = '';
if (type === 'success') icon = '✅ ';
else if (type === 'error') icon = '❌ ';
else if (type === 'warning') icon = '⚠️ ';
toast.innerText = icon + text;
toast.style.opacity = '1';
if (window.toastTimeout) clearTimeout(window.toastTimeout);
window.toastTimeout = setTimeout(() => {
toast.style.opacity = '0';
}, 2500);
}
};
@@ -85,25 +29,30 @@ window.initThemeToggle = function() {
avatar.parentNode.replaceChild(newAvatar, avatar);
newAvatar.addEventListener('click', function(e) {
// Prevent if clicking on something interactive inside (unlikely for avatar)
e.preventDefault();
const html = document.documentElement;
const currentTheme = html.getAttribute('data-theme');
const newTheme = currentTheme === 'dark' ? 'light' : 'dark';
html.setAttribute('data-theme', newTheme);
localStorage.setItem('theme', newTheme);
window.showToast('已切换至 ' + (newTheme === 'dark' ? '深色' : '浅色') + '模式', 'success');
// Sync Artalk Theme
if (window.artalk && typeof window.artalk.setDarkMode === 'function') {
window.artalk.setDarkMode(newTheme === 'dark');
var html = document.documentElement;
var currentTheme = html.getAttribute('data-theme');
var newTheme = currentTheme === 'dark' ? 'light' : 'dark';
var apply = function() {
html.setAttribute('data-theme', newTheme);
localStorage.setItem('theme', newTheme);
window.showToast('已切换至 ' + (newTheme === 'dark' ? '深色' : '浅色') + '模式', 'success');
if (window.artalk && typeof window.artalk.setDarkMode === 'function') {
window.artalk.setDarkMode(newTheme === 'dark');
}
window.dispatchEvent(new Event('resize'));
};
if (document.startViewTransition) {
document.startViewTransition(function() { apply(); });
} else {
apply();
}
// Trigger generic resize event to force charts/etc to redraw if needed
window.dispatchEvent(new Event('resize'));
});
};
+2 -3
View File
@@ -48,7 +48,6 @@
</div>
</div>
{{ $iconfont := resources.Get "js/libs/iconfont.js" }}
{{ $lightgallery := resources.Get "js/libs/lightgallery.umd.min.js" }}
{{ $uikit := resources.Get "js/libs/uikit.min.js" }}
{{ $easylazyload := resources.Get "js/modules/lazyload.js" }}
{{ $viewimage := resources.Get "js/libs/view-image.min.js" }}
@@ -56,8 +55,8 @@
{{ $main := resources.Get "js/main.js" }}
{{ $mypjax := resources.Get "js/modules/mypjax.js" }}
{{ $nprogress := resources.Get "js/libs/nprogress.js" }}
{{ $message := resources.Get "js/libs/message.min.js" }}
{{ $tiaozhuan := resources.Get "js/modules/tiaozhuan.js" }}
{{ $toast := resources.Get "js/modules/toast.js" }}
{{ $reward := resources.Get "js/modules/reward.js" }}
{{ $echarts := resources.Get "js/libs/echarts.min.js" }}
{{ $artalk := resources.Get "js/libs/Artalk.js" }}
@@ -73,7 +72,7 @@
{{ $artalkModule := resources.Get "js/modules/artalk.js" }}
{{ $pjaxLib := resources.Get "js/libs/pjax.js" }}
{{ $scripts := slice $iconfont $lightgallery $uikit $easylazyload $viewimage $barrage $utils $loader $cache $search $floatingTools $paragraphComments $infiniteScroll $artalkModule $nprogress $pjaxLib $main $mypjax $message $tiaozhuan $reward $echarts $artalk | resources.Concat "js/bundle.js" | resources.Minify | resources.Fingerprint }}
{{ $scripts := slice $iconfont $uikit $easylazyload $viewimage $barrage $utils $loader $cache $search $floatingTools $paragraphComments $infiniteScroll $artalkModule $nprogress $pjaxLib $main $mypjax $toast $tiaozhuan $reward $echarts $artalk | resources.Concat "js/bundle.js" | resources.Minify | resources.Fingerprint }}
<script defer src="{{ $scripts.RelPermalink }}"></script>
<script>