@daboss2003/liveness-web 1.0.0 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/dist/ui.js +22 -24
  2. package/package.json +1 -1
  3. package/src/ui.ts +22 -24
package/dist/ui.js CHANGED
@@ -4,9 +4,10 @@ import { DEFAULT_SOUND_DATA_URLS } from "./default-sounds.generated";
4
4
  const OVAL_W = 270;
5
5
  const OVAL_H = 360;
6
6
  const OVAL_TOP_PCT = 40;
7
- // Approximate ellipse perimeter via Ramanujan's formula so pathLength matches
8
- const RX = OVAL_W / 2 - 2;
9
- const RY = OVAL_H / 2 - 2;
7
+ // Progress stroke sits on the main oval: ellipse inset by half stroke so outer edge aligns with cutout
8
+ const STROKE_HALF = 1.75;
9
+ const RX = OVAL_W / 2 - STROKE_HALF;
10
+ const RY = OVAL_H / 2 - STROKE_HALF;
10
11
  const ELLIPSE_PERIMETER = Math.PI * (3 * (RX + RY) - Math.sqrt((3 * RX + RY) * (RX + 3 * RY)));
11
12
  /** Step label (from engine) → hint icon kind. Use this so the correct icon shows when steps are randomized. */
12
13
  const STEP_LABEL_TO_HINT = {
@@ -40,6 +41,11 @@ function createStyles() {
40
41
  display: flex;
41
42
  flex-direction: column;
42
43
  align-items: center;
44
+ /* Radii: keep oval inside viewport on mobile (diameter = 2× this), cap at desktop */
45
+ --oval-w: min(45vmin, ${OVAL_W}px);
46
+ --oval-h: min(60vmin, ${OVAL_H}px);
47
+ /* Half-height of the visible oval for positioning (oval uses full w/h as radii) */
48
+ --oval-half-h: var(--oval-h);
43
49
  }
44
50
 
45
51
  /* ── Full-bleed video behind everything ─────────────────────────────── */
@@ -65,9 +71,9 @@ function createStyles() {
65
71
  .lv-overlay {
66
72
  position: absolute;
67
73
  inset: 0;
68
- /* The mask punches a transparent oval at 50% x, OVAL_TOP_PCT% y */
69
- --ow: min(72vw, ${OVAL_W}px);
70
- --oh: min(90vw, ${OVAL_H}px);
74
+ /* Ellipse size (radii): full oval w/h so cutout is large; ring is scaled to match */
75
+ --ow: var(--oval-w);
76
+ --oh: var(--oval-h);
71
77
  background: var(--lv-dark);
72
78
  -webkit-mask-image: radial-gradient(
73
79
  ellipse var(--ow) var(--oh) at 50% ${OVAL_TOP_PCT}%,
@@ -85,22 +91,17 @@ function createStyles() {
85
91
  background: rgba(180,0,0,0.55);
86
92
  }
87
93
 
88
- /* ── Oval SVG ring ───────────────────────────────────────────────────── */
94
+ /* ── Oval SVG ring (2× so progress sits on the main oval boundary) ───── */
89
95
  .lv-ring-wrap {
90
96
  position: absolute;
91
97
  left: 50%;
92
98
  top: ${OVAL_TOP_PCT}%;
93
99
  transform: translate(-50%, -50%);
94
- width: min(72vw, ${OVAL_W}px);
95
- height: min(90vw, ${OVAL_H}px);
100
+ width: calc(2 * var(--oval-w));
101
+ height: calc(2 * var(--oval-h));
96
102
  pointer-events: none;
97
103
  }
98
104
  .lv-ring-wrap svg { width: 100%; height: 100%; overflow: visible; }
99
- .lv-ring-track {
100
- fill: none;
101
- stroke: rgba(255,255,255,0.15);
102
- stroke-width: 3.5;
103
- }
104
105
  .lv-ring-progress {
105
106
  fill: none;
106
107
  stroke: var(--lv-green);
@@ -134,7 +135,7 @@ function createStyles() {
134
135
  .lv-dots {
135
136
  position: absolute;
136
137
  z-index: 2;
137
- top: calc(${OVAL_TOP_PCT}% + min(45vw, ${OVAL_H / 2}px) + 20px);
138
+ top: calc(${OVAL_TOP_PCT}% + var(--oval-half-h) + 20px);
138
139
  left: 50%;
139
140
  transform: translateX(-50%);
140
141
  display: flex;
@@ -152,7 +153,7 @@ function createStyles() {
152
153
  .lv-instruction {
153
154
  position: absolute;
154
155
  z-index: 2;
155
- top: calc(${OVAL_TOP_PCT}% + min(45vw, ${OVAL_H / 2}px) + 52px);
156
+ top: calc(${OVAL_TOP_PCT}% + var(--oval-half-h) + 52px);
156
157
  left: 50%;
157
158
  transform: translateX(-50%);
158
159
  white-space: nowrap;
@@ -168,7 +169,7 @@ function createStyles() {
168
169
  .lv-pos-hint {
169
170
  position: absolute;
170
171
  z-index: 2;
171
- top: calc(${OVAL_TOP_PCT}% + min(45vw, ${OVAL_H / 2}px) + 84px);
172
+ top: calc(${OVAL_TOP_PCT}% + var(--oval-half-h) + 84px);
172
173
  left: 50%;
173
174
  transform: translateX(-50%);
174
175
  font-size: 13px;
@@ -278,9 +279,6 @@ export function startLivenessWithUI(options) {
278
279
  ringWrap.className = "lv-ring-wrap";
279
280
  ringWrap.innerHTML = `
280
281
  <svg viewBox="0 0 ${OVAL_W} ${OVAL_H}">
281
- <ellipse class="lv-ring-track"
282
- cx="${rx}" cy="${ry}" rx="${RX}" ry="${RY}"
283
- pathLength="${ELLIPSE_PERIMETER.toFixed(1)}"/>
284
282
  <ellipse class="lv-ring-progress"
285
283
  cx="${rx}" cy="${ry}" rx="${RX}" ry="${RY}"
286
284
  pathLength="${ELLIPSE_PERIMETER.toFixed(1)}"
@@ -295,10 +293,10 @@ export function startLivenessWithUI(options) {
295
293
  hintIcon.setAttribute("aria-hidden", "true");
296
294
  root.appendChild(hintIcon);
297
295
  // ── Header ─────────────────────────────────────────────────────────────────
298
- const header = document.createElement("div");
299
- header.className = "lv-header";
300
- header.innerHTML = `<span class="lv-header-title">Face Verification</span>`;
301
- root.appendChild(header);
296
+ // const header = document.createElement("div");
297
+ // header.className = "lv-header";
298
+ // header.innerHTML = `<span class="lv-header-title">Face Verification</span>`;
299
+ // root.appendChild(header);
302
300
  // ── Step dots ──────────────────────────────────────────────────────────────
303
301
  const dotsEl = document.createElement("div");
304
302
  dotsEl.className = "lv-dots";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@daboss2003/liveness-web",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "Web liveness detection using MediaPipe Face Landmarker (CDN)",
5
5
  "keywords": [
6
6
  "liveness",
package/src/ui.ts CHANGED
@@ -14,9 +14,10 @@ const OVAL_W = 270;
14
14
  const OVAL_H = 360;
15
15
  const OVAL_TOP_PCT = 40;
16
16
 
17
- // Approximate ellipse perimeter via Ramanujan's formula so pathLength matches
18
- const RX = OVAL_W / 2 - 2;
19
- const RY = OVAL_H / 2 - 2;
17
+ // Progress stroke sits on the main oval: ellipse inset by half stroke so outer edge aligns with cutout
18
+ const STROKE_HALF = 1.75;
19
+ const RX = OVAL_W / 2 - STROKE_HALF;
20
+ const RY = OVAL_H / 2 - STROKE_HALF;
20
21
  const ELLIPSE_PERIMETER = Math.PI * (3 * (RX + RY) - Math.sqrt((3 * RX + RY) * (RX + 3 * RY)));
21
22
 
22
23
  type HintKind = "left" | "blink" | "right" | "nod" | "mouth";
@@ -55,6 +56,11 @@ function createStyles(): HTMLStyleElement {
55
56
  display: flex;
56
57
  flex-direction: column;
57
58
  align-items: center;
59
+ /* Radii: keep oval inside viewport on mobile (diameter = 2× this), cap at desktop */
60
+ --oval-w: min(45vmin, ${OVAL_W}px);
61
+ --oval-h: min(60vmin, ${OVAL_H}px);
62
+ /* Half-height of the visible oval for positioning (oval uses full w/h as radii) */
63
+ --oval-half-h: var(--oval-h);
58
64
  }
59
65
 
60
66
  /* ── Full-bleed video behind everything ─────────────────────────────── */
@@ -80,9 +86,9 @@ function createStyles(): HTMLStyleElement {
80
86
  .lv-overlay {
81
87
  position: absolute;
82
88
  inset: 0;
83
- /* The mask punches a transparent oval at 50% x, OVAL_TOP_PCT% y */
84
- --ow: min(72vw, ${OVAL_W}px);
85
- --oh: min(90vw, ${OVAL_H}px);
89
+ /* Ellipse size (radii): full oval w/h so cutout is large; ring is scaled to match */
90
+ --ow: var(--oval-w);
91
+ --oh: var(--oval-h);
86
92
  background: var(--lv-dark);
87
93
  -webkit-mask-image: radial-gradient(
88
94
  ellipse var(--ow) var(--oh) at 50% ${OVAL_TOP_PCT}%,
@@ -100,22 +106,17 @@ function createStyles(): HTMLStyleElement {
100
106
  background: rgba(180,0,0,0.55);
101
107
  }
102
108
 
103
- /* ── Oval SVG ring ───────────────────────────────────────────────────── */
109
+ /* ── Oval SVG ring (2× so progress sits on the main oval boundary) ───── */
104
110
  .lv-ring-wrap {
105
111
  position: absolute;
106
112
  left: 50%;
107
113
  top: ${OVAL_TOP_PCT}%;
108
114
  transform: translate(-50%, -50%);
109
- width: min(72vw, ${OVAL_W}px);
110
- height: min(90vw, ${OVAL_H}px);
115
+ width: calc(2 * var(--oval-w));
116
+ height: calc(2 * var(--oval-h));
111
117
  pointer-events: none;
112
118
  }
113
119
  .lv-ring-wrap svg { width: 100%; height: 100%; overflow: visible; }
114
- .lv-ring-track {
115
- fill: none;
116
- stroke: rgba(255,255,255,0.15);
117
- stroke-width: 3.5;
118
- }
119
120
  .lv-ring-progress {
120
121
  fill: none;
121
122
  stroke: var(--lv-green);
@@ -149,7 +150,7 @@ function createStyles(): HTMLStyleElement {
149
150
  .lv-dots {
150
151
  position: absolute;
151
152
  z-index: 2;
152
- top: calc(${OVAL_TOP_PCT}% + min(45vw, ${OVAL_H / 2}px) + 20px);
153
+ top: calc(${OVAL_TOP_PCT}% + var(--oval-half-h) + 20px);
153
154
  left: 50%;
154
155
  transform: translateX(-50%);
155
156
  display: flex;
@@ -167,7 +168,7 @@ function createStyles(): HTMLStyleElement {
167
168
  .lv-instruction {
168
169
  position: absolute;
169
170
  z-index: 2;
170
- top: calc(${OVAL_TOP_PCT}% + min(45vw, ${OVAL_H / 2}px) + 52px);
171
+ top: calc(${OVAL_TOP_PCT}% + var(--oval-half-h) + 52px);
171
172
  left: 50%;
172
173
  transform: translateX(-50%);
173
174
  white-space: nowrap;
@@ -183,7 +184,7 @@ function createStyles(): HTMLStyleElement {
183
184
  .lv-pos-hint {
184
185
  position: absolute;
185
186
  z-index: 2;
186
- top: calc(${OVAL_TOP_PCT}% + min(45vw, ${OVAL_H / 2}px) + 84px);
187
+ top: calc(${OVAL_TOP_PCT}% + var(--oval-half-h) + 84px);
187
188
  left: 50%;
188
189
  transform: translateX(-50%);
189
190
  font-size: 13px;
@@ -301,9 +302,6 @@ export function startLivenessWithUI(options: StartLivenessOptions): LivenessEngi
301
302
  ringWrap.className = "lv-ring-wrap";
302
303
  ringWrap.innerHTML = `
303
304
  <svg viewBox="0 0 ${OVAL_W} ${OVAL_H}">
304
- <ellipse class="lv-ring-track"
305
- cx="${rx}" cy="${ry}" rx="${RX}" ry="${RY}"
306
- pathLength="${ELLIPSE_PERIMETER.toFixed(1)}"/>
307
305
  <ellipse class="lv-ring-progress"
308
306
  cx="${rx}" cy="${ry}" rx="${RX}" ry="${RY}"
309
307
  pathLength="${ELLIPSE_PERIMETER.toFixed(1)}"
@@ -320,10 +318,10 @@ export function startLivenessWithUI(options: StartLivenessOptions): LivenessEngi
320
318
  root.appendChild(hintIcon);
321
319
 
322
320
  // ── Header ─────────────────────────────────────────────────────────────────
323
- const header = document.createElement("div");
324
- header.className = "lv-header";
325
- header.innerHTML = `<span class="lv-header-title">Face Verification</span>`;
326
- root.appendChild(header);
321
+ // const header = document.createElement("div");
322
+ // header.className = "lv-header";
323
+ // header.innerHTML = `<span class="lv-header-title">Face Verification</span>`;
324
+ // root.appendChild(header);
327
325
 
328
326
  // ── Step dots ──────────────────────────────────────────────────────────────
329
327
  const dotsEl = document.createElement("div");