hookehuyr

fix(SharePoster): 修复海报生成时的背景透明问题和CDN图片压缩参数

- 将海报背景从透明改为白色,避免透明背景导致的显示问题
- 修复CDN图片压缩参数重复追加的问题并优化参数处理逻辑
- 重新启用生成中的提示文本
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
14 </div> 14 </div>
15 15
16 <!-- 生成中提示 --> 16 <!-- 生成中提示 -->
17 - <!-- <div v-if="is_generating" class="text-center text-gray-500 text-sm mb-2">正在生成海报...</div> --> 17 + <div v-if="is_generating" class="text-center text-gray-500 text-sm mb-2">正在生成海报...</div>
18 18
19 <!-- 海报区域:直接使用 Canvas 合成的图片,支持长按保存 --> 19 <!-- 海报区域:直接使用 Canvas 合成的图片,支持长按保存 -->
20 <!-- 当已生成海报图时,容器不再应用卡片边框与阴影,避免双重边框视觉效果;降级展示仍保留卡片样式 --> 20 <!-- 当已生成海报图时,容器不再应用卡片边框与阴影,避免双重边框视觉效果;降级展示仍保留卡片样式 -->
...@@ -27,7 +27,7 @@ ...@@ -27,7 +27,7 @@
27 <img :src="cover_final_src" alt="课程封面" class="w-full h-auto object-contain" crossorigin="anonymous" /> 27 <img :src="cover_final_src" alt="课程封面" class="w-full h-auto object-contain" crossorigin="anonymous" />
28 </div> 28 </div>
29 <!-- 下部信息区:左二维码 + 右文案 --> 29 <!-- 下部信息区:左二维码 + 右文案 -->
30 - <div class="PosterInfo p-4 bg-white"> 30 + <div class="PosterInfo p-4">
31 <div class="flex items-start"> 31 <div class="flex items-start">
32 <!-- 左侧二维码 --> 32 <!-- 左侧二维码 -->
33 <div class="PosterQR mr-4"> 33 <div class="PosterQR mr-4">
...@@ -82,9 +82,13 @@ function normalize_image_url(src) { ...@@ -82,9 +82,13 @@ function normalize_image_url(src) {
82 try { 82 try {
83 const u = new URL(url, window.location.origin) 83 const u = new URL(url, window.location.origin)
84 if (u.hostname === 'cdn.ipadbiz.cn') { 84 if (u.hostname === 'cdn.ipadbiz.cn') {
85 - if (u.hostname === 'cdn.ipadbiz.cn') { 85 + // CDN 图片统一追加压缩参数,若地址未包含 imageMogr2,则追加压缩参数
86 - return url + '?imageMogr2/thumbnail/400x/strip/quality/70' 86 + const param = 'imageMogr2/thumbnail/200x/strip/quality/70'
87 - } 87 + const has_mogr = url.includes('imageMogr2')
88 + if (!has_mogr) {
89 + return url + (url.includes('?') ? '&' : '?') + param
90 + }
91 + return url
88 } 92 }
89 } catch (e) { 93 } catch (e) {
90 // 非绝对路径或无法解析的场景,直接返回原值 94 // 非绝对路径或无法解析的场景,直接返回原值
...@@ -300,11 +304,10 @@ async function compose_poster() { ...@@ -300,11 +304,10 @@ async function compose_poster() {
300 // 透明 1x1 PNG 作为占位图,避免资源获取失败直接中断 304 // 透明 1x1 PNG 作为占位图,避免资源获取失败直接中断
301 const placeholder = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAusB9Wm3T9kAAAAASUVORK5CYII=' 305 const placeholder = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAusB9Wm3T9kAAAAASUVORK5CYII='
302 // 生成 PNG dataURL 306 // 生成 PNG dataURL
303 - // 设置背景为透明,保留圆角的透明角,避免白色四角
304 const data_url = await toPng(clone, { 307 const data_url = await toPng(clone, {
305 pixelRatio: pixel_ratio, 308 pixelRatio: pixel_ratio,
306 cacheBust: true, 309 cacheBust: true,
307 - backgroundColor: 'transparent', 310 + backgroundColor: '#ffffff',
308 imagePlaceholder: placeholder, 311 imagePlaceholder: placeholder,
309 fetchRequestInit: { mode: 'cors', cache: 'no-cache', credentials: 'omit' }, 312 fetchRequestInit: { mode: 'cors', cache: 'no-cache', credentials: 'omit' },
310 // 避免外层 margin 影响截图结果 313 // 避免外层 margin 影响截图结果
...@@ -345,7 +348,7 @@ function create_offscreen_clone(node) { ...@@ -345,7 +348,7 @@ function create_offscreen_clone(node) {
345 overflow: 'hidden', 348 overflow: 'hidden',
346 opacity: '0', 349 opacity: '0',
347 pointerEvents: 'none', 350 pointerEvents: 'none',
348 - background: 'transparent', 351 + background: '#ffffff',
349 zIndex: '-1000' 352 zIndex: '-1000'
350 }) 353 })
351 // 克隆节点样式校正,避免动画、阴影、滤镜干扰 354 // 克隆节点样式校正,避免动画、阴影、滤镜干扰
......