feat(h5): apply second customer UI feedback and homepage refinements
This commit is contained in:
+55
-7
@@ -4,10 +4,43 @@
|
||||
|
||||
var $ = function (sel, root) { return (root || document).querySelector(sel); };
|
||||
|
||||
/* camera-outline-pure-begin */
|
||||
// Round the exported polygon's corners without changing its position/size.
|
||||
// Quadratic control points stay within the original corner's convex hull.
|
||||
function smoothCameraOutline(outline) {
|
||||
if (typeof outline !== 'string' || !/^M[\d.,\sL+-]+Z$/i.test(outline)) return outline;
|
||||
var numbers = outline.match(/[-+]?\d*\.?\d+/g);
|
||||
if (!numbers) return outline;
|
||||
var values = numbers.map(Number), points = [];
|
||||
if (values.length < 6 || values.length % 2 || !values.every(isFinite)) return outline;
|
||||
for (var i = 0; i < values.length; i += 2) {
|
||||
var p = [values[i], values[i + 1]], last = points[points.length - 1];
|
||||
if (!last || Math.hypot(p[0] - last[0], p[1] - last[1]) > .001) points.push(p);
|
||||
}
|
||||
if (points.length > 1 && Math.hypot(points[0][0] - points[points.length - 1][0], points[0][1] - points[points.length - 1][1]) < .001) points.pop();
|
||||
if (points.length < 3) return outline;
|
||||
var corners = points.map(function (p, j) {
|
||||
var prev = points[(j + points.length - 1) % points.length], next = points[(j + 1) % points.length];
|
||||
var before = Math.hypot(prev[0] - p[0], prev[1] - p[1]), after = Math.hypot(next[0] - p[0], next[1] - p[1]);
|
||||
var radius = Math.min(2, before * .35, after * .35);
|
||||
return { p: p, enter: [p[0] + (prev[0] - p[0]) * radius / before, p[1] + (prev[1] - p[1]) * radius / before],
|
||||
exit: [p[0] + (next[0] - p[0]) * radius / after, p[1] + (next[1] - p[1]) * radius / after] };
|
||||
});
|
||||
function xy(p) { return p.map(function (n) { return n.toFixed(3); }).join(','); }
|
||||
var path = 'M' + xy(corners[0].exit);
|
||||
for (var k = 1; k <= corners.length; k++) {
|
||||
var c = corners[k % corners.length];
|
||||
path += 'L' + xy(c.enter) + 'Q' + xy(c.p) + ' ' + xy(c.exit);
|
||||
}
|
||||
return path + 'Z';
|
||||
}
|
||||
/* camera-outline-pure-end */
|
||||
|
||||
// The PSD geometry uses the same 375 × 812 coordinate space as the capture mask.
|
||||
(window.CameraGeometry || []).forEach(function (geometry, index) {
|
||||
var outline = smoothCameraOutline(geometry.outline);
|
||||
document.querySelectorAll('#cam-mask path[data-shape="' + index + '"]').forEach(function (path) {
|
||||
path.setAttribute('d', geometry.outline);
|
||||
path.setAttribute('d', outline);
|
||||
});
|
||||
var group = document.createElementNS('http://www.w3.org/2000/svg', 'g');
|
||||
group.setAttribute('data-shape', index);
|
||||
@@ -107,7 +140,10 @@
|
||||
}
|
||||
|
||||
/* ─── 弹窗管理 ─── */
|
||||
function openOverlay(id) { $('#' + id).classList.add('show'); }
|
||||
function openOverlay(id) {
|
||||
if (id === 'p-privacy' || id === 'p-photoguide') loadAppFont();
|
||||
$('#' + id).classList.add('show');
|
||||
}
|
||||
function closeOverlay(id) { $('#' + id).classList.remove('show'); }
|
||||
|
||||
// 点遮罩空白处关闭(与各自关闭按钮同行为:隐私→回启动页、拍照引导→留在档案页)
|
||||
@@ -581,8 +617,8 @@
|
||||
var MISS_FRAMES_MAX = 3; // 连续漏检 ≥3 帧(~450ms)才报「未检测到人脸」,容忍单帧抖动
|
||||
var OK_HOLD_MS = 450; // 条件连续合格时长:滤掉一闪而过的合格帧(防运动模糊),达标立即抓拍
|
||||
var NO_PROGRESS_MS = 12000; // 单步超过该时长仍未抓拍→浮出调整提示
|
||||
var PREVIEW_SCALE = 0.85; // 与 CSS #cam-video 的 scale 一致:预览缩小 15%
|
||||
// 人脸框高/视频高。原标定(取景框≈42% 屏高)为 0.26/0.58;预览缩小 15% 后,同样装满轮廓需要脸在原始帧里大 1/0.85 倍
|
||||
var PREVIEW_SCALE = 1; // 与 CSS #cam-video 铺满预览一致,镜像只改变方向
|
||||
// 人脸框高/原始视频高;保留现有距离判定阈值。
|
||||
var FACE_NEAR = 0.30, FACE_FAR = 0.68;
|
||||
var CENTER_TOL = 0.18; // 人脸中心偏离取景中线超过屏宽 18% → 提示移入取景框
|
||||
var LUMA_DARK = 65, LUMA_BRIGHT = 235; // 脸部平均亮度(0–255):过暗/过曝提示调整光线
|
||||
@@ -611,6 +647,7 @@
|
||||
}
|
||||
var detRunning = false, detTimer = null; // 检测循环
|
||||
var okSince = 0; // 当前连续 ok 起点(0=未在累积)
|
||||
var readyHapticAt = 0; // 抖动重试时最多每两秒提醒一次
|
||||
var nudgeTimer = null; // 12s 无进展提示
|
||||
var capturing = false; // 白屏补光抓帧中(暂停姿态判定)
|
||||
var lastHint = ''; // 提示文案去重
|
||||
@@ -636,7 +673,7 @@
|
||||
$('#cam-mask defs').appendChild(clip);
|
||||
})();
|
||||
|
||||
function setMask(cls) { // '' 静态白框 | 'no-face' 呼吸动画 | 'ok' 品牌绿定格
|
||||
function setMask(cls) { // State also drives readiness hints; no visible outline stroke.
|
||||
var m = $('#cam-mask');
|
||||
m.classList.toggle('no-face', cls === 'no-face');
|
||||
m.classList.toggle('ok', cls === 'ok');
|
||||
@@ -664,6 +701,7 @@
|
||||
// 每步进入/回退时的检测状态机重置(蒙版/提示/无进展计时全部归位)
|
||||
function resetDetectState() {
|
||||
okSince = 0;
|
||||
readyHapticAt = 0;
|
||||
missFrames = 0;
|
||||
oppFrames = 0;
|
||||
lastHint = '';
|
||||
@@ -737,6 +775,13 @@
|
||||
} catch (e) { return null; }
|
||||
}
|
||||
var missFrames = 0; // 连续漏检帧计数:单帧抖动不报「未检测到人脸」
|
||||
/* camera-haptic-pure-begin */
|
||||
function captureHaptic(nav) {
|
||||
// Unsupported/blocked vibration must never interrupt automatic capture.
|
||||
try { return typeof nav.vibrate === 'function' && nav.vibrate([70, 40, 70]) !== false; }
|
||||
catch (e) { return false; }
|
||||
}
|
||||
/* camera-haptic-pure-end */
|
||||
function onDetect(d) {
|
||||
var hint = '', ok = false;
|
||||
missFrames = d ? 0 : missFrames + 1;
|
||||
@@ -784,7 +829,10 @@
|
||||
// 合格时「保持不动」只显示不播报:随即抓拍,「拍摄完成」会截断它
|
||||
setHint(ok ? '保持不动' : hint, ok, ok);
|
||||
if (!ok) { okSince = 0; return; }
|
||||
if (!okSince) okSince = Date.now();
|
||||
if (!okSince) {
|
||||
okSince = Date.now();
|
||||
if (!readyHapticAt || okSince - readyHapticAt >= 2000) { readyHapticAt = okSince; captureHaptic(navigator); }
|
||||
}
|
||||
else if (Date.now() - okSince >= OK_HOLD_MS) { okSince = 0; doCapture(); }
|
||||
}
|
||||
|
||||
@@ -858,12 +906,12 @@
|
||||
if (!camLive) return;
|
||||
shutterLock = true;
|
||||
capturing = true;
|
||||
if (isManualMode()) captureHaptic(navigator); // Manual shutter has no readiness callback.
|
||||
clearTimeout(nudgeTimer);
|
||||
hideNudge();
|
||||
var seq = camSeq;
|
||||
var fill = $('#cam-fill');
|
||||
fill.classList.add('on'); // .on 无过渡,瞬间点亮
|
||||
if (navigator.vibrate) navigator.vibrate(30);
|
||||
setTimeout(function () {
|
||||
var shot = captureFromVideo();
|
||||
setTimeout(function () {
|
||||
|
||||
Reference in New Issue
Block a user