/** * 画布图片显示层:远端图片异步加载时,允许用Provider提供的本地预览覆盖,减轻上传或资源切换时的空白。 * 组件不上传文件、不管理Blob URL寿命;这些职责留给提供sources与loaded回调的上层。 */ "use client"; import { createContext, useContext } from "react"; /** 按元素id查找预览URL,并在底层图片加载成功时通知上层;默认空Map与空回调支持独立渲染。 */ export const ImagePreviews = createContext<{ sources: Map; loaded: (id: string) => void }>({ sources: new Map(), loaded: () => {} }); /** src用于底层正式图片,title作为其替代文本;图片禁止原生拖拽,避免干扰画布手势。 * 底图使用懒加载、异步解码并禁止发送Referer;onLoad只通知上层,不直接删除预览。 */ export function CanvasImage({ id, src, title, className }: { id: string; src?: string; title: string; className: string }) { // 本地源按元素id而非远端URL索引,同一资源在不同节点中可拥有各自的显示交接状态。 const previews = useContext(ImagePreviews), local = previews.sources.get(id); // onLoad 只携带元素 id,不附带 src/资源代次;迟到加载能否清掉当前预览需由 Provider 的 loaded 实现判定。 const remote = {title} previews.loaded(id)} />; // 没有 onError 状态或自动重试;底图失败时若上层仍保留 local,覆盖图可继续显示。 // 没有预览或预览已等于正式源时,只绘制一张图片,避免冗余叠层。 if (!local || local === src) return remote; // 正式图片继续在底层加载,本地预览绝对定位覆盖在上方;Provider清掉该id后自然移除覆盖。 // 覆盖图是装饰性的、忽略指针事件,读屏与节点交互都交给底层正式图片和外层节点。 return
{remote}
; }