File size: 4,659 Bytes
96dd062 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 | <script lang="ts">
import { onMount } from "svelte";
import { siteConfig } from "@/config";
export let currentLayout: "list" | "grid" = "list";
/**
* 文章列表布局切换按钮
* 目前已弃用,已集成至DisplaySettingsIntegrated.svelte,当前文件保留以备将来可能的单独使用
*/
let mounted = false;
let isSmallScreen = false;
let isSwitching = false;
function checkScreenSize() {
isSmallScreen = window.innerWidth < 1200;
if (isSmallScreen) {
currentLayout = "list";
}
}
onMount(() => {
mounted = true;
checkScreenSize();
// 从localStorage读取用户偏好,如果没有则使用传入的默认值
const savedLayout = localStorage.getItem("postListLayout");
if (savedLayout && (savedLayout === "list" || savedLayout === "grid")) {
currentLayout = savedLayout;
} else {
// 如果没有保存的偏好,使用传入的默认布局(从props)
// currentLayout已经在声明时设置了默认值
}
// 监听窗口大小变化
window.addEventListener("resize", checkScreenSize);
return () => {
window.removeEventListener("resize", checkScreenSize);
};
});
function switchLayout() {
if (!mounted || isSmallScreen || isSwitching) return;
isSwitching = true;
currentLayout = currentLayout === "list" ? "grid" : "list";
localStorage.setItem("postListLayout", currentLayout);
// 触发自定义事件,通知父组件布局已改变
const event = new CustomEvent("layoutChange", {
detail: { layout: currentLayout },
});
window.dispatchEvent(event);
// 动画完成后重置状态
setTimeout(() => {
isSwitching = false;
}, 500);
}
// 监听布局变化事件
onMount(() => {
const handleCustomEvent = (event: Event) => {
const customEvent = event as CustomEvent<{ layout: "list" | "grid" }>;
currentLayout = customEvent.detail.layout;
};
window.addEventListener("layoutChange", handleCustomEvent);
return () => {
window.removeEventListener("layoutChange", handleCustomEvent);
};
});
// 监听PostPage的布局初始化事件
onMount(() => {
const handleLayoutInit = () => {
// 从PostPage获取当前布局状态
const postListContainer = document.getElementById("post-list-container");
if (postListContainer) {
const isGridMode = postListContainer.classList.contains("grid-mode");
currentLayout = isGridMode ? "grid" : "list";
}
};
// 延迟执行,确保PostPage已经初始化
setTimeout(handleLayoutInit, 100);
return () => {
// 清理函数
};
});
</script>
{#if mounted && siteConfig.postListLayout.allowSwitch && !isSmallScreen}
<button
aria-label="切换文章列表布局"
class="btn-plain scale-animation rounded-lg h-11 w-11 active:scale-90 flex items-center justify-center theme-switch-btn {isSwitching ? 'switching' : ''}"
on:click={switchLayout}
disabled={isSwitching}
title={currentLayout === 'list' ? '切换到网格模式' : '切换到列表模式'}
>
{#if currentLayout === 'list'}
<!-- 列表图标 -->
<svg class="w-5 h-5 icon-transition" fill="currentColor" viewBox="0 0 24 24">
<path d="M4 6h16v2H4zm0 5h16v2H4zm0 5h16v2H4z"/>
</svg>
{:else}
<!-- 网格图标 -->
<svg class="w-5 h-5 icon-transition" fill="currentColor" viewBox="0 0 24 24">
<path d="M3 3h7v7H3V3zm0 11h7v7H3v-7zm11-11h7v7h-7V3zm0 11h7v7h-7v-7z"/>
</svg>
{/if}
</button>
{/if}
<style>
/* 确保主题切换按钮的背景色即时更新 */
.theme-switch-btn::before {
transition: transform 75ms ease-out, background-color 0ms !important;
}
/* 图标过渡动画 */
.icon-transition {
transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.3s ease;
}
/* 切换中的按钮动画 */
.switching {
pointer-events: none;
}
.switching .icon-transition {
animation: iconRotate 0.5s cubic-bezier(0.4, 0, 0.2, 1);
}
@keyframes iconRotate {
0% {
transform: rotate(0deg) scale(1);
opacity: 1;
}
50% {
transform: rotate(180deg) scale(0.8);
opacity: 0.5;
}
100% {
transform: rotate(360deg) scale(1);
opacity: 1;
}
}
/* 悬停效果增强 */
.theme-switch-btn:not(.switching):hover .icon-transition {
transform: scale(1.1);
}
/* 按钮禁用状态 */
.theme-switch-btn:disabled {
cursor: not-allowed;
opacity: 0.7;
}
</style>
|