fix(report): cap dimension scores and match zoom crops to thumbnails
This commit is contained in:
+26
-2
@@ -1300,7 +1300,7 @@
|
||||
el.classList.toggle('dim-bad', has && rank === 'worst');
|
||||
box.classList.toggle('worst', has && rank === 'worst');
|
||||
box.classList.toggle('best', has && rank === 'best');
|
||||
box.querySelector('.dim-bar i').style.width = has ? Math.max(0, Math.min(100, dim.S * 10)) + '%' : '0';
|
||||
box.querySelector('.dim-bar i').style.width = has ? Math.max(0, SkinScore.capDimensionScore(dim.S) * 10) + '%' : '0';
|
||||
}
|
||||
|
||||
function setCat(key, cat) {
|
||||
@@ -1733,6 +1733,30 @@
|
||||
});
|
||||
|
||||
/* ─── 报告页:每张维度卡各自的子 Tab;点检测图放大,再点返回 ─── */
|
||||
function reportZoomSource(img, frame) {
|
||||
// Bake the thumbnail's CSS zoom/translation and clipping into the large
|
||||
// image. Annotations are already composited with their own photograph.
|
||||
try {
|
||||
var box=img.getBoundingClientRect(), windowBox=frame.getBoundingClientRect();
|
||||
if (!img.naturalWidth || !box.width || !windowBox.width || !windowBox.height) return img.src;
|
||||
var style=getComputedStyle(img), scaleX=box.width/img.naturalWidth, scaleY=box.height/img.naturalHeight;
|
||||
if (style.objectFit==='cover' || style.objectFit==='contain') {
|
||||
scaleX=scaleY=style.objectFit==='cover' ? Math.max(scaleX,scaleY) : Math.min(scaleX,scaleY);
|
||||
}
|
||||
var width=img.naturalWidth*scaleX, height=img.naturalHeight*scaleY;
|
||||
var position=style.objectPosition.split(' ');
|
||||
var px=position[0].endsWith('%') ? parseFloat(position[0])/100 : .5;
|
||||
var py=position[1] && position[1].endsWith('%') ? parseFloat(position[1])/100 : .5;
|
||||
var ratio=Math.min(4,1600/Math.max(windowBox.width,windowBox.height));
|
||||
var cv=document.createElement('canvas');
|
||||
cv.width=Math.round(windowBox.width*ratio);cv.height=Math.round(windowBox.height*ratio);
|
||||
var ctx=cv.getContext('2d');
|
||||
ctx.fillStyle=getComputedStyle(frame).backgroundColor;ctx.fillRect(0,0,cv.width,cv.height);
|
||||
ctx.drawImage(img,(box.left-windowBox.left+(box.width-width)*px)*ratio,
|
||||
(box.top-windowBox.top+(box.height-height)*py)*ratio,width*ratio,height*ratio);
|
||||
return cv.toDataURL('image/jpeg',.95);
|
||||
} catch (_) { return img.src; }
|
||||
}
|
||||
document.querySelectorAll('.card-detail').forEach(function (card, group) {
|
||||
card.querySelector('.detail-tabs').setAttribute('role', 'tablist');
|
||||
var tabs = Array.from(card.querySelectorAll('.dtab')), panels = Array.from(card.querySelectorAll('.dpanel'));
|
||||
@@ -1781,7 +1805,7 @@
|
||||
var sideButton = e.target.closest('.contour-side-photo, .wrinkle-side-photo');
|
||||
var img = sideButton ? sideButton.querySelector('img') : e.target.closest('.ditem-img');
|
||||
if (img && img.src && !img.hidden && img.closest('.dpanel').classList.contains('active')) {
|
||||
$('#zoom-img').src = img.src;
|
||||
$('#zoom-img').src = reportZoomSource(img, sideButton || img.closest('.viz-frame'));
|
||||
openOverlay('p-zoom');
|
||||
history.pushState({ screen: currentScreen, overlay: 'p-zoom' }, '');
|
||||
}
|
||||
|
||||
+8
-5
@@ -19,10 +19,10 @@
|
||||
var focusTips = { firm: '强化紧塑力,提拉轮廓抚平细纹。', vita: '提升透亮感,补水提亮加强防晒。', stab: '增强稳韧力,舒缓泛红强韧屏障。' };
|
||||
function numeric(value) { return typeof value === 'number' && isFinite(value); }
|
||||
function dimensionScore(value) {
|
||||
return numeric(value) ? (Math.round((value + 1e-9) * 10) / 10).toFixed(1) : '-';
|
||||
return numeric(value) ? Math.min(9.9, Math.round((value + 1e-9) * 10) / 10).toFixed(1) : '-';
|
||||
}
|
||||
var gradeRule = 'display-score-8-9-v1';
|
||||
var copyRule = 'wrinkles-excellent-acne-detected-water-v2';
|
||||
var copyRule = 'wrinkles-excellent-acne-detected-water-contour-v3';
|
||||
function scoreOfP(P) {
|
||||
return numeric(P) ? Math.round((Math.max(7, Math.min(10, 7 + 3 * P / 100)) + 1e-9) * 100) / 100 : null;
|
||||
}
|
||||
@@ -118,9 +118,12 @@
|
||||
level = levelForScore(viewS);
|
||||
var template = valid && level ? Copy.conclusion[d[0]][level] : '';
|
||||
if (d[0] === 'contour') {
|
||||
var severity = vars.dark_circle_sev;
|
||||
var description = numeric(severity) ? ['无明显黑眼圈', '黑眼圈轻度', '黑眼圈中度', '黑眼圈重度'][severity] : null;
|
||||
template = template.replace(/黑眼圈轻\/中\/重度(根据分数给出判断)/g, description || '黑眼圈数据未获取');
|
||||
// Mention only positively detected eye problems, independent of the
|
||||
// contour grade. Missing data must not imply absence or presence.
|
||||
var eyeNotes = [], pouch = vars.eye_pouch, severity = vars.dark_circle_sev;
|
||||
if (numeric(pouch) && pouch % 1 === 0 && pouch >= 1 && pouch <= 3) eyeNotes.push(pouch === 1 ? '眼周眼袋初显' : '眼袋明显');
|
||||
if (numeric(severity) && severity % 1 === 0 && severity >= 1 && severity <= 3) eyeNotes.push(['','黑眼圈轻度','黑眼圈中度','黑眼圈重度'][severity]);
|
||||
template = template.replace(/(?:眼袋明显|眼周眼袋初显|眼周无明显眼袋问题)、黑眼圈轻\/中\/重度(根据分数给出判断);/g, eyeNotes.length ? eyeNotes.join('、') + ';' : '');
|
||||
if (vars.la == null || vars.ra == null) template = template.replace(/左右下颌线角度(?:分别为左|左) \{la\}°\/ 右 \{ra\}°[;;]/g, '');
|
||||
}
|
||||
if (d[0] === 'color') {
|
||||
|
||||
+4
-2
@@ -63,6 +63,7 @@
|
||||
}
|
||||
function round2(x) { return Math.round((x + 1e-9) * 100) / 100; } // 1e-9 修正 9.445 类浮点
|
||||
function sRaw(P) { return 7 + 3 * P / 100; }
|
||||
function capDimensionScore(S) { return isNum(S) ? Math.min(9.9, S) : null; }
|
||||
// One shared 100-point total for H5, saved reports and admin. Never use the
|
||||
// upstream total or reweight an incomplete set of dimensions.
|
||||
function weightedTotal(dims) {
|
||||
@@ -71,7 +72,7 @@
|
||||
for (var i = 0; i < keys.length; i++) {
|
||||
var score = dims[keys[i]] && dims[keys[i]].S;
|
||||
if (!isNum(score) || score < 0 || score > 10) return null;
|
||||
total += score * weights[i];
|
||||
total += capDimensionScore(score) * weights[i];
|
||||
}
|
||||
return Math.round(total + 1e-9);
|
||||
}
|
||||
@@ -757,7 +758,7 @@
|
||||
}
|
||||
var dP = ps.length ? Math.min.apply(null, ps) : null;
|
||||
if (!ps.length) notes.push(d.name + ':旗下分类数据全部缺失,维度不参与评分');
|
||||
dims[d.key] = { name: d.name, P: dP, S: sShow(dP), level: levelOf(dP) };
|
||||
dims[d.key] = { name: d.name, P: dP, S: capDimensionScore(sShow(dP)), level: levelOf(dP) };
|
||||
}
|
||||
|
||||
// 均分(判档用未四舍五入原值)与最差分类(并列按 CAT_DEFS 顺序取前者)
|
||||
@@ -942,6 +943,7 @@
|
||||
return Math.max(actual - 5, Math.min(actual + 5, raw));
|
||||
}
|
||||
return {
|
||||
capDimensionScore: capDimensionScore,
|
||||
weightedTotal: weightedTotal,
|
||||
reportSkinAge: reportSkinAge,
|
||||
buildViewModel: buildViewModel,
|
||||
|
||||
Reference in New Issue
Block a user