Skip to content

Commit

Permalink
feat: 阅读进度
Browse files Browse the repository at this point in the history
  • Loading branch information
coderz-w committed Nov 12, 2024
1 parent df6ee64 commit d000474
Show file tree
Hide file tree
Showing 5 changed files with 82 additions and 4 deletions.
21 changes: 21 additions & 0 deletions src/components/icons/Progress.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
export function MaterialSymbolsProgressActivity(props: { progress: number }) {
const offset = 87.92 * ((100 - props.progress) / 100);

return (
<svg style={{ transform: 'rotate(-90deg)' }} width="1em" height="1em" viewBox="0 0 36 36">
{/* 第一圈 */}
<circle r="14" cx="18" cy="18" fill="transparent" stroke="#e0e0e0" strokeWidth="4"></circle>
{/* 第二圈,进度条 */}
<circle
r="14"
cx="18"
cy="18"
fill="transparent"
stroke="var(--accent-color)"
strokeWidth="4"
strokeDasharray="87.92"
strokeDashoffset={`${offset}`}
></circle>
</svg>
);
}
2 changes: 2 additions & 0 deletions src/components/modules/shared/ArticleRightAside.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React from 'react';
import { PropsWithChildren } from 'react';

import { TocAside } from '../toc/TocAside';
import { ReadIndicator } from './ReadIndicator';

export const ArticleRightAside = ({ children }: PropsWithChildren) => {
return (
Expand All @@ -11,6 +12,7 @@ export const ArticleRightAside = ({ children }: PropsWithChildren) => {
as="div"
className="static ml-4"
treeClassName="absolute h-full min-h-[120px] flex flex-col"
accessory={<ReadIndicator />}
/>
</div>
{!!children &&
Expand Down
55 changes: 55 additions & 0 deletions src/components/modules/shared/ReadIndicator.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
'use client';

import { useEffect, useState } from 'react';

import { MaterialSymbolsProgressActivity } from '@/components/icons/Progress';
import { MotionButtonBase } from '@/components/ui/button';
import { cn } from '@/lib/helper';
import { springScrollToTop } from '@/lib/scroller';
import { useMainArticleStore } from '@/store/mainArticleStore';

export const ReadIndicator = () => {
const { Element } = useMainArticleStore();
const [readPercent, setReadPercent] = useState(0);

useEffect(() => {
const handleScroll = () => {
if (!Element) return;

const elementTop = Element.getBoundingClientRect().top + window.scrollY;
const scrollTop = window.scrollY;
const winHeight = window.innerHeight;
const elementHeight = Element.offsetHeight;
// 为了解决一开始进度不为0的问题
const readHeight = scrollTop > winHeight ? scrollTop + winHeight - elementTop : scrollTop;
const scrollPosition = (readHeight / elementHeight) * 100;
setReadPercent(Math.floor(Math.min(Math.max(0, scrollPosition), 100)));
};

window.addEventListener('scroll', handleScroll);
handleScroll();

return () => {
window.removeEventListener('scroll', handleScroll);
};
}, [Element]);

return (
<div className="text-gray-800 dark:text-neutral-300">
<div className="flex items-center gap-2">
<MaterialSymbolsProgressActivity progress={readPercent} />
{readPercent}%<br />
</div>
<MotionButtonBase
onClick={springScrollToTop}
className={cn(
'mt-1 flex flex-nowrap items-center gap-2 opacity-50 transition-all duration-500 hover:opacity-100',
readPercent > 10 ? '' : 'pointer-events-none opacity-0',
)}
>
<i className="i-mingcute-arrow-up-circle-line" />
<span className="whitespace-nowrap">回到顶部</span>
</MotionButtonBase>
</div>
);
};
4 changes: 3 additions & 1 deletion src/components/modules/toc/TocAside.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { useMainArticleStore } from '@/store/mainArticleStore';

export type TocAsideProps = {
treeClassName?: string;
accessory?: React.ReactNode | React.FC;
accessory?: React.ReactNode;
as?: React.ElementType;
className?: string;
};
Expand All @@ -21,6 +21,7 @@ export const TocAside = ({
children,
treeClassName,
as: As = 'aside',
accessory,
}: TocAsideProps & PropsWithChildren) => {
const containerRef = useRef<HTMLUListElement>(null);
const $article = useMainArticleStore(useShallow((state) => state.Element));
Expand Down Expand Up @@ -62,6 +63,7 @@ export const TocAside = ({
$headings={$headings}
containerRef={containerRef}
className={cn('absolute max-h-[75vh]', treeClassName)}
accessory={accessory}
/>
{children}
</As>
Expand Down
4 changes: 1 addition & 3 deletions src/components/ui/divider/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,7 @@ export const DividerVertical: FC<
className,
)}
{...rest}
>
w
</span>
></span>
);
};

Expand Down

0 comments on commit d000474

Please sign in to comment.