feat: 添加移动端检测功能,优化组件动画效果
All checks were successful
Push to Helm Chart Repository / build (push) Successful in 9s

This commit is contained in:
2025-09-11 00:03:03 +08:00
parent 29dc8bf27a
commit b3e8a5ef77
4 changed files with 29 additions and 40 deletions

View File

@ -15,6 +15,7 @@ import { listLabels } from "@/api/label";
import { POST_SORT_TYPE } from "@/localstore";
import { motion } from "framer-motion";
import { useDevice } from "@/hooks/use-device";
import { checkIsMobile } from "@/utils/client/device";
// 定义排序类型
type SortType = 'latest' | 'popular';
@ -23,7 +24,6 @@ export default function BlogHome() {
const [labels, setLabels] = useState<Label[]>([]);
const [posts, setPosts] = useState<Post[]>([]);
const [loading, setLoading] = useState(false);
const { isMobile } = useDevice();
const [sortType, setSortType, sortTypeLoaded] = useStoredState<SortType>(POST_SORT_TYPE, 'latest');
useEffect(() => {
if (!sortTypeLoaded) return;
@ -90,7 +90,7 @@ export default function BlogHome() {
{/* 主要内容区域 */}
<motion.div
className="lg:col-span-3 self-start"
initial={{ y: isMobile ? 30 : 60, opacity: 0 }}
initial={{ y: checkIsMobile() ? 30 : 60, opacity: 0 }}
animate={{ y: 0, opacity: 1 }}
transition={{ duration: config.animationDurationSecond, ease: "easeOut" }}>
{/* 文章列表标题 */}
@ -155,9 +155,9 @@ export default function BlogHome() {
{/* 侧边栏 */}
<motion.div
initial={isMobile ? { y: 30, opacity: 0 } : { x: 80, opacity: 0 }}
initial={checkIsMobile() ? { y: 30, opacity: 0 } : { x: 80, opacity: 0 }}
animate={{ x: 0, y: 0, opacity: 1 }}
transition={{ duration: config.animationDurationSecond , ease: "easeOut" }}
transition={{ duration: config.animationDurationSecond, ease: "easeOut" }}
>
<Sidebar
cards={[

View File

@ -73,11 +73,8 @@ async function PostHeader({ post }: { post: Post }) {
aria-hidden="true"
style={{ zIndex: -1 }}
/>
<motion.div
initial={{ opacity: 0, x: -50 }}
animate={{ opacity: 1, x: 0 }}
transition={{ duration: config.animationDurationSecond, ease: "easeOut" }}
className="container mx-auto px-4"
<div
className={`container mx-auto px-4`}
>
{(post.labels || post.isOriginal) && (
<div className="flex flex-wrap gap-2 mb-4">
@ -98,7 +95,7 @@ async function PostHeader({ post }: { post: Post }) {
<div>
<PostMeta post={post} />
</div>
</motion.div>
</div>
</div>
);
@ -145,10 +142,23 @@ async function PostContent({ post }: { post: Post }) {
async function BlogPost({ post }: { post: Post }) {
return (
<div className="h-full">
<div className="h-full"
>
{/* <ScrollToTop /> */}
<PostHeader post={post} />
<PostContent post={post} />
<motion.div
initial={{ opacity: 0, y: -30 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: config.animationDurationSecond, ease: "easeOut" }}>
<PostHeader post={post} />
</motion.div>
<motion.div
initial={{ opacity: 0, y: 30 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: config.animationDurationSecond, ease: "easeOut" }}>
<PostContent post={post} />
</motion.div>
<CommentSection targetType={TargetType.Post} targetId={post.id} totalCount={post.commentCount} />
</div>
);

View File

@ -6,67 +6,43 @@ import "highlight.js/styles/github.css"; // 你可以换成喜欢的主题
import "highlight.js/styles/github-dark.css"; // 适用于暗黑模式
import "highlight.js/styles/github-dark-dimmed.css"; // 适用于暗黑模式
import CodeBlock from "@/components/common/markdown-codeblock";
import * as motion from "motion/react-client"
import config from "@/config";
import { Skeleton } from "@/components/ui/skeleton";
export function MotionDiv(props: React.ComponentPropsWithoutRef<"div">) {
return (
<motion.div
initial={{ opacity: 0.3, y: 50 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: config.animationDurationSecond, ease: "easeOut" }}
>{props.children}
</motion.div>
);
}
export const markdownComponents = {
h1: (props: React.ComponentPropsWithoutRef<"h1">) => (
<MotionDiv>
<h1
className="scroll-m-20 text-4xl font-extrabold tracking-tight text-balance mt-10 mb-6"
{...props}
/>
</MotionDiv>
),
h2: (props: React.ComponentPropsWithoutRef<"h2">) => (
<MotionDiv>
<h2
className="scroll-m-20 border-b pb-2 text-3xl font-semibold tracking-tight first:mt-0 mt-8 mb-4"
{...props}
/>
</MotionDiv>
),
h3: (props: React.ComponentPropsWithoutRef<"h3">) => (
<MotionDiv>
<h3
className="scroll-m-20 text-2xl font-semibold tracking-tight mt-6 mb-3"
{...props}
/>
</MotionDiv>
),
h4: (props: React.ComponentPropsWithoutRef<"h4">) => (
<MotionDiv>
<h4
className="scroll-m-20 text-xl font-semibold tracking-tight mt-5 mb-2"
{...props}
/></MotionDiv>
/>
),
p: (props: React.ComponentPropsWithoutRef<"p">) => (
<MotionDiv>
<div className="leading-7 mt-4 mb-4">{props.children}</div>
</MotionDiv>
),
blockquote: (props: React.ComponentPropsWithoutRef<"blockquote">) => (
<MotionDiv>
<blockquote
className="border-l-4 border-blue-400 pl-4 italic my-6 py-2"
{...props}
/>
</MotionDiv>
),
code: (props: React.ComponentPropsWithoutRef<"code">) => (
<code
@ -75,7 +51,7 @@ export const markdownComponents = {
/>
),
pre: ({ children, ...props }: React.ComponentPropsWithoutRef<"pre">) => (
<MotionDiv><CodeBlock {...props}>{children}</CodeBlock></MotionDiv>
<CodeBlock {...props}>{children}</CodeBlock>
),
a: (props: React.ComponentPropsWithoutRef<"a">) => (
<a

View File

@ -0,0 +1,3 @@
export function checkIsMobile() {
return typeof window !== "undefined" && window.innerWidth <= 768;
}