mirror of
https://github.com/snowykami/neo-blog.git
synced 2025-09-25 18:46:23 +00:00
feat: 添加移动端检测功能,优化组件动画效果
All checks were successful
Push to Helm Chart Repository / build (push) Successful in 9s
All checks were successful
Push to Helm Chart Repository / build (push) Successful in 9s
This commit is contained in:
@ -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={[
|
||||
|
@ -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>
|
||||
);
|
||||
|
@ -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
|
||||
|
3
web/src/utils/client/device.ts
Normal file
3
web/src/utils/client/device.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export function checkIsMobile() {
|
||||
return typeof window !== "undefined" && window.innerWidth <= 768;
|
||||
}
|
Reference in New Issue
Block a user