给博客主题切换加个动画
这个功能其实想搞很久了,之前在某个主题上看到过一个圆形扩散的过渡动画。
当时自己尝试着结合AI实现了一下,虽然大致效果实现了,但是整个流畅度很差,而且存在很多BUG。
前些日子看到大佬发布的 Retypeset 主题上有一个自上而下的过渡动画。流畅度,美观度都非常不错,而且这个主题也是基于Astro实现的,我要抄的话也简单一些 😄。
0:00
/0:07
效果
整个效果实现依旧是基于 Chrome 的 View-Transition 这个特性实现的。如果你的博客有主题切换功能,且是MPA网页,就能通过添加一些简单的脚本和CSS实现这个效果。(SPA能不能用我没测试,根据文档是可以的,但是效果应该不好?)
我粗略理解的整个实现原理大概为:
在切换主题的时候页面会有一个new
版本和一个old
版本。
页面最初时old
会版本覆盖在new
版本之上,在执行完主题设置操作后,让old
版本的高度逐渐缩小,缩小后自然就会把下方的new
显示出来。
这个过程就是我们看到的流畅切换动画。
首先添加一个页面切换的动画CSS,animation-theme-toggle
为切换动画的名称。
/*** 这里的 view-transition-new 和 view-transition-old ***/
::view-transition-new(animation-theme-toggle) {
animation: reveal 0.5s cubic-bezier(0.4, 0, 0.2, 1);
clip-path: inset(0 0 0 0);
z-index: 99;
}
::view-transition-old(animation-theme-toggle) {
animation: none;
z-index: -1;
}
@keyframes reveal {
0% {
clip-path: inset(var(--from));
}
}
接着在主题切换的js实现做一些修改,让主题切换按钮调用此处定义的changeTheme即可,我这里是点击按钮时传入需要切换的主题名称,可以根据自己的主题实现来。
// theme为需要切换的主题名称
const changeTheme = (theme) => {
// 兼容性支持
if (document.documentElement.classList.contains('reduce-motion')) {
updateTheme();
return;
}
// 因为我的侧边栏是没有启用动画效果的
// 但是主题切换时需要启用动画效果,所以这里把原来的设置清空。
const menuContent = document.querySelector('.book-menu-content[data-astro-transition-scope]');
const menuanmi = document.querySelector('.book-menu-content[data-astro-transition-scope]').dataset.astroTransitionScope;
// 临时移除侧边栏的transition:animate="none"属性
if (menuContent) {
menuContent.dataset.astroTransitionScope = '';
}
// 给html设置我们需要的使用的动画类
document.documentElement.style.setProperty('view-transition-name', 'animation-theme-toggle');
document.documentElement.setAttribute('data-theme-changing', '');
// 开始执行动画并且修改主题
const themeTransition = document.startViewTransition(() => {
// 存储主题设置&修改页面主题
localStorage.setItem('name', theme.desc);
localStorage.setItem('theme', theme.name);
localStorage.setItem('themetype', theme.type);
document.documentElement.setAttribute('class', theme.name);
setTheme(theme.desc);
// 切换主题时「自上而下」的效果和「自下而上」的效果轮换着来。
const root = document.documentElement;
const currentFrom = getComputedStyle(root).getPropertyValue('--from').trim();
if (currentFrom === '100% 0 0 0') {
root.style.setProperty('--from', '0 0 100% 0');
} else {
root.style.setProperty('--from', '100% 0 0 0');
}
document.dispatchEvent(new Event('theme-changed'));
});
// 动画执行完毕后执行清理操作
themeTransition.finished.then(() => {
document.documentElement.style.removeProperty('view-transition-name');
document.documentElement.removeAttribute('data-theme-changing');
// 关闭主题列表
document.getElementById('theme').checked = false;
// 恢复侧边栏的transition:animate="none"属性
if (menuContent) {
menuContent.dataset.astroTransitionScope = menuanmi;
}
});
};
看起来可能有点复杂,其实在 const themeTransition = document.startViewTransition(() => {
后面加入你的主题切换函数应该就可以了。
这个函数里面前面5行都是我的主题切换操作,替换这部分,保留其他的就可以了。
想折腾的小伙伴可以试试。
加入评论