博士tl;
这个问题让我很抓狂,在渲染画布之前用crossOrigin="anonymous"加载图像解决了这个问题。
详细和过于具体的解决方案
对于那些使用React + canvg + Amazon S3并希望通过canvas将svg导出为png的人来说,这可能很有用。
首先,创建一个React钩子来检测预加载的跨原点图像:
// useLoadCrossOriginImage.tsx
import { useReducer, useMemo } from 'react'
export function useLoadCrossOriginImage(imageUrls: string[]) {
const [count, increase] = useReducer((count) => count + 1, 0)
const render = () =>
imageUrls.map((url) => (
<img
src={url}
crossOrigin="anonymous"
onLoad={increase}
className="hidden"
/>
))
const loaded = useMemo(() => count === imageUrls.length, [count, imageUrls])
return {
render,
loaded,
}
}
然后,加载图像后,惰性渲染svg:
// ImagePreview.tsx
import { useLoadCrossOriginImage } from './useLoadCrossOriginImage'
// This is usually state from parent component
const imageUrls = [
'https://s3-ap-northeast-1.amazonaws.com/bucket/xxxxxxx.png',
'https://s3-ap-northeast-1.amazonaws.com/bucket/yyyyyyy.png',
]
export const ImagePreview = () => {
const { loaded, render } = useLoadCrossOriginImage(imageUrls)
return (
<div className="border border-slate-300" onClick={onClick}>
{render()}
{loaded && (
<svg xmlns="http://www.w3.org/2000/svg">
{imageUrls.map((imageUrl) => (
<image key={el.id} href={imageUrl} />
))}
</svg>
)}
<canvas className="hidden" />
</div>
)
}
最后,你可以将canvas元素转换为png:
const canvas = document.querySelector('canvas')!
const ctx = canvas.getContext('2d')!
const svg = document.querySelector('svg')!
const v = Canvg.fromString(ctx, svg.outerHTML, { anonymousCrossOrigin: true })
最后,S3的cors策略应该是这样的:
{
"CORSRules": [
{
"ID": "s3-cors-policy",
"AllowedHeaders": ["*"],
"AllowedMethods": ["GET", "HEAD"],
"AllowedOrigins": ["*"],
"ExposeHeaders": []
}
]
}
请将“maxagesecseconds”保留为空。