@souldi/try-on 0.4.4 → 0.5.0
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.
- package/CHANGELOG.md +12 -0
- package/README.md +23 -34
- package/dist/mannequin-loader.mjs +114 -169
- package/dist/widget.mjs +1 -1
- package/dist/widget.umd.js +1 -1
- package/package.json +3 -2
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.5.0
|
|
4
|
+
|
|
5
|
+
**Breaking.** `init({ appearance })` replaces the `theme` option.
|
|
6
|
+
|
|
7
|
+
- **New: `appearance`.** `init({ appearance: { ink, fill, corners, font } })` styles the try-on button, the result dialog and the sign-in and upload screens. It is the same closed set of values a Shopify merchant sets on the Souldi block. Missing or unknown values fall back to the defaults, one by one, and never throw. See [Appearance](README.md#appearance).
|
|
8
|
+
- **Removed: `theme`.** Its properties (`mode`, `accentColor`, `accentTextColor`, `borderRadius`, `fontFamily`, `headingFontFamily`, `buttonOutlineColor`) are gone, not deprecated. A `theme` that is still passed is ignored and logs one console warning.
|
|
9
|
+
- **Removed: dark mode.** The widget is light-only, like its result dialog. An embedder who passed `theme: { mode: 'dark' }` now gets the light sign-in and upload screens.
|
|
10
|
+
- **Contrast is reported, never corrected.** The `ink` is used as given. If it is below 4.5:1 against white, the widget logs one console warning.
|
|
11
|
+
|
|
12
|
+
**CDN embedders get this on the day it is published.** The README's script tag, `https://unpkg.com/@souldi/try-on/dist/widget.umd.js`, names no version, so it serves the newest release. To stay on 0.4 while you move from `theme` to `appearance`, pin the version in the URL (`https://unpkg.com/@souldi/try-on@0.4/dist/widget.umd.js`) or in your `package.json`.
|
package/README.md
CHANGED
|
@@ -97,7 +97,7 @@ Call once per page load to configure the widget.
|
|
|
97
97
|
| Option | Type | Required | Description |
|
|
98
98
|
|--------|------|----------|-------------|
|
|
99
99
|
| `tenantApiKey` | `string` | Yes | Your publishable API key (from your Souldi dashboard) |
|
|
100
|
-
| `
|
|
100
|
+
| `appearance` | `object` | No | How the widget looks: ink, fill, corners and font ([see Appearance](#appearance)) |
|
|
101
101
|
| `fontsUrl` | `string` | No | Where you serve the package's `dist/fonts` directory, to load the fonts from your own origin instead of unpkg ([see Fonts](#fonts)) |
|
|
102
102
|
| `declareFonts` | `boolean` | No | Default `true`: the widget declares its fonts in your page ([see Fonts](#fonts)). Pass `false` only if your page already declares them |
|
|
103
103
|
| `onGenerationStart` | `function(info)` | No | Called when AI generation begins |
|
|
@@ -222,51 +222,40 @@ It returns a handle for that button. Among its methods:
|
|
|
222
222
|
|
|
223
223
|
---
|
|
224
224
|
|
|
225
|
-
##
|
|
225
|
+
## Appearance
|
|
226
226
|
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
### Theme Modes
|
|
227
|
+
One **appearance** styles the whole widget: the try-on button, the result dialog, and the sign-in and upload screens. It is the same closed set of choices a Shopify merchant makes on the Souldi block, so every embed has one appearance contract.
|
|
230
228
|
|
|
231
229
|
```javascript
|
|
232
230
|
VirtualTryOn.init({
|
|
233
231
|
tenantApiKey: 'your_key',
|
|
234
|
-
|
|
235
|
-
|
|
232
|
+
appearance: {
|
|
233
|
+
ink: '#7A1F5C', // your brand colour, as six hex digits
|
|
234
|
+
fill: 'filled', // 'outlined' (default) or 'filled'
|
|
235
|
+
corners: 'rounded', // 'square' (default), 'rounded' or 'pill'
|
|
236
|
+
font: 'theme', // 'souldi' (default) or 'theme'
|
|
236
237
|
},
|
|
237
238
|
});
|
|
238
239
|
```
|
|
239
240
|
|
|
240
|
-
|
|
241
|
+
| Property | Values | Default | What it does |
|
|
242
|
+
|----------|--------|---------|--------------|
|
|
243
|
+
| `ink` | `#rrggbb` | `#1F201F` | Colours the accents only: the primary action, selected states, the size stamp and focus rings. Never body text. |
|
|
244
|
+
| `fill` | `'outlined'` \| `'filled'` | `'outlined'` | The primary action is the ink on white (outlined) or white on the ink (filled). Secondary actions stay outlined. |
|
|
245
|
+
| `corners` | `'square'` \| `'rounded'` \| `'pill'` | `'square'` | Buttons take the shape as chosen. Cards and sheets cap it (0, 12px and 20px), so a dialog never turns into an oval. |
|
|
246
|
+
| `font` | `'souldi'` \| `'theme'` | `'souldi'` | The widget's own faces ([see Fonts](#fonts)), or your page's font, inherited everywhere, headings included. |
|
|
241
247
|
|
|
242
|
-
|
|
248
|
+
Each value is checked on its own. A missing or unknown value falls back to its default and never throws, so a typo can't break your page. An `ink` that isn't six hex digits (`#FFF`, `red`) falls back to the default ink.
|
|
243
249
|
|
|
244
|
-
|
|
245
|
-
VirtualTryOn.init({
|
|
246
|
-
tenantApiKey: 'your_key',
|
|
247
|
-
theme: {
|
|
248
|
-
mode: 'dark',
|
|
249
|
-
accentColor: '#7C3AED', // Primary button & accent color
|
|
250
|
-
accentTextColor: '#FFFFFF', // Text color on accent buttons
|
|
251
|
-
borderRadius: '8px', // Border radius for cards & modals
|
|
252
|
-
fontFamily: '"Inter", sans-serif',
|
|
253
|
-
headingFontFamily: '"Playfair Display", serif',
|
|
254
|
-
buttonOutlineColor: '#7C3AED', // Outline on the split button
|
|
255
|
-
},
|
|
256
|
-
});
|
|
257
|
-
```
|
|
250
|
+
**Pick an ink that reads on white.** The ink is used exactly as you give it. If it is below 4.5:1 contrast against white (the WCAG AA minimum for text), the widget logs one console warning and still uses it. It doesn't darken the colour for you.
|
|
258
251
|
|
|
259
|
-
|
|
252
|
+
There is no `size` option. One that is passed is ignored.
|
|
260
253
|
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
| `borderRadius` | CSS value | `12px` | Corner rounding for cards and modals |
|
|
267
|
-
| `fontFamily` | CSS font stack | `'Souldi Manrope', system-ui, sans-serif` | Body text font |
|
|
268
|
-
| `headingFontFamily` | CSS font stack | `'Souldi Newsreader', serif` | Heading font |
|
|
269
|
-
| `buttonOutlineColor` | CSS color | `transparent` | Border color on the try-on button |
|
|
254
|
+
> **Removed in 0.5.0:** `theme` (`mode`, `accentColor`, `accentTextColor`,
|
|
255
|
+
> `borderRadius`, `fontFamily`, `headingFontFamily`, `buttonOutlineColor`) and
|
|
256
|
+
> the dark mode it selected. The widget is light-only. A `theme` that is still
|
|
257
|
+
> passed is ignored and logs one console warning. Move to `appearance`. See the
|
|
258
|
+
> [changelog](CHANGELOG.md).
|
|
270
259
|
|
|
271
260
|
All UI is rendered inside a **Shadow DOM**, so your site's CSS will never leak into the widget and the widget will never break your styles.
|
|
272
261
|
|
|
@@ -309,7 +298,7 @@ The widget then contacts no one but you for its fonts, and a `font-src 'self'` p
|
|
|
309
298
|
<script>
|
|
310
299
|
VirtualTryOn.init({
|
|
311
300
|
tenantApiKey: 'pk_live_abc123',
|
|
312
|
-
|
|
301
|
+
appearance: { ink: '#2563EB', corners: 'rounded' },
|
|
313
302
|
onGenerationSuccess: (url) => {
|
|
314
303
|
console.log('Try-on result:', url);
|
|
315
304
|
},
|
|
@@ -10,88 +10,33 @@ var e = Object.freeze([
|
|
|
10
10
|
]), t = Object.freeze(["height", ...e]), n = Object.freeze(/* @__PURE__ */ new Set(["torso_length"]));
|
|
11
11
|
Object.freeze(Object.fromEntries(t.filter((e) => !n.has(e)).map((e) => [`declared_${e}_cm`, e])));
|
|
12
12
|
//#endregion
|
|
13
|
-
//#region src/fonts.mjs
|
|
14
|
-
var r = "Souldi Manrope", i = "Souldi Newsreader", a = {
|
|
15
|
-
"cyrillic-ext": "U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F",
|
|
16
|
-
cyrillic: "U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116",
|
|
17
|
-
greek: "U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF",
|
|
18
|
-
vietnamese: "U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB",
|
|
19
|
-
"latin-ext": "U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C4, U+2113, U+2C60-2C7F, U+A720-A7FF",
|
|
20
|
-
latin: "U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD"
|
|
21
|
-
};
|
|
22
|
-
function o({ family: e, style: t, weight: n, stem: r, subsets: i }) {
|
|
23
|
-
return i.map((i) => ({
|
|
24
|
-
family: e,
|
|
25
|
-
style: t,
|
|
26
|
-
weight: n,
|
|
27
|
-
display: "swap",
|
|
28
|
-
file: `${r}-${i}.woff2`,
|
|
29
|
-
unicodeRange: a[i]
|
|
30
|
-
}));
|
|
31
|
-
}
|
|
32
|
-
var s = [
|
|
33
|
-
"cyrillic-ext",
|
|
34
|
-
"cyrillic",
|
|
35
|
-
"greek",
|
|
36
|
-
"vietnamese",
|
|
37
|
-
"latin-ext",
|
|
38
|
-
"latin"
|
|
39
|
-
], c = [
|
|
40
|
-
"vietnamese",
|
|
41
|
-
"latin-ext",
|
|
42
|
-
"latin"
|
|
43
|
-
];
|
|
44
|
-
Object.freeze([
|
|
45
|
-
...o({
|
|
46
|
-
family: r,
|
|
47
|
-
style: "normal",
|
|
48
|
-
weight: "300 800",
|
|
49
|
-
stem: "manrope",
|
|
50
|
-
subsets: s
|
|
51
|
-
}),
|
|
52
|
-
...o({
|
|
53
|
-
family: i,
|
|
54
|
-
style: "normal",
|
|
55
|
-
weight: "300 600",
|
|
56
|
-
stem: "newsreader",
|
|
57
|
-
subsets: c
|
|
58
|
-
}),
|
|
59
|
-
...o({
|
|
60
|
-
family: i,
|
|
61
|
-
style: "italic",
|
|
62
|
-
weight: "300 600",
|
|
63
|
-
stem: "newsreader-italic",
|
|
64
|
-
subsets: c
|
|
65
|
-
})
|
|
66
|
-
]);
|
|
67
|
-
//#endregion
|
|
68
13
|
//#region src/mannequin3d.js
|
|
69
|
-
async function
|
|
70
|
-
let [
|
|
14
|
+
async function r(t, n, r = []) {
|
|
15
|
+
let [i, { GLTFLoader: a }] = await Promise.all([import("./mannequin-chunks/three.module-BTc07Glh.mjs"), import("./mannequin-chunks/GLTFLoader-D8TDDHUI.mjs")]), o = !1, s = new i.Scene(), c = t.clientWidth || 300, l = t.clientHeight || 260, u = new i.WebGLRenderer({
|
|
71
16
|
antialias: !0,
|
|
72
17
|
alpha: !0
|
|
73
18
|
});
|
|
74
|
-
|
|
75
|
-
let
|
|
76
|
-
|
|
77
|
-
let
|
|
78
|
-
|
|
79
|
-
let
|
|
19
|
+
u.setPixelRatio(Math.min(2, window.devicePixelRatio || 1)), u.setSize(c, l), u.outputColorSpace = i.SRGBColorSpace, u.domElement.style.cssText = "position:absolute;inset:0;z-index:1;", t.appendChild(u.domElement), s.add(new i.HemisphereLight(16777215, 12170412, 1.5));
|
|
20
|
+
let d = new i.DirectionalLight(16777215, 1.6);
|
|
21
|
+
d.position.set(2, 4, 5), s.add(d);
|
|
22
|
+
let f = new i.DirectionalLight(16777215, .7);
|
|
23
|
+
f.position.set(-3, 2, -4), s.add(f);
|
|
24
|
+
let p = new i.MeshStandardMaterial({
|
|
80
25
|
color: 16053489,
|
|
81
26
|
roughness: .45,
|
|
82
27
|
metalness: 0
|
|
83
|
-
}),
|
|
84
|
-
if (
|
|
85
|
-
let
|
|
86
|
-
|
|
87
|
-
e.isMesh && (e.material =
|
|
88
|
-
}),
|
|
89
|
-
let
|
|
90
|
-
|
|
91
|
-
let b = new
|
|
92
|
-
|
|
93
|
-
let x = .01, S = [], C = new
|
|
94
|
-
|
|
28
|
+
}), m = [p], h = [], g = await new Promise((e, t) => new a().load(n, e, void 0, t));
|
|
29
|
+
if (o) return { destroy() {} };
|
|
30
|
+
let _ = g.scene;
|
|
31
|
+
_.traverse((e) => {
|
|
32
|
+
e.isMesh && (e.material = p, m.push(e.geometry));
|
|
33
|
+
}), s.add(_);
|
|
34
|
+
let v = new i.Box3().setFromObject(_), y = 1.68 / (v.max.y - v.min.y || 1);
|
|
35
|
+
_.scale.setScalar(y), _.updateMatrixWorld(!0);
|
|
36
|
+
let b = new i.Box3().setFromObject(_);
|
|
37
|
+
_.position.y -= b.min.y + .7, _.position.x -= (b.min.x + b.max.x) / 2, _.position.z -= (b.min.z + b.max.z) / 2, _.updateMatrixWorld(!0);
|
|
38
|
+
let x = .01, S = [], C = new i.Vector3();
|
|
39
|
+
_.traverse((e) => {
|
|
95
40
|
if (!e.isMesh) return;
|
|
96
41
|
let t = e.geometry.getAttribute("position"), n = Math.max(1, Math.floor(t.count / 8e4));
|
|
97
42
|
for (let r = 0; r < t.count; r += n) C.fromBufferAttribute(t, r), e.localToWorld(C), S.push(C.x, C.y, C.z);
|
|
@@ -116,73 +61,73 @@ async function l(t, n, i = []) {
|
|
|
116
61
|
}, D = (e) => {
|
|
117
62
|
let t = E(e, -.5, .5, .008);
|
|
118
63
|
return t ? t.w : 0;
|
|
119
|
-
},
|
|
120
|
-
for (let e = w - .12; e > w - .45; e -= .01) if (D(e) >
|
|
64
|
+
}, ee = Math.max(.08, D(w - .1)), O = w - .28;
|
|
65
|
+
for (let e = w - .12; e > w - .45; e -= .01) if (D(e) > ee * 1.9) {
|
|
121
66
|
O = e - .015;
|
|
122
67
|
break;
|
|
123
68
|
}
|
|
124
|
-
let
|
|
69
|
+
let k = O - .13, A = E(k, -.19, .19), j = {
|
|
125
70
|
shY: O,
|
|
126
71
|
shHalf: D(O) / 2,
|
|
127
|
-
chestY:
|
|
128
|
-
rx:
|
|
129
|
-
zc:
|
|
130
|
-
zF:
|
|
131
|
-
},
|
|
132
|
-
for (let e =
|
|
72
|
+
chestY: k,
|
|
73
|
+
rx: A ? A.w / 2 : .14,
|
|
74
|
+
zc: A ? A.cz : 0,
|
|
75
|
+
zF: A ? A.z1 : .1
|
|
76
|
+
}, M = null, N = null;
|
|
77
|
+
for (let e = j.shY - .28; e > j.shY - .44; e -= .015) {
|
|
133
78
|
let t = E(e, -.19, .19);
|
|
134
|
-
t && (!
|
|
79
|
+
t && (!M || t.w < M.t.w) && (M = {
|
|
135
80
|
y: e,
|
|
136
81
|
t
|
|
137
82
|
});
|
|
138
83
|
}
|
|
139
|
-
for (let e =
|
|
84
|
+
for (let e = j.shY - .42; e > j.shY - .54; e -= .02) {
|
|
140
85
|
let t = E(e, -.165, .165);
|
|
141
|
-
t && (!
|
|
86
|
+
t && (!N || t.w > N.t.w) && (N = {
|
|
142
87
|
y: e,
|
|
143
88
|
t
|
|
144
89
|
});
|
|
145
90
|
}
|
|
146
|
-
|
|
147
|
-
let
|
|
148
|
-
|
|
149
|
-
let
|
|
150
|
-
|
|
151
|
-
let
|
|
152
|
-
let
|
|
153
|
-
color: r ||
|
|
91
|
+
j.waistRow = M, j.hipsRow = N, j.crotchY = N ? N.y - .12 : j.shY - .6;
|
|
92
|
+
let P = new i.PerspectiveCamera(30, c / l, .05, 100), F = O - .3;
|
|
93
|
+
P.position.set(0, F, 2.35), P.lookAt(0, F, 0);
|
|
94
|
+
let I = new i.Group();
|
|
95
|
+
s.add(I);
|
|
96
|
+
let L = "#5F7F63", R = (e, t, n) => new i.Vector3(e, t, n), z = {}, B = (e, t, n, r) => {
|
|
97
|
+
let a = new i.CatmullRomCurve3(e, t, "catmullrom", .5), o = new i.TubeGeometry(a, t ? 90 : 40, n, 8, t), s = new i.TubeGeometry(a, t ? 90 : 40, n * 2.2, 8, t), c = new i.MeshBasicMaterial({ color: r || L }), l = new i.MeshBasicMaterial({
|
|
98
|
+
color: r || L,
|
|
154
99
|
transparent: !0,
|
|
155
100
|
opacity: .15,
|
|
156
101
|
depthWrite: !1
|
|
157
102
|
});
|
|
158
|
-
|
|
159
|
-
},
|
|
160
|
-
let n = (
|
|
103
|
+
h.push(o, s, c, l), I.add(new i.Mesh(o, c)), I.add(new i.Mesh(s, l));
|
|
104
|
+
}, V = (e, t, n) => Math.max(.4, (e + n / (2 * Math.PI) * x) / (e + t)), te = (e, t, n) => Math.max(.4, (e + n / 2 * x) / (e + t)), ne = (e, t) => {
|
|
105
|
+
let n = (j.rx + .012) * e, r = n * .72, i = j.zF - j.zc - r * .4, a = [];
|
|
161
106
|
for (let e = 0; e < 36; e++) {
|
|
162
107
|
let t = e / 36 * Math.PI * 2, o = Math.sin(t);
|
|
163
|
-
a.push(
|
|
108
|
+
a.push(R(n * Math.cos(t), j.chestY - .012 * Math.max(0, o), j.zc + r * o + Math.max(0, i) * Math.max(0, o) * Math.max(0, o)));
|
|
164
109
|
}
|
|
165
|
-
|
|
166
|
-
},
|
|
167
|
-
let n = (
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
], !1, .65 * x, t),
|
|
175
|
-
},
|
|
110
|
+
B(a, !0, .7 * x, t), z.chest = R(-(n + x), j.chestY, j.zc);
|
|
111
|
+
}, re = (e, t) => {
|
|
112
|
+
let n = (j.shHalf - .015) * e, r = E(j.shY, -.25, .25), i = r ? r.cz : j.zc;
|
|
113
|
+
B([
|
|
114
|
+
R(-(n + x), j.shY - .5 * x, i + 2 * x),
|
|
115
|
+
R(-n * .5, j.shY + 3.5 * x, i + 5 * x),
|
|
116
|
+
R(0, j.shY + 4.5 * x, i + 6 * x),
|
|
117
|
+
R(n * .5, j.shY + 3.5 * x, i + 5 * x),
|
|
118
|
+
R(n + x, j.shY - .5 * x, i + 2 * x)
|
|
119
|
+
], !1, .65 * x, t), z.shoulder_width = R(-(n + 1.5 * x), j.shY - .5 * x, i + 2 * x);
|
|
120
|
+
}, H = (e, t, n, r) => {
|
|
176
121
|
if (!e) return;
|
|
177
122
|
let i = (e.t.w / 2 + .008) * t, a = Math.min((e.t.z1 - e.t.z0) / 2 + .008, i * .78) * t, o = [];
|
|
178
123
|
for (let t = 0; t < 36; t++) {
|
|
179
124
|
let n = t / 36 * Math.PI * 2;
|
|
180
|
-
o.push(
|
|
125
|
+
o.push(R(e.t.cx + i * Math.cos(n), e.y, e.t.cz + a * Math.sin(n)));
|
|
181
126
|
}
|
|
182
|
-
|
|
183
|
-
},
|
|
127
|
+
B(o, !0, .7 * x, r), z[n] = R(e.t.cx - i - x, e.y, e.t.cz);
|
|
128
|
+
}, ie = (e, t) => {
|
|
184
129
|
let n = [];
|
|
185
|
-
for (let e =
|
|
130
|
+
for (let e = j.shY - .05; e > j.shY - .25; e -= .015) {
|
|
186
131
|
let t = T(e, .012);
|
|
187
132
|
if (!t.length) continue;
|
|
188
133
|
let r = t[0];
|
|
@@ -199,79 +144,79 @@ async function l(t, n, i = []) {
|
|
|
199
144
|
n.forEach(([e, t, n]) => {
|
|
200
145
|
r += t, i += t * t, a += e, o += e * t, s += n, c += n * t;
|
|
201
146
|
});
|
|
202
|
-
let u = l * i - r * r, d = (l * o - a * r) / u, f = (a - d * r) / l, p = (l * c - s * r) / u, m = (s - p * r) / l, h =
|
|
147
|
+
let u = l * i - r * r, d = (l * o - a * r) / u, f = (a - d * r) / l, p = (l * c - s * r) / u, m = (s - p * r) / l, h = j.shY - .02, g = R(f + d * h, h, m + p * h), _ = R(d, 1, p).normalize();
|
|
203
148
|
_.y > 0 && _.multiplyScalar(-1);
|
|
204
149
|
let v = g.clone().addScaledVector(_, Math.max(e, 4) * x * 1.1), y = [];
|
|
205
150
|
for (let e = 0; e <= 6; e++) {
|
|
206
151
|
let t = g.clone().lerp(v, e / 6);
|
|
207
152
|
t.x += .8 * x, t.z += .5 * x, y.push(t);
|
|
208
153
|
}
|
|
209
|
-
|
|
210
|
-
},
|
|
211
|
-
if (!
|
|
212
|
-
let n =
|
|
154
|
+
B(y, !1, .7 * x, t), z.sleeve_length = y[y.length - 1].clone();
|
|
155
|
+
}, ae = (e, t) => {
|
|
156
|
+
if (!j.hipsRow) return;
|
|
157
|
+
let n = j.hipsRow.y - .1, r = n - Math.max(e, 4) * x * .9, i = [];
|
|
213
158
|
for (let e = 0; e <= 10; e++) {
|
|
214
|
-
let t = n + (r - n) * (e / 10), a = t >
|
|
215
|
-
i.push(
|
|
159
|
+
let t = n + (r - n) * (e / 10), a = t > j.crotchY ? E(t, -.165, .165) : E(t, .006, .16, .016);
|
|
160
|
+
i.push(R(a ? a.cx : 0, t, a ? a.z1 : j.zF));
|
|
216
161
|
}
|
|
217
|
-
|
|
218
|
-
},
|
|
219
|
-
let n =
|
|
162
|
+
B(i, !1, .65 * x, t), z.inseam = i[i.length - 1].clone();
|
|
163
|
+
}, oe = (e, t) => {
|
|
164
|
+
let n = j.shY - .02, r = n - Math.max(e, 4) * x * .9, i = [];
|
|
220
165
|
for (let e = 0; e <= 10; e++) {
|
|
221
166
|
let t = n + (r - n) * (e / 10), a = E(t, -.19, .19);
|
|
222
|
-
i.push(
|
|
167
|
+
i.push(R(a ? a.cx : 0, t, a ? a.z1 : j.zF));
|
|
223
168
|
}
|
|
224
|
-
|
|
225
|
-
},
|
|
226
|
-
for (;
|
|
227
|
-
let e =
|
|
169
|
+
B(i, !1, .65 * x, t), z.torso_length = i[i.length - 1].clone();
|
|
170
|
+
}, U = (e) => {
|
|
171
|
+
for (; I.children.length;) {
|
|
172
|
+
let e = I.children.pop();
|
|
228
173
|
e.geometry.dispose(), e.material.dispose();
|
|
229
174
|
}
|
|
230
|
-
|
|
231
|
-
for (let e in
|
|
175
|
+
h = [];
|
|
176
|
+
for (let e in z) delete z[e];
|
|
232
177
|
for (let t of e) {
|
|
233
|
-
let e = t.color ||
|
|
178
|
+
let e = t.color || L, n = W[t.key];
|
|
234
179
|
n && n(t.easeCm, e);
|
|
235
180
|
}
|
|
236
|
-
},
|
|
237
|
-
chest: (e, t) =>
|
|
181
|
+
}, W = {
|
|
182
|
+
chest: (e, t) => ne(V(j.rx, .012, e), t),
|
|
238
183
|
waist: (e, t) => {
|
|
239
|
-
|
|
184
|
+
j.waistRow && H(j.waistRow, V(j.waistRow.t.w / 2, .008, e), "waist", t);
|
|
240
185
|
},
|
|
241
186
|
hips: (e, t) => {
|
|
242
|
-
|
|
187
|
+
j.hipsRow && H(j.hipsRow, V(j.hipsRow.t.w / 2, .008, e), "hips", t);
|
|
243
188
|
},
|
|
244
|
-
shoulder_width: (e, t) =>
|
|
245
|
-
sleeve_length: (e, t) =>
|
|
246
|
-
inseam: (e, t) =>
|
|
247
|
-
torso_length: (e, t) =>
|
|
189
|
+
shoulder_width: (e, t) => re(te(j.shHalf, .015, e), t),
|
|
190
|
+
sleeve_length: (e, t) => ie(20 + e, t),
|
|
191
|
+
inseam: (e, t) => ae(60 + e, t),
|
|
192
|
+
torso_length: (e, t) => oe(65 + e, t)
|
|
248
193
|
};
|
|
249
|
-
for (let t of e)
|
|
250
|
-
|
|
251
|
-
let
|
|
252
|
-
|
|
253
|
-
let
|
|
254
|
-
|
|
255
|
-
let
|
|
256
|
-
|
|
194
|
+
for (let t of e) W[t] || console.warn(`[VirtualTryOn] mannequin has no band geometry for measurement "${t}"`);
|
|
195
|
+
U(r);
|
|
196
|
+
let G = document.createElement("div");
|
|
197
|
+
G.style.cssText = "position:absolute;inset:0;z-index:2;pointer-events:none;font-family:inherit;";
|
|
198
|
+
let K = "http://www.w3.org/2000/svg", q = document.createElementNS(K, "svg");
|
|
199
|
+
q.setAttribute("width", String(c)), q.setAttribute("height", String(l)), q.style.cssText = "position:absolute;inset:0;", G.appendChild(q), t.appendChild(G);
|
|
200
|
+
let J = r.filter((e) => z[e.key]), Y = {}, se = {}, X = {}, Z = {};
|
|
201
|
+
J.forEach((e) => {
|
|
257
202
|
let t = document.createElement("div");
|
|
258
203
|
t.style.cssText = "position:absolute;display:flex;align-items:center;gap:6px;background:rgba(255,255,255,0.94);border-radius:999px;padding:4px 10px 4px 5px;box-shadow:0 6px 20px rgba(28,28,22,0.12);white-space:nowrap;";
|
|
259
204
|
let n = document.createElement("span");
|
|
260
205
|
n.style.cssText = "width:16px;height:16px;border-radius:50%;background:#6F8F72;color:#FAF7ED;font-size:9.5px;font-weight:700;flex:none;display:inline-flex;align-items:center;justify-content:center;", n.textContent = e.letter;
|
|
261
206
|
let r = document.createElement("span");
|
|
262
|
-
r.style.cssText = `font-size:11px;font-weight:700;color:${e.color || "#5F7F63"};`, r.textContent = e.verdict || "", t.appendChild(n), t.appendChild(r),
|
|
263
|
-
let i = document.createElementNS(
|
|
264
|
-
i.setAttribute("stroke", "rgba(28,28,22,0.30)"), i.setAttribute("stroke-width", "1"),
|
|
265
|
-
let a = document.createElementNS(
|
|
266
|
-
a.setAttribute("r", "2.2"), a.setAttribute("fill", "#1c1c16"), a.setAttribute("fill-opacity", "0.55"),
|
|
207
|
+
r.style.cssText = `font-size:11px;font-weight:700;color:${e.color || "#5F7F63"};`, r.textContent = e.verdict || "", t.appendChild(n), t.appendChild(r), G.appendChild(t);
|
|
208
|
+
let i = document.createElementNS(K, "line");
|
|
209
|
+
i.setAttribute("stroke", "rgba(28,28,22,0.30)"), i.setAttribute("stroke-width", "1"), q.appendChild(i);
|
|
210
|
+
let a = document.createElementNS(K, "circle");
|
|
211
|
+
a.setAttribute("r", "2.2"), a.setAttribute("fill", "#1c1c16"), a.setAttribute("fill-opacity", "0.55"), q.appendChild(a), Y[e.key] = t, se[e.key] = r, X[e.key] = i, Z[e.key] = a;
|
|
267
212
|
});
|
|
268
|
-
let
|
|
213
|
+
let ce = () => {
|
|
269
214
|
let e = t.clientWidth, n = t.clientHeight;
|
|
270
215
|
if (!e || !n) return;
|
|
271
|
-
|
|
216
|
+
q.setAttribute("width", String(e)), q.setAttribute("height", String(n));
|
|
272
217
|
let r = {};
|
|
273
|
-
|
|
274
|
-
let i =
|
|
218
|
+
J.forEach((t) => {
|
|
219
|
+
let i = z[t.key].clone().project(P);
|
|
275
220
|
r[t.key] = {
|
|
276
221
|
x: (i.x * .5 + .5) * e,
|
|
277
222
|
y: (-i.y * .5 + .5) * n
|
|
@@ -281,41 +226,41 @@ async function l(t, n, i = []) {
|
|
|
281
226
|
left: [],
|
|
282
227
|
right: []
|
|
283
228
|
};
|
|
284
|
-
|
|
229
|
+
J.forEach((t) => (r[t.key].x < e / 2 ? i.left : i.right).push(t)), Object.entries(i).forEach(([t, i]) => {
|
|
285
230
|
i.sort((e, t) => r[e.key].y - r[t.key].y);
|
|
286
231
|
let a = 16;
|
|
287
232
|
i.forEach((i) => {
|
|
288
233
|
let o = r[i.key], s = Math.max(o.y, a);
|
|
289
234
|
s = Math.min(s, n - 24), a = s + 34;
|
|
290
|
-
let c =
|
|
235
|
+
let c = Y[i.key];
|
|
291
236
|
c.style.top = `${s - 13}px`, t === "left" ? (c.style.left = "10px", c.style.right = "auto") : (c.style.right = "10px", c.style.left = "auto");
|
|
292
237
|
let l = c.offsetWidth, u = t === "left" ? 10 + l + 3 : e - 10 - l - 3;
|
|
293
238
|
X[i.key].setAttribute("x1", String(u)), X[i.key].setAttribute("y1", String(s)), X[i.key].setAttribute("x2", String(o.x)), X[i.key].setAttribute("y2", String(o.y)), Z[i.key].setAttribute("cx", String(o.x)), Z[i.key].setAttribute("cy", String(o.y));
|
|
294
239
|
});
|
|
295
240
|
});
|
|
296
241
|
}, Q = () => {
|
|
297
|
-
|
|
242
|
+
o || (u.render(s, P), ce());
|
|
298
243
|
};
|
|
299
244
|
Q();
|
|
300
|
-
let
|
|
245
|
+
let le = new ResizeObserver(() => {
|
|
301
246
|
let e = t.clientWidth, n = t.clientHeight;
|
|
302
|
-
!e || !n || (
|
|
247
|
+
!e || !n || (u.setSize(e, n), P.aspect = e / n, P.updateProjectionMatrix(), Q());
|
|
303
248
|
});
|
|
304
|
-
|
|
305
|
-
let $ =
|
|
249
|
+
le.observe(t);
|
|
250
|
+
let $ = r.slice();
|
|
306
251
|
return {
|
|
307
252
|
destroy() {
|
|
308
|
-
|
|
253
|
+
o || (o = !0, le.disconnect(), m.forEach((e) => e.dispose()), h.forEach((e) => e.dispose()), u.dispose(), u.domElement.remove(), G.remove());
|
|
309
254
|
},
|
|
310
255
|
updateMany(e) {
|
|
311
|
-
if (!(
|
|
256
|
+
if (!(o || !e.length)) {
|
|
312
257
|
for (let t of e) {
|
|
313
258
|
let e = $.findIndex((e) => e.key === t.key);
|
|
314
259
|
e !== -1 && ($[e] = t);
|
|
315
260
|
}
|
|
316
|
-
|
|
261
|
+
U($);
|
|
317
262
|
for (let t of e) {
|
|
318
|
-
let e =
|
|
263
|
+
let e = se[t.key];
|
|
319
264
|
e && (e.textContent = t.verdict || "", e.style.color = t.color || "#5F7F63");
|
|
320
265
|
}
|
|
321
266
|
Q();
|
|
@@ -324,4 +269,4 @@ async function l(t, n, i = []) {
|
|
|
324
269
|
};
|
|
325
270
|
}
|
|
326
271
|
//#endregion
|
|
327
|
-
export {
|
|
272
|
+
export { r as mountMannequin3D };
|