Skip to content

Commit 09cbb5a

Browse files
committed
feat: 添加@zumer/snapdom库以支持图像下载功能,并在RoteShareCard组件中更新下载逻辑
1 parent 95df868 commit 09cbb5a

3 files changed

Lines changed: 24 additions & 36 deletions

File tree

web/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,12 @@
3131
"@types/file-saver": "^2.0.7",
3232
"@types/lodash": "^4.17.17",
3333
"@types/react-avatar-editor": "^13.0.4",
34+
"@zumer/snapdom": "^1.7.1",
3435
"axios": "^1.9.0",
3536
"class-variance-authority": "^0.7.1",
3637
"clsx": "^2.1.1",
3738
"cmdk": "^1.1.1",
3839
"file-saver": "^2.0.5",
39-
"html-to-image": "^1.11.13",
4040
"i18next": "^25.2.0",
4141
"isbot": "^5.1.27",
4242
"jotai": "^2.12.4",

web/src/components/rote/AttachmentsGrid.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,14 @@ import 'react-photo-view/dist/react-photo-view.css';
44

55
interface AttachmentsGridProps {
66
attachments: (Attachment | File)[];
7+
withTimeStamp?: boolean;
78
}
89

910
/**
1011
* AttachmentsGrid 组件 - 用于显示附件网格
1112
* 支持1-9张图片的自适应布局
1213
*/
13-
export default function AttachmentsGrid({ attachments }: AttachmentsGridProps) {
14+
export default function AttachmentsGrid({ attachments, withTimeStamp }: AttachmentsGridProps) {
1415
return (
1516
attachments.length > 0 && (
1617
<div className="my-2 flex w-fit flex-wrap gap-1 overflow-hidden rounded-2xl">
@@ -27,8 +28,9 @@ export default function AttachmentsGrid({ attachments }: AttachmentsGridProps) {
2728
? 'w-full max-w-[500px] rounded-2xl border-[0.5px]'
2829
: 'aspect-square w-[calc(1/3*100%-3px)]'
2930
} bg-foreground/3 grow object-cover`}
30-
src={file.compressUrl || file.url}
31+
src={`${file.compressUrl || file.url}?${withTimeStamp ? new Date().getTime() : ''}`}
3132
loading="lazy"
33+
crossOrigin={withTimeStamp ? 'anonymous' : undefined}
3234
alt=""
3335
/>
3436
</PhotoView>

web/src/components/rote/roteShareCard.tsx

Lines changed: 19 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
import { SoftBottom } from '@/components/others/SoftBottom';
22
import { Button } from '@/components/ui/button';
3-
import { saveAs } from 'file-saver';
4-
import { toPng } from 'html-to-image';
3+
import { snapdom } from '@zumer/snapdom';
54
import { Link, Save } from 'lucide-react';
65
import moment from 'moment';
7-
import { useTranslation } from 'react-i18next';
86
import { useState } from 'react';
7+
import { useTranslation } from 'react-i18next';
98
import QRCode from 'react-qr-code';
9+
1010
import { toast } from 'sonner';
1111
import AttachmentsGrid from './AttachmentsGrid';
1212

@@ -51,33 +51,19 @@ function RoteShareCard({ rote }: any) {
5151
setIsGenerating(true);
5252
const element: any = document.querySelector('#shareCanva');
5353
if (element) {
54-
// 获取元素的宽度和高度
55-
const width = element.clientWidth;
56-
const height = element.clientHeight;
57-
58-
// 配置 html2canvas 选项
59-
const options: any = {
60-
width: width,
61-
height: height,
62-
canvasWidth: (width * 720) / width,
63-
canvasHeight: (height * 720) / width,
64-
backgroundColor: null,
65-
cacheBust: true,
66-
};
67-
68-
const dataUrl = await toPng(element, options);
69-
70-
if (!dataUrl) {
71-
toast.error(t('imageGenerationFailed'));
72-
setIsGenerating(false);
73-
return;
74-
}
75-
76-
if (window.saveAs) {
77-
window.saveAs(dataUrl, `${rote.id}.png`);
78-
} else {
79-
saveAs(dataUrl, `${rote.id}.png`);
80-
}
54+
await snapdom.download(element, {
55+
scale: 3,
56+
format: 'png',
57+
filename: `rote-${rote.id}.png`,
58+
crossOrigin: (url) => {
59+
// Use credentials for same-origin images
60+
if (url.startsWith(window.location.origin)) {
61+
return 'use-credentials';
62+
}
63+
// Use anonymous for cross-origin images
64+
return 'anonymous';
65+
},
66+
});
8167

8268
toast.success(t('imageSaved'));
8369
setIsGenerating(false);
@@ -123,11 +109,11 @@ function RoteShareCard({ rote }: any) {
123109
<div className="font-sm opacity-60">
124110
{moment().utc(rote.createdAt).format('YYYY/MM/DD HH:mm:ss')}
125111
</div>
126-
<div className="font-serif leading-7 font-light tracking-wide break-words whitespace-pre-line md:text-lg">
112+
<div className="leading-7 font-light tracking-wide break-words whitespace-pre-line md:text-lg">
127113
{rote.content}
128114
</div>
129-
<AttachmentsGrid attachments={rote.attachments} />
130-
<div className="md:text-md flex flex-wrap items-center gap-2 font-serif text-xs">
115+
<AttachmentsGrid attachments={rote.attachments} withTimeStamp={true} />
116+
<div className="md:text-md flex flex-wrap items-center gap-2 text-sm">
131117
{rote.tags.map((tag: any) => (
132118
<span
133119
className={`rounded-md px-2 py-1 md:px-3 ${themes[themeIndex].tagClass}`}

0 commit comments

Comments
 (0)