diff --git a/js/Bangumi Topic Share/Bangumi_Topic_Share.js b/js/Bangumi Topic Share/Bangumi_Topic_Share.js index c9f5b66..743d675 100644 --- a/js/Bangumi Topic Share/Bangumi_Topic_Share.js +++ b/js/Bangumi Topic Share/Bangumi_Topic_Share.js @@ -1,7 +1,7 @@ // ==UserScript== // @name Bangumi Topic Share // @namespace https://greasyfork.org/scripts/574689 -// @version 6.4 +// @version 6.5 // @description Bangumi 分享工具:生成分享卡片,支持图片复制/下载、一键复制分享文案、贴贴反应展示、可选 AI 标签 // @author Stardream // @contributor Chang ji, Mewtw0 @@ -547,7 +547,7 @@
- ${base64CharImage ? `
${pureTitle}
${badgeLabel}
` : badgeLabel ? `
${pureTitle}
${badgeLabel}
` : ''} + ${base64CharImage ? `
${pureTitle}
${badgeLabel}
` : badgeLabel ? `
${pureTitle}
${badgeLabel}
` : ''} ${username ? `
@@ -679,6 +679,41 @@ const timeout = new Promise((_, reject) => setTimeout(() => reject(new Error('timeout')), 10000)); const captureEl = document.querySelector('#capture-area'); + // html2canvas ignores object-fit, so a cover image would be stretched to fill the box. + // Cover-crop the cover to its box (object-position: center top) at 2x on the LIVE, already + // laid-out card (measuring inside the fresh iframe races on a cold first open, yielding a + // 0-size box → crop skipped → stretched image). The cropped result is applied to the + // iframe clone below. Recomputed from the original base64 each time, so it stays idempotent. + let croppedCover = ''; + if (base64CharImage) { + const liveCover = captureEl.querySelector('.bgm-cover-img'); + if (liveCover) { + const box = liveCover.getBoundingClientRect(); + const bw = box.width, bh = box.height; + const src = new Image(); + await new Promise(res => { src.onload = res; src.onerror = res; src.src = base64CharImage; }); + const iw = src.naturalWidth, ih = src.naturalHeight; + if (iw && ih && bw && bh) { + const s = 2; + const cnv = document.createElement('canvas'); + cnv.width = Math.round(bw * s); + cnv.height = Math.round(bh * s); + const ctx = cnv.getContext('2d'); + ctx.imageSmoothingEnabled = true; + ctx.imageSmoothingQuality = 'high'; + const boxAspect = bw / bh, imgAspect = iw / ih; + let sx, sy, sw, sh; + if (imgAspect > boxAspect) { // wider than box: crop sides, keep full height, center + sh = ih; sw = ih * boxAspect; sx = (iw - sw) / 2; sy = 0; + } else { // taller than box: crop bottom, keep full width, top-aligned + sw = iw; sh = iw / boxAspect; sx = 0; sy = 0; + } + ctx.drawImage(src, sx, sy, sw, sh, 0, 0, cnv.width, cnv.height); + croppedCover = cnv.toDataURL('image/png'); + } + } + } + iframe = document.createElement('iframe'); iframe.style.cssText = 'position:fixed;top:0;left:0;border:0;opacity:0;pointer-events:none;z-index:99999;'; iframe.style.width = captureEl.offsetWidth + 'px'; @@ -734,6 +769,12 @@ iDoc.body.style.cssText = 'margin:0;padding:0;background:transparent;display:inline-block;'; iDoc.body.innerHTML = captureEl.innerHTML; + // Apply the pre-cropped cover (computed on the live card above) to the iframe clone. + if (croppedCover) { + const ic = iDoc.querySelector('.bgm-cover-img'); + if (ic) { ic.src = croppedCover; ic.style.objectFit = 'fill'; } + } + await new Promise(r => requestAnimationFrame(() => requestAnimationFrame(r))); // html2canvas renders inline backgrounds as full-width blocks; replace each @@ -961,7 +1002,7 @@ } const subject = await fetchBangumiAPI(`subjects/${ep.subject_id}`); let subjectName = subject?.name_cn || subject?.name || ''; - let subjectImageUrl = subject?.images?.medium || subject?.images?.common || ''; + let subjectImageUrl = subject?.images?.common || subject?.images?.large || subject?.images?.medium || ''; if (!subject) { _shareNotice = 'NSFW 条目 API 无权访问,已降级为页面数据,部分信息可能不完整'; const subjectLink = document.querySelector('#headerSubject a[href*="/subject/"]') || document.querySelector('a.cover[href*="/subject/"]'); @@ -1032,7 +1073,7 @@ const typeMap = { 1: '书籍', 2: '动画', 3: '音乐', 4: '游戏', 6: '三次元' }; const result = { name: data.name_cn || data.name || '', - imageUrl: data.images?.medium || data.images?.common || '', + imageUrl: data.images?.common || data.images?.large || data.images?.medium || '', type: typeMap[data.type] || '作品', typeNum: data.type, infobox: data.infobox || []