@@ -16,11 +16,16 @@ import {
1616 message
1717} from "antd" ;
1818import { createStyles } from "antd-style" ;
19+ import domtoimage from "dom-to-image-more" ;
1920import { saveAs } from "file-saver" ;
20- import { useCallback , useMemo , useState } from "react" ;
21+ import { RefObject , useCallback , useMemo , useState } from "react" ;
22+
23+ const globalOptions = {
24+ disableEmbedFonts : true
25+ } ;
2126
2227type ViewerHeaderProps = {
23- svg : React . ReactElement ;
28+ svg : React . ReactNode ;
2429 text : string ;
2530 download ?: boolean ;
2631 zoomIn ?: boolean ;
@@ -71,86 +76,36 @@ export default ({
7176 const [ scale , setScale ] = useState < number > ( 1 ) ;
7277 const { styles } = useStyles ( ) ;
7378
74- const getCurrentSvgElement = useCallback ( ( ) : SVGElement | null => {
75- console . log ( svgEl ) ;
76- if ( ! svgEl ) return null ;
77- return svgEl as unknown as SVGSVGElement ;
78- } , [ ] ) ;
79-
80- const serializeSvg = useCallback ( ( svgEl : SVGSVGElement ) : string => {
81- const serializer = new XMLSerializer ( ) ;
82- let svgString = serializer . serializeToString ( svgEl ) ;
83- // Ensure xmlns is present
84- if ( ! svgString . includes ( 'xmlns="http://www.w3.org/2000/svg"' ) ) {
85- svgString = svgString . replace (
86- "<svg" ,
87- '<svg xmlns="http://www.w3.org/2000/svg"'
88- ) ;
79+ const getCurrentSvgElement = useCallback ( ( ) : SVGSVGElement | null => {
80+ const svgEle = svg as React . ReactElement < React . SVGProps < SVGSVGElement > > ;
81+ if (
82+ ! svgEle ||
83+ ! svgEle . props ||
84+ ! svgEle . props . ref ||
85+ typeof svgEle . props . ref !== "object" ||
86+ ! ( "current" in svgEle . props . ref )
87+ ) {
88+ return null ;
8989 }
90- return svgString ;
90+ return svgEle . props . ref . current ;
9191 } , [ ] ) ;
9292
93- const svgToPngBlob = useCallback (
94- async ( svgEl : SVGSVGElement ) : Promise < Blob > => {
95- const rect = svgEl . getBoundingClientRect ( ) ;
96- const viewBox = svgEl . viewBox ?. baseVal ;
97- const width = Math . max (
98- 1 ,
99- viewBox ?. width || svgEl . width ?. baseVal ?. value || rect . width || 800
100- ) ;
101- const height = Math . max (
102- 1 ,
103- viewBox ?. height || svgEl . height ?. baseVal ?. value || rect . height || 600
104- ) ;
105- const pixelRatio = Math . max ( 1 , window . devicePixelRatio || 1 ) ;
106-
107- const svgString = serializeSvg ( svgEl ) ;
108- const blob = new Blob ( [ svgString ] , {
109- type : "image/svg+xml;charset=utf-8"
110- } ) ;
111- const url = URL . createObjectURL ( blob ) ;
112- try {
113- const img = await new Promise < HTMLImageElement > ( ( resolve , reject ) => {
114- const image = new Image ( ) ;
115- image . crossOrigin = "anonymous" ;
116- image . onload = ( ) => resolve ( image ) ;
117- image . onerror = ( e ) => reject ( e ) ;
118- image . src = url ;
119- } ) ;
120- const canvas = document . createElement ( "canvas" ) ;
121- canvas . width = Math . round ( width * pixelRatio * scale ) ;
122- canvas . height = Math . round ( height * pixelRatio * scale ) ;
123- const ctx = canvas . getContext ( "2d" ) ;
124- if ( ! ctx ) throw new Error ( "CanvasContext is null" ) ;
125- ctx . scale ( pixelRatio * scale , pixelRatio * scale ) ;
126- ctx . drawImage ( img , 0 , 0 , width , height ) ;
127- return await new Promise < Blob > ( ( resolve , reject ) => {
128- canvas . toBlob (
129- ( b ) => ( b ? resolve ( b ) : reject ( new Error ( "toBlob failed" ) ) ) ,
130- "image/png"
131- ) ;
132- } ) ;
133- } finally {
134- URL . revokeObjectURL ( url ) ;
135- }
136- } ,
137- [ scale , serializeSvg ]
138- ) ;
139-
14093 const handleDownload = useCallback ( async ( ) => {
14194 const svgEl = getCurrentSvgElement ( ) ;
14295 if ( ! svgEl ) {
14396 message . warning ( "未找到可下载的图片" ) ;
14497 return ;
14598 }
14699 try {
147- const blob = await svgToPngBlob ( svgEl ) ;
100+ const blob = await domtoimage . toBlob ( svgEl , globalOptions ) ;
101+ console . log ( blob ) ;
148102 saveAs ( blob , "svg-viewer.png" ) ;
149103 message . success ( "图片已开始下载" ) ;
150104 } catch ( e ) {
151105 message . error ( "下载失败" ) ;
106+ console . error ( e ) ;
152107 }
153- } , [ getCurrentSvgElement , svgToPngBlob ] ) ;
108+ } , [ getCurrentSvgElement ] ) ;
154109
155110 const handleCopyImage = useCallback ( async ( ) => {
156111 const svgEl = getCurrentSvgElement ( ) ;
@@ -159,7 +114,7 @@ export default ({
159114 return ;
160115 }
161116 try {
162- const blob = await svgToPngBlob ( svgEl ) ;
117+ const blob = await domtoimage . toPng ( svgEl , globalOptions ) ;
163118 if ( navigator . clipboard && ( navigator . clipboard as any ) . write ) {
164119 await navigator . clipboard . write ( [
165120 new window . ClipboardItem ( { "image/png" : blob } )
@@ -169,9 +124,10 @@ export default ({
169124 message . warning ( "当前环境不支持复制图片" ) ;
170125 }
171126 } catch ( e ) {
127+ console . error ( e ) ;
172128 message . error ( "复制失败" ) ;
173129 }
174- } , [ getCurrentSvgElement , svgToPngBlob ] ) ;
130+ } , [ getCurrentSvgElement ] ) ;
175131
176132 const handleCopyCode = useCallback ( async ( ) => {
177133 try {
0 commit comments