@daboss2003/liveness-web 1.0.0 → 1.0.1

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 +21 -24
  2. package/package.json +1 -1
  3. package/src/ui.ts +21 -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,10 @@ function createStyles() {
40
41
  display: flex;
41
42
  flex-direction: column;
42
43
  align-items: center;
44
+ --oval-w: min(72vmin, ${OVAL_W}px);
45
+ --oval-h: min(96vmin, ${OVAL_H}px);
46
+ /* Half-height of the visible oval for positioning (oval uses full w/h as radii) */
47
+ --oval-half-h: var(--oval-h);
43
48
  }
44
49
 
45
50
  /* ── Full-bleed video behind everything ─────────────────────────────── */
@@ -65,9 +70,9 @@ function createStyles() {
65
70
  .lv-overlay {
66
71
  position: absolute;
67
72
  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);
73
+ /* Ellipse size (radii): full oval w/h so cutout is large; ring is scaled to match */
74
+ --ow: var(--oval-w);
75
+ --oh: var(--oval-h);
71
76
  background: var(--lv-dark);
72
77
  -webkit-mask-image: radial-gradient(
73
78
  ellipse var(--ow) var(--oh) at 50% ${OVAL_TOP_PCT}%,
@@ -85,22 +90,17 @@ function createStyles() {
85
90
  background: rgba(180,0,0,0.55);
86
91
  }
87
92
 
88
- /* ── Oval SVG ring ───────────────────────────────────────────────────── */
93
+ /* ── Oval SVG ring (2× so progress sits on the main oval boundary) ───── */
89
94
  .lv-ring-wrap {
90
95
  position: absolute;
91
96
  left: 50%;
92
97
  top: ${OVAL_TOP_PCT}%;
93
98
  transform: translate(-50%, -50%);
94
- width: min(72vw, ${OVAL_W}px);
95
- height: min(90vw, ${OVAL_H}px);
99
+ width: calc(2 * var(--oval-w));
100
+ height: calc(2 * var(--oval-h));
96
101
  pointer-events: none;
97
102
  }
98
103
  .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
104
  .lv-ring-progress {
105
105
  fill: none;
106
106
  stroke: var(--lv-green);
@@ -134,7 +134,7 @@ function createStyles() {
134
134
  .lv-dots {
135
135
  position: absolute;
136
136
  z-index: 2;
137
- top: calc(${OVAL_TOP_PCT}% + min(45vw, ${OVAL_H / 2}px) + 20px);
137
+ top: calc(${OVAL_TOP_PCT}% + var(--oval-half-h) + 20px);
138
138
  left: 50%;
139
139
  transform: translateX(-50%);
140
140
  display: flex;
@@ -152,7 +152,7 @@ function createStyles() {
152
152
  .lv-instruction {
153
153
  position: absolute;
154
154
  z-index: 2;
155
- top: calc(${OVAL_TOP_PCT}% + min(45vw, ${OVAL_H / 2}px) + 52px);
155
+ top: calc(${OVAL_TOP_PCT}% + var(--oval-half-h) + 52px);
156
156
  left: 50%;
157
157
  transform: translateX(-50%);
158
158
  white-space: nowrap;
@@ -168,7 +168,7 @@ function createStyles() {
168
168
  .lv-pos-hint {
169
169
  position: absolute;
170
170
  z-index: 2;
171
- top: calc(${OVAL_TOP_PCT}% + min(45vw, ${OVAL_H / 2}px) + 84px);
171
+ top: calc(${OVAL_TOP_PCT}% + var(--oval-half-h) + 84px);
172
172
  left: 50%;
173
173
  transform: translateX(-50%);
174
174
  font-size: 13px;
@@ -278,9 +278,6 @@ export function startLivenessWithUI(options) {
278
278
  ringWrap.className = "lv-ring-wrap";
279
279
  ringWrap.innerHTML = `
280
280
  <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
281
  <ellipse class="lv-ring-progress"
285
282
  cx="${rx}" cy="${ry}" rx="${RX}" ry="${RY}"
286
283
  pathLength="${ELLIPSE_PERIMETER.toFixed(1)}"
@@ -295,10 +292,10 @@ export function startLivenessWithUI(options) {
295
292
  hintIcon.setAttribute("aria-hidden", "true");
296
293
  root.appendChild(hintIcon);
297
294
  // ── 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);
295
+ // const header = document.createElement("div");
296
+ // header.className = "lv-header";
297
+ // header.innerHTML = `<span class="lv-header-title">Face Verification</span>`;
298
+ // root.appendChild(header);
302
299
  // ── Step dots ──────────────────────────────────────────────────────────────
303
300
  const dotsEl = document.createElement("div");
304
301
  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.1",
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,10 @@ function createStyles(): HTMLStyleElement {
55
56
  display: flex;
56
57
  flex-direction: column;
57
58
  align-items: center;
59
+ --oval-w: min(72vmin, ${OVAL_W}px);
60
+ --oval-h: min(96vmin, ${OVAL_H}px);
61
+ /* Half-height of the visible oval for positioning (oval uses full w/h as radii) */
62
+ --oval-half-h: var(--oval-h);
58
63
  }
59
64
 
60
65
  /* ── Full-bleed video behind everything ─────────────────────────────── */
@@ -80,9 +85,9 @@ function createStyles(): HTMLStyleElement {
80
85
  .lv-overlay {
81
86
  position: absolute;
82
87
  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);
88
+ /* Ellipse size (radii): full oval w/h so cutout is large; ring is scaled to match */
89
+ --ow: var(--oval-w);
90
+ --oh: var(--oval-h);
86
91
  background: var(--lv-dark);
87
92
  -webkit-mask-image: radial-gradient(
88
93
  ellipse var(--ow) var(--oh) at 50% ${OVAL_TOP_PCT}%,
@@ -100,22 +105,17 @@ function createStyles(): HTMLStyleElement {
100
105
  background: rgba(180,0,0,0.55);
101
106
  }
102
107
 
103
- /* ── Oval SVG ring ───────────────────────────────────────────────────── */
108
+ /* ── Oval SVG ring (2× so progress sits on the main oval boundary) ───── */
104
109
  .lv-ring-wrap {
105
110
  position: absolute;
106
111
  left: 50%;
107
112
  top: ${OVAL_TOP_PCT}%;
108
113
  transform: translate(-50%, -50%);
109
- width: min(72vw, ${OVAL_W}px);
110
- height: min(90vw, ${OVAL_H}px);
114
+ width: calc(2 * var(--oval-w));
115
+ height: calc(2 * var(--oval-h));
111
116
  pointer-events: none;
112
117
  }
113
118
  .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
119
  .lv-ring-progress {
120
120
  fill: none;
121
121
  stroke: var(--lv-green);
@@ -149,7 +149,7 @@ function createStyles(): HTMLStyleElement {
149
149
  .lv-dots {
150
150
  position: absolute;
151
151
  z-index: 2;
152
- top: calc(${OVAL_TOP_PCT}% + min(45vw, ${OVAL_H / 2}px) + 20px);
152
+ top: calc(${OVAL_TOP_PCT}% + var(--oval-half-h) + 20px);
153
153
  left: 50%;
154
154
  transform: translateX(-50%);
155
155
  display: flex;
@@ -167,7 +167,7 @@ function createStyles(): HTMLStyleElement {
167
167
  .lv-instruction {
168
168
  position: absolute;
169
169
  z-index: 2;
170
- top: calc(${OVAL_TOP_PCT}% + min(45vw, ${OVAL_H / 2}px) + 52px);
170
+ top: calc(${OVAL_TOP_PCT}% + var(--oval-half-h) + 52px);
171
171
  left: 50%;
172
172
  transform: translateX(-50%);
173
173
  white-space: nowrap;
@@ -183,7 +183,7 @@ function createStyles(): HTMLStyleElement {
183
183
  .lv-pos-hint {
184
184
  position: absolute;
185
185
  z-index: 2;
186
- top: calc(${OVAL_TOP_PCT}% + min(45vw, ${OVAL_H / 2}px) + 84px);
186
+ top: calc(${OVAL_TOP_PCT}% + var(--oval-half-h) + 84px);
187
187
  left: 50%;
188
188
  transform: translateX(-50%);
189
189
  font-size: 13px;
@@ -301,9 +301,6 @@ export function startLivenessWithUI(options: StartLivenessOptions): LivenessEngi
301
301
  ringWrap.className = "lv-ring-wrap";
302
302
  ringWrap.innerHTML = `
303
303
  <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
304
  <ellipse class="lv-ring-progress"
308
305
  cx="${rx}" cy="${ry}" rx="${RX}" ry="${RY}"
309
306
  pathLength="${ELLIPSE_PERIMETER.toFixed(1)}"
@@ -320,10 +317,10 @@ export function startLivenessWithUI(options: StartLivenessOptions): LivenessEngi
320
317
  root.appendChild(hintIcon);
321
318
 
322
319
  // ── 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);
320
+ // const header = document.createElement("div");
321
+ // header.className = "lv-header";
322
+ // header.innerHTML = `<span class="lv-header-title">Face Verification</span>`;
323
+ // root.appendChild(header);
327
324
 
328
325
  // ── Step dots ──────────────────────────────────────────────────────────────
329
326
  const dotsEl = document.createElement("div");