惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Stack Overflow Blog
Stack Overflow Blog
T
Tailwind CSS Blog
Recent Announcements
Recent Announcements
宝玉的分享
宝玉的分享
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
P
Proofpoint News Feed
D
Docker
Google DeepMind News
Google DeepMind News
aimingoo的专栏
aimingoo的专栏
B
Blog RSS Feed
Microsoft Security Blog
Microsoft Security Blog
博客园 - 【当耐特】
M
MIT News - Artificial intelligence
云风的 BLOG
云风的 BLOG
小众软件
小众软件
Hugging Face - Blog
Hugging Face - Blog
WordPress大学
WordPress大学
IT之家
IT之家
H
Help Net Security
Apple Machine Learning Research
Apple Machine Learning Research
Martin Fowler
Martin Fowler
S
SegmentFault 最新的问题
B
Blog
D
DataBreaches.Net

Hyde Blog

Hyde Blog Hyde Blog Hyde Blog Hyde Blog Hyde Blog Hyde Blog Hyde Blog Hyde Blog Hyde Blog Hyde Blog Hyde Blog Hyde Blog Hyde Blog Hyde Blog Hyde Blog Hyde Blog Hyde Blog Hyde Blog Hyde Blog Hyde Blog Hyde Blog Hyde Blog Hyde Blog Hyde Blog Hyde Blog Hyde Blog Hyde Blog Hyde Blog Hyde Blog Hyde Blog
Hyde Blog
Hyde · 2025-10-05 · via Hyde Blog

tsx

import Link from '@docusaurus/Link'
import Translate from '@docusaurus/Translate'
import type { BlogPost } from '@docusaurus/plugin-content-blog'
import { usePluginData } from '@docusaurus/useGlobalData'
import { cn } from '@site/src/lib/utils'
import Image from '@theme/IdealImage'
import { motion, useScroll, useTransform } from 'framer-motion'
import React from 'react'
import { Section } from '../Section'

const chunk = (arr, size) =>
  Array.from({ length: Math.ceil(arr.length / size) }, (_, i) => arr.slice(i * size, i * size + size))

const BLOG_POSTS_COUNT = 6
const BLOG_POSTS_PER_ROW = 2

export function BlogItem({ post }: { post: BlogPost }) {
  const {
    // highlight-next-line
    metadata: { permalink, frontMatter, title, description, date, tags },
  } = post
    // highlight-next-line
  const truncatedDescription = description.length > 100 ? `${description.slice(0, 100)}...` : description

  return (
    <motion.li
      className={cn('card', 'margin-bottom--md flex w-full bg-blog shadow-blog')}
      key={permalink}
      initial={{ y: 100, opacity: 0.001 }}
      whileInView={{ y: 0, opacity: 1, transition: { duration: 0.5 } }}
      whileHover={{ y: -10, transition: { duration: 0.3 } }}
      viewport={{ once: true }}
    // highlight-start
      style={{
        border: '1px solid #ccc', // 默认边框样式
        borderRadius: '12px', // 圆角边框
        overflow: 'hidden',
      }}
    // highlight-end
    >
      {frontMatter.image && (
        // highlight-start
        // 如果frontMatter.image存在,则创建一个Link组件,指向permalink,并设置className为cursor-pointer overflow-hidden
        <Link href={permalink} className="w-full cursor-pointer overflow-hidden">
          <Image
            // 创建一个Image组件,src为frontMatter?.image,alt为title,img为空字符串,className为object-cover w-full h-auto,style为maxHeight: '150px'
            src={frontMatter?.image}
            alt={title}
            img={''}
            className="object-cover w-full h-auto"
            style={{ maxHeight: '150px' }}
          />
        // highlight-end
        </Link>
      )}
      // highlight-start
      <div className={'card__body p-2'} style={{ padding: '8px' }}>
        {/* // 设置card__body的样式,包括内边距为8px */}
        <h4 className="text-base" style={{ overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
        // highlight-end  
          <Link href={permalink} className="relative hover:no-underline">
            {title}
          </Link>
        </h4>
        // highlight-start
        <p className="text-sm" style={{ marginBottom: '4px' }}>{truncatedDescription}</p>
        {/* 创建时间 */}
        <div className="flex justify-between items-center mt-2">
          <p className="text-xs text-gray-500">{`创建时间: ${new Date(date).toLocaleDateString()}`}</p>
          {/* 标签 */}
          <div className="flex flex-wrap gap-1">
            {tags.map(tag => (
              <span key={tag.label} className="text-xs bg-gray-200 text-gray-700 rounded px-2 py-1">
                {tag.label}
              </span>
            ))}
          </div>
        </div>
        // highlight-end
      </div>
    </motion.li>
  )
}

export default function BlogSection(): JSX.Element {
  const blogData = usePluginData('docusaurus-plugin-content-blog') as {
    posts: BlogPost[]
    postNum: number
    tagNum: number
  }

  const posts = chunk(blogData.posts.slice(0, BLOG_POSTS_COUNT), BLOG_POSTS_PER_ROW)

  const ref = React.useRef<HTMLDivElement>(null)

  const { scrollYProgress } = useScroll()
  const y = useTransform(scrollYProgress, [0, 0.5, 1], [20, 0, -20], {
    clamp: false,
  })

  if (blogData.postNum === 0) {
    return <>作者还没开始写博文哦...</>
  }

  return (
    <Section title={<Translate id="homepage.blog.title">🎉 近期博客</Translate>} href={'/blog'}>
      <div ref={ref} className="flex flex-col gap-4 overflow-hidden rounded-card p-3 md:grid md:grid-cols-12">
        {posts.map((postGroup, index) => (
          // 设置列跨度为6,在大型设备上为4
          // highlight-next-line
          <div className="col-span-6 lg:col-span-4" key={index}>
            {postGroup.map((post, i) => (
              <motion.div style={{ y: i / 2 ? y : 0 }} key={i}>
                <BlogItem key={post.id} post={post} />
              </motion.div>
            ))}
          </div>
        ))}
      </div>
    </Section>
  )
}