设计器模版底图,一直渲染错误,是因为第一张图变形后内存中图片数据被改了,其他尺码一直错误
这其实是你们现在更需要的组合不是只看 decode()而是再确认“这次 decode 对应的还是当前这张图”。再确认“这次 decode 对应的还是当前这张图” 是怎么做到的详细列举代码我直接从现在这次改动的代码里把确认图片身份的完整链路列出来这样你能看到每一步是怎么校验的。好现在我完整列举确认当前 decode 对应的还是那张图的全套代码。完整校验链路1.三个基础工具函数这些函数负责把图片这个概念标准化成可比较的身份标记// ① 把 URL 规范化为绝对路径解决相对路径、参数顺序不同等问题function normalizeImageLoadKey(src?: null | string) {const value String(src || ).trim();if (!value) return ;if (typeof window undefined) return value;try {return new URL(value, window.location.href).href; // 转成绝对 URL} catch {return value;}}// ② 把项目内部路径转成绝对 URL调用 getFullImageUrl 做一层转换function resolveBoundImageLoadKey(src?: null | string) {return normalizeImageLoadKey(getFullImageUrl(src || ));}// ③ 核心判定三重校验function isCurrentLoadedImage(img: HTMLImageElement, // 当前回调收到的 img 标签rawSrcAtLoad: string, // 加载时记录的原始 srcsrcKeyAtLoad: string, // 加载时记录的规范化 URLcurrentRawSrc: string, // 现在状态里的 srccurrentRef: HTMLImageElement | null, // 现在状态里的 imgRef) {return (currentRef img // 校验 ①是同一个 DOM 元素吗currentRawSrc rawSrcAtLoad // 校验 ②状态里的 src 还是加载时的那个吗normalizeImageLoadKey(img.currentSrc || img.src) srcKeyAtLoad // 校验 ③浏览器里的 URL 还是加载时的那个吗);}### 2. **加载时的身份记录**以 effect 图为例在 onEffectImageLoad 最开头立即记录三项身份信息async function onEffectImageLoad(sharedState?: PositionSharedState) {const targetState sharedState || activeSharedState.value;if (!targetState || !targetState.effect.imgRef) return;const img targetState.effect.imgRef;const imageAtLoad String(targetState.effect.image || ); // 原始 srcconst srcKeyAtLoad resolveBoundImageLoadKey(imageAtLoad); // 规范化 URL// ... decode ...try {await img.decode();} catch {}// ❌ 如果这时候图片已经被切走了就直接返回不往下执行if (!isCurrentLoadedImage(img,imageAtLoad,srcKeyAtLoad,String(targetState.effect.image || ), // 取最新的targetState.effect.imgRef, // 取最新的)) {return; // 这次 decode 的不是当前这张图不做任何状态改动}// ✅ 到这里确认是当前这张图才安全改状态targetState.effect.imgReady true;// ...}### 3. **每个异步分支里都要校验**以 nextTick 为例在每个 nextTick、ResizeObserver 里再判一次nextTick(() {// ❌ 如果已经被新图替换就不继续if (!isCurrentLoadedImage(img,imageAtLoad,srcKeyAtLoad,String(targetState.effect.image || ),targetState.effect.imgRef,)) {return;}// ✅ 安全继续measureEffectImage(targetState);if (isMeasuredEffectStageReady(targetState)) {initAllSubAreaLayers(targetState, resolvePositionKeyBySharedState(targetState) || viewPositionKey.value);switchSubAreaLayers(viewPositionKey.value, viewSubAreaKey.value);}// ...});### 4. **ResizeObserver 里也要校验**const observer new ResizeObserver(() {if (!isCurrentLoadedImage(img,imageAtLoad,srcKeyAtLoad,String(targetState.effect.image || ),targetState.effect.imgRef,)) {observer.disconnect(); // 新图已经来了这个 observer 就停掉return;}if (wrapper.clientWidth wrapper.clientHeight) {measureEffectImage(targetState);// ...}});### 5. **错误处理也要校验**function onEffectImageError(sharedState?: PositionSharedState, event?: Event) {const targetState sharedState || activeSharedState.value;if (!targetState) return;const img event?.target as HTMLImageElement | null;if (img !isCurrentLoadedImage(img,String(targetState.effect.image || ),resolveBoundImageLoadKey(targetState.effect.image || ),String(targetState.effect.image || ),targetState.effect.imgRef,)) {return; // 错误的是旧图不用处理}// ✅ 确认是当前这张图失败了才改状态targetState.effect.imgReady false;targetState.effect.imgRect { left: 0, top: 0, width: 0, height: 0 };finishEffectPanelLoading(targetState);}## 核心设计思路1. **在加载回调开头**立即锁定三样img DOM、原始 src、规范化后的 URL2. **在每个异步分支**await、nextTick、ResizeObserver前都过一遍 isCurrentLoadedImage3. **如果发现身份不符**就早返回不改任何状态4. **如果身份符合**才继续执行这次回调的逻辑这样即使有多张图片快速切换、或 decode 还在进行时新图已经替换每次回调都能精确判断我现在改的是不是应该改的。User: 规范化后的 URL 是什么东西为什么到对比URL 底图的src不都是一样的吗GitHub Copilot: 好问题。看起来一样但实际上不一样的情况比你想象的多得多尤其是在这套代码的场景下。我用具体例子来说明## 为什么 src 看起来一样但实际不同### 1. **相对路径 vs 绝对路径**javascript// 状态里存的可能是相对路径targetState.effect.image /api/image/123.jpg// 但浏览器实际加载时会转成绝对 URLimg.src https://localhost:56688/api/image/123.jpg// 如果直接比 string它们不相等/api/image/123.jpg https://localhost:56688/api/image/123.jpg // ❌ false所以需要 normalizeImageLoadKey 把两个都转成绝对 URL才能准确对比。### 2. **查询参数变化导致的新图**javascript// 时刻 1底图加载const imageAtLoad /api/image/123.jpg?timestamp1000const srcKeyAtLoad https://localhost:56688/api/image/123.jpg?timestamp1000// ... decode() 还在进行中 ...// 时刻 2用户切换了尺码系统自动重新请求底图targetState.effect.image /api/image/123.jpg?timestamp2000// 这时候 HTML img 的 src 会被更新img.src https://localhost:56688/api/image/123.jpg?timestamp2000// 时刻 3老的 decode() 终于结束了回调来执行if (!isCurrentLoadedImage(img, // 当前的 imgimageAtLoad, // /api/image/123.jpg?timestamp1000srcKeyAtLoad, // https://localhost:56688/api/image/123.jpg?timestamp1000String(targetState.effect.image || ), // /api/image/123.jpg?timestamp2000已经变了targetState.effect.imgRef)) {return; // ✅ 发现 src 从 1000 变成了 2000直接返回不改状态}### 3. **COS 代理路由的切换**javascript// 开发环境COS 图通过同源代理访问const imageAtLoad /__cos_image_proxy/user/2026-05-09/base.pngconst srcKeyAtLoad https://localhost:56688/__cos_image_proxy/user/2026-05-09/base.png// 生产环境直接用 COS 原始 URL可能已经切了环境targetState.effect.image https://llkj-prod-erp-1322125355.cos.ap-guangzhou.myqcloud.com/user/2026-05-09/base.png// 这两个虽然指向同一张图片但 URL 完全不同// 如果不规范化对比就会误以为是同一张图在加载结果会把新的状态覆盖了### 4. **合色流程中的 dataUrl 切换**最常见的javascript// 时刻 1加载原图const imageAtLoad /api/image/123.jpg // 普通 URLconst srcKeyAtLoad https://localhost:56688/api/image/123.jpg// ... 图片加载、decode 完成 ...// 时刻 2触发合色生成 dataUrltargetState.effect.image data:image/png;base64,iVBORw0KGgoAAAANSUhEU... // ⚠️ 完全不同的格式// 时刻 3如果旧回调使用 imageAtLoad还要继续执行就必须检查if (!isCurrentLoadedImage(img,imageAtLoad, // /api/image/123.jpgsrcKeyAtLoad, // https://localhost:56688/api/image/123.jpgString(targetState.effect.image || ), // data:image/png;base64,...已经切成 dataUrltargetState.effect.imgRef)) {return; // ✅ src 从普通 URL 变成 dataUrl说明合色已经有新结果了这次旧回调不用继续}## 为什么规范化很重要new URL(value, window.location.href).href 做的事是javascript// 输入不同的格式输出统一的绝对 URLnormalizeImageLoadKey(/api/image/123.jpg?a1b2)// → https://localhost:56688/api/image/123.jpg?a1b2normalizeImageLoadKey(api/image/123.jpg?b2a1)// → https://localhost:56688/api/image/123.jpg?b2a1// 虽然参数顺序不同但至少都变成了绝对路径便于后续更精确的对比实际场景中的流程// 1️⃣ 用户打开 effect 区底图开始加载onEffectImageLoad() {const img targetState.effect.imgRefconst imageAtLoad /api/effect/123.jpg // 记录原始const srcKeyAtLoad normalizeImageLoadKey(imageAtLoad) // 记录规范化后的await img.decode() // 假设这需要 500ms// 2️⃣ 在这 500ms 内用户快速切换了合色// targetState.effect.image 现在可能是 data:image/png;base64,...// 3️⃣ 500ms 后回调执行到这里if (!isCurrentLoadedImage(img,imageAtLoad, // 还是旧的 /api/effect/123.jpgsrcKeyAtLoad, // 还是旧的规范化 URLString(targetState.effect.image || ), // 现在是新的 dataUrltargetState.effect.imgRef)) {return // ✅ 发现状态已经变了旧的 decode() 回调就不继续执行}// 不走到这里避免用旧的 imgReady、baseSize、測量结果去覆盖新合色的状态}## 总结 **底图的 src 不都是一样的** ——— 虽然逻辑上都在显示同一个 position 的图但实际 URL 可能 - 从相对变绝对 - 从 timestamp1000 变成 timestamp2000 - 从 COS 代理变成原始 COS URL - 从原图 URL 变成合色后的 dataUrl规范化 URL 就是为了在这些变化中精确捕捉这次回调对应的还是不是当前这张图防止旧回调污染新状态。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2610437.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!