@mirage-engine/painter 1.0.1 → 1.0.3
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/dist/mirage-painter.js +174 -154
- package/dist/mirage-painter.umd.js +29 -16
- package/dist/src/tools/parser.d.ts +1 -1
- package/package.json +1 -1
- package/src/Box/BoxGenerator.ts +19 -13
- package/src/shaders/base/box-fragment.glsl +13 -2
- package/src/tools/parser.ts +6 -2
package/CHANGELOG.md
CHANGED
package/dist/mirage-painter.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
var
|
|
2
|
-
var
|
|
3
|
-
var
|
|
1
|
+
var k = Object.defineProperty;
|
|
2
|
+
var P = (r, e, t) => e in r ? k(r, e, { enumerable: !0, configurable: !0, writable: !0, value: t }) : r[e] = t;
|
|
3
|
+
var S = (r, e, t) => (P(r, typeof e != "symbol" ? e + "" : e, t), t);
|
|
4
4
|
import * as d from "three";
|
|
5
|
-
class
|
|
5
|
+
class A {
|
|
6
6
|
/**
|
|
7
7
|
* Calculates or retrieves the exact alphabetic baseline (in pixels) for the given font.
|
|
8
8
|
* @param font CSS font shorthand string (e.g., "400 16px Inter")
|
|
@@ -11,125 +11,128 @@ class I {
|
|
|
11
11
|
static getBaseline(e) {
|
|
12
12
|
if (this.cache.has(e))
|
|
13
13
|
return this.cache.get(e);
|
|
14
|
-
const
|
|
15
|
-
return this.cache.set(e,
|
|
14
|
+
const t = this.calculateBaselineFromDOM(e);
|
|
15
|
+
return this.cache.set(e, t), t;
|
|
16
16
|
}
|
|
17
17
|
static calculateBaselineFromDOM(e) {
|
|
18
18
|
if (typeof document > "u")
|
|
19
19
|
return 0;
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
const
|
|
23
|
-
return document.body.removeChild(
|
|
20
|
+
const t = document.createElement("div"), n = document.createElement("span"), o = document.createElement("img");
|
|
21
|
+
t.style.visibility = "hidden", t.style.position = "absolute", t.style.top = "0px", t.style.left = "0px", t.style.font = e, t.style.margin = "0", t.style.padding = "0", t.style.border = "none", n.style.margin = "0", n.style.padding = "0", n.style.border = "none", n.style.lineHeight = "normal", o.width = 1, o.height = 1, o.style.verticalAlign = "baseline", n.appendChild(document.createTextNode("Hidden Text")), t.appendChild(n), t.appendChild(o), document.body.appendChild(t);
|
|
22
|
+
const a = o.offsetTop - n.offsetTop;
|
|
23
|
+
return document.body.removeChild(t), a;
|
|
24
24
|
}
|
|
25
25
|
}
|
|
26
|
-
|
|
27
|
-
class
|
|
28
|
-
constructor(
|
|
26
|
+
S(A, "cache", /* @__PURE__ */ new Map());
|
|
27
|
+
class G extends d.MeshBasicMaterial {
|
|
28
|
+
constructor(t, n, o, a, i = 2) {
|
|
29
29
|
super({
|
|
30
30
|
transparent: !0,
|
|
31
31
|
side: d.FrontSide,
|
|
32
32
|
color: 16777215
|
|
33
33
|
});
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
34
|
+
S(this, "canvas");
|
|
35
|
+
S(this, "ctx");
|
|
36
|
+
S(this, "qualityFactor");
|
|
37
37
|
if (this.canvas = document.createElement("canvas"), this.ctx = this.canvas.getContext("2d"), !this.ctx)
|
|
38
38
|
throw new Error("[Mirage] Failed to create canvas context");
|
|
39
|
-
this.qualityFactor =
|
|
39
|
+
this.qualityFactor = i, this.map = new d.CanvasTexture(this.canvas), this.map.colorSpace = d.LinearSRGBColorSpace, this.map.minFilter = d.LinearFilter, this.map.magFilter = d.LinearFilter, this.updateText(t, n, o, a);
|
|
40
40
|
}
|
|
41
|
-
wrapText(
|
|
42
|
-
const o =
|
|
43
|
-
`),
|
|
44
|
-
return o.forEach((
|
|
45
|
-
const c =
|
|
41
|
+
wrapText(t, n) {
|
|
42
|
+
const o = t.split(`
|
|
43
|
+
`), a = [];
|
|
44
|
+
return o.forEach((i) => {
|
|
45
|
+
const c = i.match(/[^\s\-]+\-?|\-|\s+/g) || [];
|
|
46
46
|
if (c.length === 0) {
|
|
47
|
-
|
|
47
|
+
a.push("");
|
|
48
48
|
return;
|
|
49
49
|
}
|
|
50
50
|
let s = c[0];
|
|
51
51
|
for (let l = 1; l < c.length; l++) {
|
|
52
52
|
const u = c[l];
|
|
53
|
-
this.ctx.measureText(s + u).width <=
|
|
53
|
+
this.ctx.measureText(s + u).width <= n + 2 ? s += u : (s && a.push(s), s = u.trimStart());
|
|
54
54
|
}
|
|
55
|
-
s &&
|
|
56
|
-
}),
|
|
55
|
+
s && a.push(s);
|
|
56
|
+
}), a;
|
|
57
57
|
}
|
|
58
|
-
updateText(
|
|
59
|
-
|
|
60
|
-
const s = (window.devicePixelRatio || 1) * this.qualityFactor, l = o * s, u =
|
|
61
|
-
this.canvas.width !== l || this.canvas.height !== u ? (this.canvas.width = l, this.canvas.height = u) : this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height), this.ctx.setTransform(s, 0, 0, s, 0, 0), this.ctx.font =
|
|
62
|
-
const p = this.wrapText(
|
|
63
|
-
p.forEach((f,
|
|
64
|
-
const
|
|
65
|
-
let
|
|
66
|
-
|
|
58
|
+
updateText(t, n, o, a, i) {
|
|
59
|
+
i !== void 0 && (this.qualityFactor = i);
|
|
60
|
+
const s = (window.devicePixelRatio || 1) * this.qualityFactor, l = o * s, u = a * s;
|
|
61
|
+
this.canvas.width !== l || this.canvas.height !== u ? (this.canvas.width = l, this.canvas.height = u) : this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height), this.ctx.setTransform(s, 0, 0, s, 0, 0), this.ctx.font = n.font, this.ctx.fillStyle = n.color, this.ctx.textBaseline = "alphabetic", this.ctx.globalAlpha = 1;
|
|
62
|
+
const p = this.wrapText(t, o), m = n.lineHeight, v = A.getBaseline(n.font);
|
|
63
|
+
p.forEach((f, y) => {
|
|
64
|
+
const C = y * m + v;
|
|
65
|
+
let b = 0;
|
|
66
|
+
n.textAlign === "center" ? b = o / 2 : n.textAlign === "right" && (b = o), this.ctx.textAlign = n.textAlign, this.ctx.fillText(f, b, C);
|
|
67
67
|
}), this.map && (this.map.needsUpdate = !0);
|
|
68
68
|
}
|
|
69
69
|
dispose() {
|
|
70
70
|
this.map && this.map.dispose(), super.dispose();
|
|
71
71
|
}
|
|
72
72
|
}
|
|
73
|
-
function x(r) {
|
|
74
|
-
|
|
73
|
+
function x(r, e = 0) {
|
|
74
|
+
if (typeof r == "number")
|
|
75
|
+
return r;
|
|
76
|
+
const t = parseFloat(r) || 0;
|
|
77
|
+
return typeof r == "string" && r.includes("%") ? t / 100 * e : t;
|
|
75
78
|
}
|
|
76
|
-
function
|
|
79
|
+
function w(r) {
|
|
77
80
|
if (!r || r === "transparent")
|
|
78
81
|
return { color: new d.Color(16777215), alpha: 0 };
|
|
79
82
|
const e = r.match(
|
|
80
83
|
/rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*([\d.]+))?\)/
|
|
81
84
|
);
|
|
82
85
|
if (e) {
|
|
83
|
-
const
|
|
84
|
-
return { color: new d.Color(`rgb(${
|
|
86
|
+
const t = parseInt(e[1], 10), n = parseInt(e[2], 10), o = parseInt(e[3], 10), a = e[4] !== void 0 ? parseFloat(e[4]) : 1;
|
|
87
|
+
return { color: new d.Color(`rgb(${t}, ${n}, ${o})`), alpha: a };
|
|
85
88
|
}
|
|
86
89
|
return { color: new d.Color(r), alpha: 1 };
|
|
87
90
|
}
|
|
88
|
-
function
|
|
91
|
+
function E(r) {
|
|
89
92
|
const e = [];
|
|
90
|
-
let
|
|
93
|
+
let t = "", n = 0;
|
|
91
94
|
for (let o = 0; o < r.length; o++) {
|
|
92
|
-
const
|
|
93
|
-
if (
|
|
94
|
-
|
|
95
|
-
else if (
|
|
96
|
-
|
|
97
|
-
else if (
|
|
98
|
-
e.push(
|
|
95
|
+
const a = r[o];
|
|
96
|
+
if (a === "(")
|
|
97
|
+
n++;
|
|
98
|
+
else if (a === ")")
|
|
99
|
+
n--;
|
|
100
|
+
else if (a === "," && n === 0) {
|
|
101
|
+
e.push(t.trim()), t = "";
|
|
99
102
|
continue;
|
|
100
103
|
}
|
|
101
|
-
|
|
104
|
+
t += a;
|
|
102
105
|
}
|
|
103
|
-
return
|
|
106
|
+
return t && e.push(t.trim()), e;
|
|
104
107
|
}
|
|
105
|
-
function
|
|
108
|
+
function L(r) {
|
|
106
109
|
if (!r || typeof r != "string" || !r.includes("linear-gradient"))
|
|
107
110
|
return null;
|
|
108
111
|
const e = r.match(/linear-gradient\((.*)\)/);
|
|
109
112
|
if (!e)
|
|
110
113
|
return null;
|
|
111
|
-
const
|
|
112
|
-
let o = Math.PI,
|
|
113
|
-
const
|
|
114
|
-
if (
|
|
115
|
-
|
|
116
|
-
else if (
|
|
117
|
-
const s = parseFloat(
|
|
118
|
-
|
|
114
|
+
const t = e[1], n = E(t);
|
|
115
|
+
let o = Math.PI, a = 0;
|
|
116
|
+
const i = n[0].trim();
|
|
117
|
+
if (i.startsWith("to "))
|
|
118
|
+
i === "to top" ? o = 0 : i === "to right" ? o = Math.PI / 2 : i === "to bottom" ? o = Math.PI : i === "to left" ? o = Math.PI * 1.5 : i === "to top right" || i === "to right top" ? o = Math.PI / 4 : i === "to bottom right" || i === "to right bottom" ? o = Math.PI * 0.75 : i === "to bottom left" || i === "to left bottom" ? o = Math.PI * 1.25 : (i === "to top left" || i === "to left top") && (o = Math.PI * 1.75), a = 1;
|
|
119
|
+
else if (i.endsWith("deg") || i.endsWith("rad") || i.endsWith("turn")) {
|
|
120
|
+
const s = parseFloat(i);
|
|
121
|
+
i.endsWith("deg") ? o = s * (Math.PI / 180) : i.endsWith("rad") ? o = s : i.endsWith("turn") && (o = s * Math.PI * 2), a = 1;
|
|
119
122
|
}
|
|
120
123
|
const c = [];
|
|
121
|
-
for (let s =
|
|
122
|
-
const l =
|
|
123
|
-
let p = l,
|
|
124
|
+
for (let s = a; s < n.length && !(c.length >= 8); s++) {
|
|
125
|
+
const l = n[s].trim(), u = l.lastIndexOf(" ");
|
|
126
|
+
let p = l, m = null;
|
|
124
127
|
if (u !== -1 && !l.endsWith(")")) {
|
|
125
128
|
const f = l.substring(u + 1);
|
|
126
|
-
(f.endsWith("%") || f.endsWith("px") || !isNaN(parseFloat(f))) && (p = l.substring(0, u).trim(),
|
|
129
|
+
(f.endsWith("%") || f.endsWith("px") || !isNaN(parseFloat(f))) && (p = l.substring(0, u).trim(), m = f);
|
|
127
130
|
}
|
|
128
|
-
const v =
|
|
131
|
+
const v = w(p);
|
|
129
132
|
c.push({
|
|
130
133
|
color: v.color,
|
|
131
134
|
alpha: v.alpha,
|
|
132
|
-
rawStop:
|
|
135
|
+
rawStop: m,
|
|
133
136
|
stop: 0
|
|
134
137
|
});
|
|
135
138
|
}
|
|
@@ -151,13 +154,13 @@ function G(r) {
|
|
|
151
154
|
}
|
|
152
155
|
return { angle: o, stops: c };
|
|
153
156
|
}
|
|
154
|
-
const
|
|
157
|
+
const F = `varying vec2 vUv;
|
|
155
158
|
varying vec4 vScreenPos;
|
|
156
159
|
void main() {
|
|
157
160
|
vUv = uv;
|
|
158
161
|
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
|
|
159
162
|
vScreenPos = gl_Position;
|
|
160
|
-
}`,
|
|
163
|
+
}`, U = `varying vec2 vUv;
|
|
161
164
|
uniform vec2 uSize;
|
|
162
165
|
uniform vec4 uBorderRadius;
|
|
163
166
|
uniform float uBorderWidth;
|
|
@@ -240,6 +243,18 @@ void main() {
|
|
|
240
243
|
vec2 p = (vUv - 0.5) * uSize;
|
|
241
244
|
vec2 halfSize = uSize * 0.5;
|
|
242
245
|
|
|
246
|
+
// CSS Proportional Border-Radius Clamping
|
|
247
|
+
float tl = uBorderRadius.x;
|
|
248
|
+
float tr = uBorderRadius.y;
|
|
249
|
+
float br = uBorderRadius.z;
|
|
250
|
+
float bl = uBorderRadius.w;
|
|
251
|
+
float fTop = uSize.x / max(tl + tr, 0.0001);
|
|
252
|
+
float fBottom = uSize.x / max(bl + br, 0.0001);
|
|
253
|
+
float fLeft = uSize.y / max(tl + bl, 0.0001);
|
|
254
|
+
float fRight = uSize.y / max(tr + br, 0.0001);
|
|
255
|
+
float f = min(1.0, min(min(fTop, fBottom), min(fLeft, fRight)));
|
|
256
|
+
vec4 clampedRadius = uBorderRadius * f;
|
|
257
|
+
|
|
243
258
|
#INJECT_UV_MODIFIER
|
|
244
259
|
|
|
245
260
|
// color decision pipeline
|
|
@@ -253,9 +268,8 @@ void main() {
|
|
|
253
268
|
#INJECT_BASE_COLOR
|
|
254
269
|
|
|
255
270
|
// Hybrid SDF
|
|
256
|
-
vec2 xRadii = mix(
|
|
271
|
+
vec2 xRadii = mix(clampedRadius.xw, clampedRadius.yz, step(0.0, p.x));
|
|
257
272
|
float r = mix(xRadii.y, xRadii.x, step(0.0, p.y));
|
|
258
|
-
r = min(r, min(halfSize.x, halfSize.y));
|
|
259
273
|
|
|
260
274
|
float d = sdRoundedBox(p, halfSize, r);
|
|
261
275
|
|
|
@@ -286,51 +300,53 @@ void main() {
|
|
|
286
300
|
|
|
287
301
|
// #include <colorspace_fragment>
|
|
288
302
|
}
|
|
289
|
-
`,
|
|
303
|
+
`, W = `uniform sampler2D uTexture;
|
|
290
304
|
varying vec4 vScreenPos;
|
|
291
305
|
uniform vec2 uTextureRepeat;
|
|
292
306
|
uniform vec2 uTextureOffset;`, _ = `vec2 screenUv = (vScreenPos.xy / vScreenPos.w) * 0.5 + 0.5;
|
|
293
307
|
vec2 resultUv = screenUv;
|
|
294
308
|
|
|
295
|
-
`,
|
|
296
|
-
baseColor = blendSrcOver(baseColor, texColor);`,
|
|
297
|
-
vertexShader:
|
|
298
|
-
fragmentShader:
|
|
299
|
-
},
|
|
300
|
-
declChunk:
|
|
309
|
+
`, z = `vec4 texColor = texture2D(uTexture, resultUv);
|
|
310
|
+
baseColor = blendSrcOver(baseColor, texColor);`, R = {
|
|
311
|
+
vertexShader: F,
|
|
312
|
+
fragmentShader: U
|
|
313
|
+
}, I = {
|
|
314
|
+
declChunk: W,
|
|
301
315
|
uvChunk: _,
|
|
302
|
-
baseColorChunk:
|
|
316
|
+
baseColorChunk: z
|
|
303
317
|
};
|
|
304
|
-
function
|
|
305
|
-
const
|
|
306
|
-
let
|
|
318
|
+
function V(r, e, t, n = null, o) {
|
|
319
|
+
const a = n !== null || !!r.imageSrc;
|
|
320
|
+
let i = "";
|
|
307
321
|
const c = {};
|
|
308
322
|
if (o != null && o.uniforms)
|
|
309
323
|
for (const [h, g] of Object.entries(o.uniforms))
|
|
310
|
-
typeof g == "number" ? (
|
|
311
|
-
`, c[h] = { value: g }) : Array.isArray(g) ? g.length === 2 ? (
|
|
312
|
-
`, c[h] = { value: new d.Vector2(...g) }) : g.length === 3 ? (
|
|
313
|
-
`, c[h] = { value: new d.Vector3(...g) }) : g.length === 4 && (
|
|
314
|
-
`, c[h] = { value: new d.Vector4(...g) }) : (
|
|
324
|
+
typeof g == "number" ? (i += `uniform float ${h};
|
|
325
|
+
`, c[h] = { value: g }) : Array.isArray(g) ? g.length === 2 ? (i += `uniform vec2 ${h};
|
|
326
|
+
`, c[h] = { value: new d.Vector2(...g) }) : g.length === 3 ? (i += `uniform vec3 ${h};
|
|
327
|
+
`, c[h] = { value: new d.Vector3(...g) }) : g.length === 4 && (i += `uniform vec4 ${h};
|
|
328
|
+
`, c[h] = { value: new d.Vector4(...g) }) : (i += `uniform float ${h};
|
|
315
329
|
`, c[h] = { value: g });
|
|
316
|
-
const s = (
|
|
317
|
-
` +
|
|
318
|
-
`,
|
|
319
|
-
|
|
330
|
+
const s = o !== void 0, l = (a || s ? I.declChunk : "") + `
|
|
331
|
+
` + i, u = `vec2 screenUv = (vScreenPos.xy / vScreenPos.w) * 0.5 + 0.5;
|
|
332
|
+
`, p = r.isTraveler ? `vec2 resultUv = screenUv;
|
|
333
|
+
` : `vec2 resultUv = vUv * uTextureRepeat + uTextureOffset;
|
|
334
|
+
`, m = a || s ? u + p + ((o == null ? void 0 : o.uvModifier) || "") : "", v = a || s ? I.baseColorChunk : "", f = (o == null ? void 0 : o.colorModifier) || "", y = R.fragmentShader.replace("#INJECT_DECLARATIONS", l).replace("#INJECT_UV_MODIFIER", m).replace("#INJECT_BASE_COLOR", v).replace("#INJECT_COLOR_MODIFIER", f), C = w(r.backgroundColor), b = w(r.borderColor), B = {
|
|
335
|
+
uSize: { value: new d.Vector2(e, t) },
|
|
320
336
|
uBgColor: {
|
|
321
337
|
value: new d.Vector4(
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
338
|
+
C.color.r,
|
|
339
|
+
C.color.g,
|
|
340
|
+
C.color.b,
|
|
341
|
+
C.alpha
|
|
326
342
|
)
|
|
327
343
|
},
|
|
328
344
|
uBorderColor: {
|
|
329
345
|
value: new d.Vector4(
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
346
|
+
b.color.r,
|
|
347
|
+
b.color.g,
|
|
348
|
+
b.color.b,
|
|
349
|
+
b.alpha
|
|
334
350
|
)
|
|
335
351
|
},
|
|
336
352
|
uBorderRadius: { value: new d.Vector4(0, 0, 0, 0) },
|
|
@@ -346,24 +362,24 @@ function U(r, e, n, a = null, o) {
|
|
|
346
362
|
},
|
|
347
363
|
uGradientStops: { value: new Float32Array(8) }
|
|
348
364
|
};
|
|
349
|
-
|
|
350
|
-
const
|
|
351
|
-
uniforms: { ...
|
|
352
|
-
vertexShader:
|
|
353
|
-
fragmentShader:
|
|
365
|
+
M(B.uBorderRadius.value, r.borderRadius, Math.min(e, t)), a && (B.uTexture.value = n);
|
|
366
|
+
const O = new d.ShaderMaterial({
|
|
367
|
+
uniforms: { ...B, ...c },
|
|
368
|
+
vertexShader: R.vertexShader,
|
|
369
|
+
fragmentShader: y,
|
|
354
370
|
transparent: !0,
|
|
355
371
|
side: d.FrontSide
|
|
356
372
|
// for better performance
|
|
357
373
|
});
|
|
358
|
-
return r.backgroundImage && T(
|
|
374
|
+
return r.backgroundImage && T(O, { backgroundImage: r.backgroundImage }), O;
|
|
359
375
|
}
|
|
360
|
-
function
|
|
361
|
-
const
|
|
376
|
+
function D(r, e, t, n, o) {
|
|
377
|
+
const a = x(e.borderWidth);
|
|
362
378
|
T(r, {
|
|
363
|
-
width:
|
|
364
|
-
height:
|
|
379
|
+
width: t,
|
|
380
|
+
height: n,
|
|
365
381
|
borderRadius: e.borderRadius,
|
|
366
|
-
borderWidth:
|
|
382
|
+
borderWidth: a,
|
|
367
383
|
backgroundColor: e.backgroundColor,
|
|
368
384
|
borderColor: e.borderColor,
|
|
369
385
|
opacity: e.opacity,
|
|
@@ -372,8 +388,12 @@ function V(r, e, n, a, o) {
|
|
|
372
388
|
});
|
|
373
389
|
}
|
|
374
390
|
function T(r, e) {
|
|
375
|
-
var
|
|
376
|
-
if (e.width !== void 0 && e.height !== void 0 && r.uniforms.uSize.value.set(e.width, e.height), e.borderRadius !== void 0
|
|
391
|
+
var n;
|
|
392
|
+
if (e.width !== void 0 && e.height !== void 0 && r.uniforms.uSize.value.set(e.width, e.height), e.borderRadius !== void 0) {
|
|
393
|
+
const o = e.width !== void 0 && e.height !== void 0 ? Math.min(e.width, e.height) : Math.min(r.uniforms.uSize.value.x, r.uniforms.uSize.value.y);
|
|
394
|
+
M(r.uniforms.uBorderRadius.value, e.borderRadius, o);
|
|
395
|
+
}
|
|
396
|
+
if (e.borderWidth !== void 0 && (r.uniforms.uBorderWidth.value = e.borderWidth), e.backgroundColor !== void 0)
|
|
377
397
|
if (Array.isArray(e.backgroundColor)) {
|
|
378
398
|
const o = r.uniforms.uBgColor.value.w;
|
|
379
399
|
r.uniforms.uBgColor.value.set(
|
|
@@ -383,7 +403,7 @@ function T(r, e) {
|
|
|
383
403
|
o
|
|
384
404
|
);
|
|
385
405
|
} else if (typeof e.backgroundColor == "string") {
|
|
386
|
-
const o =
|
|
406
|
+
const o = w(e.backgroundColor);
|
|
387
407
|
r.uniforms.uBgColor.value.set(
|
|
388
408
|
o.color.r,
|
|
389
409
|
o.color.g,
|
|
@@ -409,7 +429,7 @@ function T(r, e) {
|
|
|
409
429
|
o
|
|
410
430
|
);
|
|
411
431
|
} else if (typeof e.borderColor == "string") {
|
|
412
|
-
const o =
|
|
432
|
+
const o = w(e.borderColor);
|
|
413
433
|
r.uniforms.uBorderColor.value.set(
|
|
414
434
|
o.color.r,
|
|
415
435
|
o.color.g,
|
|
@@ -426,11 +446,11 @@ function T(r, e) {
|
|
|
426
446
|
);
|
|
427
447
|
}
|
|
428
448
|
e.opacity !== void 0 && (r.uniforms.uOpacity.value = e.opacity), e.bgOpacity !== void 0 && (r.uniforms.uBgColor.value.w = e.bgOpacity), e.borderOpacity !== void 0 && (r.uniforms.uBorderColor.value.w = e.borderOpacity), r.uniforms.uTexture && e.texture !== void 0 && (r.uniforms.uTexture.value = e.texture);
|
|
429
|
-
const
|
|
430
|
-
if (
|
|
431
|
-
const o =
|
|
432
|
-
if (o &&
|
|
433
|
-
const s = o /
|
|
449
|
+
const t = e.texture !== void 0 ? e.texture : (n = r.uniforms.uTexture) == null ? void 0 : n.value;
|
|
450
|
+
if (t && (t.image instanceof ImageBitmap || t.image instanceof HTMLImageElement || t.image instanceof HTMLCanvasElement)) {
|
|
451
|
+
const o = t.image.naturalWidth || t.image.videoWidth || t.image.width, a = t.image.naturalHeight || t.image.videoHeight || t.image.height, i = e.width ?? r.uniforms.uSize.value.x, c = e.height ?? r.uniforms.uSize.value.y;
|
|
452
|
+
if (o && a && i && c) {
|
|
453
|
+
const s = o / a, l = i / c;
|
|
434
454
|
if (s > l) {
|
|
435
455
|
const u = l / s;
|
|
436
456
|
r.uniforms.uTextureRepeat.value.set(u, 1), r.uniforms.uTextureOffset.value.set((1 - u) / 2, 0);
|
|
@@ -442,27 +462,27 @@ function T(r, e) {
|
|
|
442
462
|
} else
|
|
443
463
|
r.uniforms.uTextureRepeat && (r.uniforms.uTextureRepeat.value.set(1, 1), r.uniforms.uTextureOffset.value.set(0, 0));
|
|
444
464
|
if (e.backgroundImage !== void 0) {
|
|
445
|
-
const o =
|
|
465
|
+
const o = L(e.backgroundImage);
|
|
446
466
|
if (o) {
|
|
447
467
|
r.uniforms.uGradientCount.value = o.stops.length, r.uniforms.uGradientAngle.value = o.angle;
|
|
448
|
-
for (let
|
|
449
|
-
if (
|
|
450
|
-
const
|
|
451
|
-
r.uniforms.uGradientColors.value[
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
), r.uniforms.uGradientStops.value[
|
|
468
|
+
for (let a = 0; a < 8; a++)
|
|
469
|
+
if (a < o.stops.length) {
|
|
470
|
+
const i = o.stops[a];
|
|
471
|
+
r.uniforms.uGradientColors.value[a].set(
|
|
472
|
+
i.color.r,
|
|
473
|
+
i.color.g,
|
|
474
|
+
i.color.b,
|
|
475
|
+
i.alpha
|
|
476
|
+
), r.uniforms.uGradientStops.value[a] = i.stop;
|
|
457
477
|
} else
|
|
458
|
-
r.uniforms.uGradientColors.value[
|
|
478
|
+
r.uniforms.uGradientColors.value[a].set(0, 0, 0, 0), r.uniforms.uGradientStops.value[a] = 1;
|
|
459
479
|
} else
|
|
460
480
|
r.uniforms.uGradientCount.value = 0;
|
|
461
481
|
}
|
|
462
482
|
for (const o of Object.keys(e))
|
|
463
483
|
o !== "width" && o !== "height" && o !== "borderRadius" && o !== "borderWidth" && o !== "backgroundColor" && o !== "borderColor" && o !== "opacity" && o !== "bgOpacity" && o !== "borderOpacity" && o !== "texture" && o !== "backgroundImage" && r.uniforms[o] !== void 0 && (r.uniforms[o].value !== void 0 && r.uniforms[o].value !== null && typeof r.uniforms[o].value.set == "function" ? Array.isArray(e[o]) ? r.uniforms[o].value.set(...e[o]) : e[o] !== void 0 && (e[o].copy ? r.uniforms[o].value.copy(e[o]) : r.uniforms[o].value = e[o]) : r.uniforms[o].value = e[o]);
|
|
464
484
|
}
|
|
465
|
-
function
|
|
485
|
+
function M(r, e, t = 0) {
|
|
466
486
|
if (e == null) {
|
|
467
487
|
r.set(0, 0, 0, 0);
|
|
468
488
|
return;
|
|
@@ -475,38 +495,38 @@ function A(r, e) {
|
|
|
475
495
|
r.set(e[0], e[1], e[2], e[3]);
|
|
476
496
|
return;
|
|
477
497
|
}
|
|
478
|
-
const n = e.split("/")[0].trim().split(/\s+/),
|
|
479
|
-
r.set(
|
|
498
|
+
const n = e.split("/")[0].trim().split(/\s+/), o = x(n[0], t), a = x(n[1] ?? n[0], t), i = x(n[2] ?? n[0], t), c = x(n[3] ?? n[1] ?? n[0], t);
|
|
499
|
+
r.set(o, a, i, c);
|
|
480
500
|
}
|
|
481
501
|
const N = {
|
|
482
|
-
create(r, e,
|
|
483
|
-
return r === "BOX" ?
|
|
502
|
+
create(r, e, t, n, o, a = 2, i = null, c) {
|
|
503
|
+
return r === "BOX" ? V(
|
|
484
504
|
e,
|
|
485
|
-
|
|
505
|
+
n,
|
|
486
506
|
o,
|
|
487
|
-
|
|
507
|
+
i,
|
|
488
508
|
c
|
|
489
|
-
) : r === "TEXT" ? new
|
|
490
|
-
|
|
509
|
+
) : r === "TEXT" ? new G(
|
|
510
|
+
t || "",
|
|
491
511
|
e,
|
|
492
|
-
|
|
512
|
+
n,
|
|
493
513
|
o,
|
|
494
|
-
|
|
514
|
+
a
|
|
495
515
|
) : new d.MeshBasicMaterial({ visible: !1 });
|
|
496
516
|
},
|
|
497
|
-
update(r, e,
|
|
498
|
-
e === "BOX" ?
|
|
517
|
+
update(r, e, t, n, o, a, i = 2, c) {
|
|
518
|
+
e === "BOX" ? D(
|
|
499
519
|
r,
|
|
500
|
-
|
|
520
|
+
t,
|
|
501
521
|
o,
|
|
502
|
-
|
|
522
|
+
a,
|
|
503
523
|
c
|
|
504
524
|
) : e === "TEXT" && r.updateText(
|
|
505
|
-
|
|
506
|
-
|
|
525
|
+
n || "",
|
|
526
|
+
t,
|
|
507
527
|
o,
|
|
508
|
-
|
|
509
|
-
|
|
528
|
+
a,
|
|
529
|
+
i
|
|
510
530
|
);
|
|
511
531
|
},
|
|
512
532
|
forceUpdateUniforms(r, e) {
|
|
@@ -515,12 +535,12 @@ const N = {
|
|
|
515
535
|
};
|
|
516
536
|
export {
|
|
517
537
|
N as Painter,
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
538
|
+
G as TextGenerator,
|
|
539
|
+
V as createBoxMaterial,
|
|
540
|
+
w as parseColor,
|
|
541
|
+
L as parseLinearGradient,
|
|
522
542
|
x as parsePixelValue,
|
|
523
543
|
T as setBoxUniforms,
|
|
524
|
-
|
|
525
|
-
|
|
544
|
+
E as splitByComma,
|
|
545
|
+
D as updateBoxMaterial
|
|
526
546
|
};
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
(function(f,
|
|
2
|
-
`),a=[];return
|
|
1
|
+
(function(f,p){typeof exports=="object"&&typeof module<"u"?p(exports,require("three")):typeof define=="function"&&define.amd?define(["exports","three"],p):(f=typeof globalThis<"u"?globalThis:f||self,p(f.MiragePainter={},f.THREE))})(this,function(f,p){"use strict";var q=Object.defineProperty;var j=(f,p,x)=>p in f?q(f,p,{enumerable:!0,configurable:!0,writable:!0,value:x}):f[p]=x;var T=(f,p,x)=>(j(f,typeof p!="symbol"?p+"":p,x),x);function x(r){const e=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(r){for(const t in r)if(t!=="default"){const n=Object.getOwnPropertyDescriptor(r,t);Object.defineProperty(e,t,n.get?n:{enumerable:!0,get:()=>r[t]})}}return e.default=r,Object.freeze(e)}const u=x(p);class M{static getBaseline(e){if(this.cache.has(e))return this.cache.get(e);const t=this.calculateBaselineFromDOM(e);return this.cache.set(e,t),t}static calculateBaselineFromDOM(e){if(typeof document>"u")return 0;const t=document.createElement("div"),n=document.createElement("span"),o=document.createElement("img");t.style.visibility="hidden",t.style.position="absolute",t.style.top="0px",t.style.left="0px",t.style.font=e,t.style.margin="0",t.style.padding="0",t.style.border="none",n.style.margin="0",n.style.padding="0",n.style.border="none",n.style.lineHeight="normal",o.width=1,o.height=1,o.style.verticalAlign="baseline",n.appendChild(document.createTextNode("Hidden Text")),t.appendChild(n),t.appendChild(o),document.body.appendChild(t);const a=o.offsetTop-n.offsetTop;return document.body.removeChild(t),a}}T(M,"cache",new Map);class A extends u.MeshBasicMaterial{constructor(t,n,o,a,i=2){super({transparent:!0,side:u.FrontSide,color:16777215});T(this,"canvas");T(this,"ctx");T(this,"qualityFactor");if(this.canvas=document.createElement("canvas"),this.ctx=this.canvas.getContext("2d"),!this.ctx)throw new Error("[Mirage] Failed to create canvas context");this.qualityFactor=i,this.map=new u.CanvasTexture(this.canvas),this.map.colorSpace=u.LinearSRGBColorSpace,this.map.minFilter=u.LinearFilter,this.map.magFilter=u.LinearFilter,this.updateText(t,n,o,a)}wrapText(t,n){const o=t.split(`
|
|
2
|
+
`),a=[];return o.forEach(i=>{const c=i.match(/[^\s\-]+\-?|\-|\s+/g)||[];if(c.length===0){a.push("");return}let s=c[0];for(let l=1;l<c.length;l++){const d=c[l];this.ctx.measureText(s+d).width<=n+2?s+=d:(s&&a.push(s),s=d.trimStart())}s&&a.push(s)}),a}updateText(t,n,o,a,i){i!==void 0&&(this.qualityFactor=i);const s=(window.devicePixelRatio||1)*this.qualityFactor,l=o*s,d=a*s;this.canvas.width!==l||this.canvas.height!==d?(this.canvas.width=l,this.canvas.height=d):this.ctx.clearRect(0,0,this.canvas.width,this.canvas.height),this.ctx.setTransform(s,0,0,s,0,0),this.ctx.font=n.font,this.ctx.fillStyle=n.color,this.ctx.textBaseline="alphabetic",this.ctx.globalAlpha=1;const g=this.wrapText(t,o),y=n.lineHeight,m=M.getBaseline(n.font);g.forEach((h,R)=>{const w=R*y+m;let C=0;n.textAlign==="center"?C=o/2:n.textAlign==="right"&&(C=o),this.ctx.textAlign=n.textAlign,this.ctx.fillText(h,C,w)}),this.map&&(this.map.needsUpdate=!0)}dispose(){this.map&&this.map.dispose(),super.dispose()}}function S(r,e=0){if(typeof r=="number")return r;const t=parseFloat(r)||0;return typeof r=="string"&&r.includes("%")?t/100*e:t}function B(r){if(!r||r==="transparent")return{color:new u.Color(16777215),alpha:0};const e=r.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*([\d.]+))?\)/);if(e){const t=parseInt(e[1],10),n=parseInt(e[2],10),o=parseInt(e[3],10),a=e[4]!==void 0?parseFloat(e[4]):1;return{color:new u.Color(`rgb(${t}, ${n}, ${o})`),alpha:a}}return{color:new u.Color(r),alpha:1}}function P(r){const e=[];let t="",n=0;for(let o=0;o<r.length;o++){const a=r[o];if(a==="(")n++;else if(a===")")n--;else if(a===","&&n===0){e.push(t.trim()),t="";continue}t+=a}return t&&e.push(t.trim()),e}function k(r){if(!r||typeof r!="string"||!r.includes("linear-gradient"))return null;const e=r.match(/linear-gradient\((.*)\)/);if(!e)return null;const t=e[1],n=P(t);let o=Math.PI,a=0;const i=n[0].trim();if(i.startsWith("to "))i==="to top"?o=0:i==="to right"?o=Math.PI/2:i==="to bottom"?o=Math.PI:i==="to left"?o=Math.PI*1.5:i==="to top right"||i==="to right top"?o=Math.PI/4:i==="to bottom right"||i==="to right bottom"?o=Math.PI*.75:i==="to bottom left"||i==="to left bottom"?o=Math.PI*1.25:(i==="to top left"||i==="to left top")&&(o=Math.PI*1.75),a=1;else if(i.endsWith("deg")||i.endsWith("rad")||i.endsWith("turn")){const s=parseFloat(i);i.endsWith("deg")?o=s*(Math.PI/180):i.endsWith("rad")?o=s:i.endsWith("turn")&&(o=s*Math.PI*2),a=1}const c=[];for(let s=a;s<n.length&&!(c.length>=8);s++){const l=n[s].trim(),d=l.lastIndexOf(" ");let g=l,y=null;if(d!==-1&&!l.endsWith(")")){const h=l.substring(d+1);(h.endsWith("%")||h.endsWith("px")||!isNaN(parseFloat(h)))&&(g=l.substring(0,d).trim(),y=h)}const m=B(g);c.push({color:m.color,alpha:m.alpha,rawStop:y,stop:0})}if(c.length>0){for(let l=0;l<c.length;l++)c[l].rawStop!==null&&(c[l].stop=parseFloat(c[l].rawStop)/100);c[0].rawStop===null&&(c[0].stop=0),c.length>1&&c[c.length-1].rawStop===null&&(c[c.length-1].stop=1);let s=0;for(let l=1;l<c.length;l++)if(c[l].rawStop!==null||l===c.length-1){const d=l-s;if(d>1){const g=c[s].stop,m=(c[l].stop-g)/d;for(let h=1;h<d;h++)c[s+h].stop=g+m*h}s=l}}return{angle:o,stops:c}}const W=`varying vec2 vUv;
|
|
3
3
|
varying vec4 vScreenPos;
|
|
4
4
|
void main() {
|
|
5
5
|
vUv = uv;
|
|
6
6
|
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
|
|
7
7
|
vScreenPos = gl_Position;
|
|
8
|
-
}`,
|
|
8
|
+
}`,z=`varying vec2 vUv;
|
|
9
9
|
uniform vec2 uSize;
|
|
10
10
|
uniform vec4 uBorderRadius;
|
|
11
11
|
uniform float uBorderWidth;
|
|
@@ -88,6 +88,18 @@ void main() {
|
|
|
88
88
|
vec2 p = (vUv - 0.5) * uSize;
|
|
89
89
|
vec2 halfSize = uSize * 0.5;
|
|
90
90
|
|
|
91
|
+
// CSS Proportional Border-Radius Clamping
|
|
92
|
+
float tl = uBorderRadius.x;
|
|
93
|
+
float tr = uBorderRadius.y;
|
|
94
|
+
float br = uBorderRadius.z;
|
|
95
|
+
float bl = uBorderRadius.w;
|
|
96
|
+
float fTop = uSize.x / max(tl + tr, 0.0001);
|
|
97
|
+
float fBottom = uSize.x / max(bl + br, 0.0001);
|
|
98
|
+
float fLeft = uSize.y / max(tl + bl, 0.0001);
|
|
99
|
+
float fRight = uSize.y / max(tr + br, 0.0001);
|
|
100
|
+
float f = min(1.0, min(min(fTop, fBottom), min(fLeft, fRight)));
|
|
101
|
+
vec4 clampedRadius = uBorderRadius * f;
|
|
102
|
+
|
|
91
103
|
#INJECT_UV_MODIFIER
|
|
92
104
|
|
|
93
105
|
// color decision pipeline
|
|
@@ -101,9 +113,8 @@ void main() {
|
|
|
101
113
|
#INJECT_BASE_COLOR
|
|
102
114
|
|
|
103
115
|
// Hybrid SDF
|
|
104
|
-
vec2 xRadii = mix(
|
|
116
|
+
vec2 xRadii = mix(clampedRadius.xw, clampedRadius.yz, step(0.0, p.x));
|
|
105
117
|
float r = mix(xRadii.y, xRadii.x, step(0.0, p.y));
|
|
106
|
-
r = min(r, min(halfSize.x, halfSize.y));
|
|
107
118
|
|
|
108
119
|
float d = sdRoundedBox(p, halfSize, r);
|
|
109
120
|
|
|
@@ -134,18 +145,20 @@ void main() {
|
|
|
134
145
|
|
|
135
146
|
// #include <colorspace_fragment>
|
|
136
147
|
}
|
|
137
|
-
`,
|
|
148
|
+
`,V=`uniform sampler2D uTexture;
|
|
138
149
|
varying vec4 vScreenPos;
|
|
139
150
|
uniform vec2 uTextureRepeat;
|
|
140
|
-
uniform vec2 uTextureOffset;`,
|
|
151
|
+
uniform vec2 uTextureOffset;`,D=`vec2 screenUv = (vScreenPos.xy / vScreenPos.w) * 0.5 + 0.5;
|
|
141
152
|
vec2 resultUv = screenUv;
|
|
142
153
|
|
|
143
|
-
`,
|
|
144
|
-
baseColor = blendSrcOver(baseColor, texColor);`,G={vertexShader:
|
|
145
|
-
`,c[
|
|
146
|
-
`,c[
|
|
147
|
-
`,c[
|
|
148
|
-
`,c[
|
|
149
|
-
`,c[
|
|
150
|
-
`+
|
|
151
|
-
`,
|
|
154
|
+
`,H=`vec4 texColor = texture2D(uTexture, resultUv);
|
|
155
|
+
baseColor = blendSrcOver(baseColor, texColor);`,G={vertexShader:W,fragmentShader:z},E={declChunk:V,uvChunk:D,baseColorChunk:H};function L(r,e,t,n=null,o){const a=n!==null||!!r.imageSrc;let i="";const c={};if(o!=null&&o.uniforms)for(const[v,b]of Object.entries(o.uniforms))typeof b=="number"?(i+=`uniform float ${v};
|
|
156
|
+
`,c[v]={value:b}):Array.isArray(b)?b.length===2?(i+=`uniform vec2 ${v};
|
|
157
|
+
`,c[v]={value:new u.Vector2(...b)}):b.length===3?(i+=`uniform vec3 ${v};
|
|
158
|
+
`,c[v]={value:new u.Vector3(...b)}):b.length===4&&(i+=`uniform vec4 ${v};
|
|
159
|
+
`,c[v]={value:new u.Vector4(...b)}):(i+=`uniform float ${v};
|
|
160
|
+
`,c[v]={value:b});const s=o!==void 0,l=(a||s?E.declChunk:"")+`
|
|
161
|
+
`+i,d=`vec2 screenUv = (vScreenPos.xy / vScreenPos.w) * 0.5 + 0.5;
|
|
162
|
+
`,g=r.isTraveler?`vec2 resultUv = screenUv;
|
|
163
|
+
`:`vec2 resultUv = vUv * uTextureRepeat + uTextureOffset;
|
|
164
|
+
`,y=a||s?d+g+((o==null?void 0:o.uvModifier)||""):"",m=a||s?E.baseColorChunk:"",h=(o==null?void 0:o.colorModifier)||"",R=G.fragmentShader.replace("#INJECT_DECLARATIONS",l).replace("#INJECT_UV_MODIFIER",y).replace("#INJECT_BASE_COLOR",m).replace("#INJECT_COLOR_MODIFIER",h),w=B(r.backgroundColor),C=B(r.borderColor),I={uSize:{value:new u.Vector2(e,t)},uBgColor:{value:new u.Vector4(w.color.r,w.color.g,w.color.b,w.alpha)},uBorderColor:{value:new u.Vector4(C.color.r,C.color.g,C.color.b,C.alpha)},uBorderRadius:{value:new u.Vector4(0,0,0,0)},uBorderWidth:{value:S(r.borderWidth)},uOpacity:{value:r.opacity??1},uTexture:{value:null},uTextureRepeat:{value:new u.Vector2(1,1)},uTextureOffset:{value:new u.Vector2(0,0)},uGradientCount:{value:0},uGradientAngle:{value:0},uGradientColors:{value:Array.from({length:8},()=>new u.Vector4(0,0,0,0))},uGradientStops:{value:new Float32Array(8)}};U(I.uBorderRadius.value,r.borderRadius,Math.min(e,t)),a&&(I.uTexture.value=n);const F=new u.ShaderMaterial({uniforms:{...I,...c},vertexShader:G.vertexShader,fragmentShader:R,transparent:!0,side:u.FrontSide});return r.backgroundImage&&O(F,{backgroundImage:r.backgroundImage}),F}function _(r,e,t,n,o){const a=S(e.borderWidth);O(r,{width:t,height:n,borderRadius:e.borderRadius,borderWidth:a,backgroundColor:e.backgroundColor,borderColor:e.borderColor,opacity:e.opacity,texture:o,backgroundImage:e.backgroundImage})}function O(r,e){var n;if(e.width!==void 0&&e.height!==void 0&&r.uniforms.uSize.value.set(e.width,e.height),e.borderRadius!==void 0){const o=e.width!==void 0&&e.height!==void 0?Math.min(e.width,e.height):Math.min(r.uniforms.uSize.value.x,r.uniforms.uSize.value.y);U(r.uniforms.uBorderRadius.value,e.borderRadius,o)}if(e.borderWidth!==void 0&&(r.uniforms.uBorderWidth.value=e.borderWidth),e.backgroundColor!==void 0)if(Array.isArray(e.backgroundColor)){const o=r.uniforms.uBgColor.value.w;r.uniforms.uBgColor.value.set(e.backgroundColor[0],e.backgroundColor[1],e.backgroundColor[2],o)}else if(typeof e.backgroundColor=="string"){const o=B(e.backgroundColor);r.uniforms.uBgColor.value.set(o.color.r,o.color.g,o.color.b,o.alpha)}else{const o=r.uniforms.uBgColor.value.w;r.uniforms.uBgColor.value.set(e.backgroundColor.r,e.backgroundColor.g,e.backgroundColor.b,o)}if(e.borderColor!==void 0)if(Array.isArray(e.borderColor)){const o=r.uniforms.uBorderColor.value.w;r.uniforms.uBorderColor.value.set(e.borderColor[0],e.borderColor[1],e.borderColor[2],o)}else if(typeof e.borderColor=="string"){const o=B(e.borderColor);r.uniforms.uBorderColor.value.set(o.color.r,o.color.g,o.color.b,o.alpha)}else{const o=r.uniforms.uBorderColor.value.w;r.uniforms.uBorderColor.value.set(e.borderColor.r,e.borderColor.g,e.borderColor.b,o)}e.opacity!==void 0&&(r.uniforms.uOpacity.value=e.opacity),e.bgOpacity!==void 0&&(r.uniforms.uBgColor.value.w=e.bgOpacity),e.borderOpacity!==void 0&&(r.uniforms.uBorderColor.value.w=e.borderOpacity),r.uniforms.uTexture&&e.texture!==void 0&&(r.uniforms.uTexture.value=e.texture);const t=e.texture!==void 0?e.texture:(n=r.uniforms.uTexture)==null?void 0:n.value;if(t&&(t.image instanceof ImageBitmap||t.image instanceof HTMLImageElement||t.image instanceof HTMLCanvasElement)){const o=t.image.naturalWidth||t.image.videoWidth||t.image.width,a=t.image.naturalHeight||t.image.videoHeight||t.image.height,i=e.width??r.uniforms.uSize.value.x,c=e.height??r.uniforms.uSize.value.y;if(o&&a&&i&&c){const s=o/a,l=i/c;if(s>l){const d=l/s;r.uniforms.uTextureRepeat.value.set(d,1),r.uniforms.uTextureOffset.value.set((1-d)/2,0)}else{const d=s/l;r.uniforms.uTextureRepeat.value.set(1,d),r.uniforms.uTextureOffset.value.set(0,(1-d)/2)}}}else r.uniforms.uTextureRepeat&&(r.uniforms.uTextureRepeat.value.set(1,1),r.uniforms.uTextureOffset.value.set(0,0));if(e.backgroundImage!==void 0){const o=k(e.backgroundImage);if(o){r.uniforms.uGradientCount.value=o.stops.length,r.uniforms.uGradientAngle.value=o.angle;for(let a=0;a<8;a++)if(a<o.stops.length){const i=o.stops[a];r.uniforms.uGradientColors.value[a].set(i.color.r,i.color.g,i.color.b,i.alpha),r.uniforms.uGradientStops.value[a]=i.stop}else r.uniforms.uGradientColors.value[a].set(0,0,0,0),r.uniforms.uGradientStops.value[a]=1}else r.uniforms.uGradientCount.value=0}for(const o of Object.keys(e))o!=="width"&&o!=="height"&&o!=="borderRadius"&&o!=="borderWidth"&&o!=="backgroundColor"&&o!=="borderColor"&&o!=="opacity"&&o!=="bgOpacity"&&o!=="borderOpacity"&&o!=="texture"&&o!=="backgroundImage"&&r.uniforms[o]!==void 0&&(r.uniforms[o].value!==void 0&&r.uniforms[o].value!==null&&typeof r.uniforms[o].value.set=="function"?Array.isArray(e[o])?r.uniforms[o].value.set(...e[o]):e[o]!==void 0&&(e[o].copy?r.uniforms[o].value.copy(e[o]):r.uniforms[o].value=e[o]):r.uniforms[o].value=e[o])}function U(r,e,t=0){if(e==null){r.set(0,0,0,0);return}if(typeof e=="number"){r.set(e,e,e,e);return}if(Array.isArray(e)){r.set(e[0],e[1],e[2],e[3]);return}const n=e.split("/")[0].trim().split(/\s+/),o=S(n[0],t),a=S(n[1]??n[0],t),i=S(n[2]??n[0],t),c=S(n[3]??n[1]??n[0],t);r.set(o,a,i,c)}const N={create(r,e,t,n,o,a=2,i=null,c){return r==="BOX"?L(e,n,o,i,c):r==="TEXT"?new A(t||"",e,n,o,a):new u.MeshBasicMaterial({visible:!1})},update(r,e,t,n,o,a,i=2,c){e==="BOX"?_(r,t,o,a,c):e==="TEXT"&&r.updateText(n||"",t,o,a,i)},forceUpdateUniforms(r,e){O(r,e)}};f.Painter=N,f.TextGenerator=A,f.createBoxMaterial=L,f.parseColor=B,f.parseLinearGradient=k,f.parsePixelValue=S,f.setBoxUniforms=O,f.splitByComma=P,f.updateBoxMaterial=_,Object.defineProperty(f,Symbol.toStringTag,{value:"Module"})});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as THREE from "three";
|
|
2
|
-
export declare function parsePixelValue(value: string | number): number;
|
|
2
|
+
export declare function parsePixelValue(value: string | number, baseSize?: number): number;
|
|
3
3
|
export declare function parseColor(colorStr: string): {
|
|
4
4
|
color: THREE.Color;
|
|
5
5
|
alpha: number;
|
package/package.json
CHANGED
package/src/Box/BoxGenerator.ts
CHANGED
|
@@ -42,10 +42,12 @@ export function createBoxMaterial(
|
|
|
42
42
|
}
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
-
const
|
|
46
|
-
const
|
|
47
|
-
const
|
|
48
|
-
const
|
|
45
|
+
const hasHooks = hooks !== undefined;
|
|
46
|
+
const declChunk = (hasTexture || hasHooks ? BoxChunk.declChunk : "") + "\n" + customUniformsCode;
|
|
47
|
+
const screenUvDecl = "vec2 screenUv = (vScreenPos.xy / vScreenPos.w) * 0.5 + 0.5;\n";
|
|
48
|
+
const baseUvCode = styles.isTraveler ? "vec2 resultUv = screenUv;\n" : "vec2 resultUv = vUv * uTextureRepeat + uTextureOffset;\n";
|
|
49
|
+
const uvChunk = (hasTexture || hasHooks) ? screenUvDecl + baseUvCode + (hooks?.uvModifier || "") : "";
|
|
50
|
+
const baseColorChunk = (hasTexture || hasHooks) ? BoxChunk.baseColorChunk : "";
|
|
49
51
|
const colorModChunk = hooks?.colorModifier || "";
|
|
50
52
|
|
|
51
53
|
const fragmentShader = BoxShader.fragmentShader
|
|
@@ -90,7 +92,7 @@ export function createBoxMaterial(
|
|
|
90
92
|
uGradientStops: { value: new Float32Array(8) },
|
|
91
93
|
};
|
|
92
94
|
// border radius value initialize
|
|
93
|
-
setBorderRadius(uniforms.uBorderRadius.value, styles.borderRadius);
|
|
95
|
+
setBorderRadius(uniforms.uBorderRadius.value, styles.borderRadius, Math.min(width, height));
|
|
94
96
|
|
|
95
97
|
if (hasTexture) {
|
|
96
98
|
uniforms.uTexture.value = texture;
|
|
@@ -156,7 +158,10 @@ export function setBoxUniforms(
|
|
|
156
158
|
material.uniforms.uSize.value.set(values.width, values.height);
|
|
157
159
|
}
|
|
158
160
|
if (values.borderRadius !== undefined) {
|
|
159
|
-
|
|
161
|
+
const baseSize = values.width !== undefined && values.height !== undefined
|
|
162
|
+
? Math.min(values.width, values.height)
|
|
163
|
+
: Math.min(material.uniforms.uSize.value.x, material.uniforms.uSize.value.y);
|
|
164
|
+
setBorderRadius(material.uniforms.uBorderRadius.value, values.borderRadius, baseSize);
|
|
160
165
|
}
|
|
161
166
|
if (values.borderWidth !== undefined) {
|
|
162
167
|
material.uniforms.uBorderWidth.value = values.borderWidth;
|
|
@@ -233,8 +238,8 @@ export function setBoxUniforms(
|
|
|
233
238
|
|
|
234
239
|
const currentTex = values.texture !== undefined ? values.texture : material.uniforms.uTexture?.value;
|
|
235
240
|
if (currentTex && (currentTex.image instanceof ImageBitmap || currentTex.image instanceof HTMLImageElement || currentTex.image instanceof HTMLCanvasElement)) {
|
|
236
|
-
const imgWidth = currentTex.image.width;
|
|
237
|
-
const imgHeight = currentTex.image.height;
|
|
241
|
+
const imgWidth = (currentTex.image as HTMLImageElement).naturalWidth || currentTex.image.videoWidth || currentTex.image.width;
|
|
242
|
+
const imgHeight = (currentTex.image as HTMLImageElement).naturalHeight || currentTex.image.videoHeight || currentTex.image.height;
|
|
238
243
|
const domWidth = values.width ?? material.uniforms.uSize.value.x;
|
|
239
244
|
const domHeight = values.height ?? material.uniforms.uSize.value.y;
|
|
240
245
|
|
|
@@ -323,7 +328,8 @@ export function setBoxUniforms(
|
|
|
323
328
|
|
|
324
329
|
function setBorderRadius(
|
|
325
330
|
target: THREE.Vector4,
|
|
326
|
-
radius
|
|
331
|
+
radius: string | number | [number, number, number, number] | undefined | null,
|
|
332
|
+
baseSize: number = 0,
|
|
327
333
|
) {
|
|
328
334
|
if (radius === undefined || radius === null) {
|
|
329
335
|
target.set(0, 0, 0, 0);
|
|
@@ -341,10 +347,10 @@ function setBorderRadius(
|
|
|
341
347
|
}
|
|
342
348
|
|
|
343
349
|
const arr = radius.split("/")[0].trim().split(/\s+/);
|
|
344
|
-
const tl = parsePixelValue(arr[0]);
|
|
345
|
-
const tr = parsePixelValue(arr[1] ?? arr[0]);
|
|
346
|
-
const br = parsePixelValue(arr[2] ?? arr[0]);
|
|
347
|
-
const bl = parsePixelValue(arr[3] ?? arr[1] ?? arr[0]);
|
|
350
|
+
const tl = parsePixelValue(arr[0], baseSize);
|
|
351
|
+
const tr = parsePixelValue(arr[1] ?? arr[0], baseSize);
|
|
352
|
+
const br = parsePixelValue(arr[2] ?? arr[0], baseSize);
|
|
353
|
+
const bl = parsePixelValue(arr[3] ?? arr[1] ?? arr[0], baseSize);
|
|
348
354
|
|
|
349
355
|
target.set(tl, tr, br, bl);
|
|
350
356
|
}
|
|
@@ -81,6 +81,18 @@ void main() {
|
|
|
81
81
|
vec2 p = (vUv - 0.5) * uSize;
|
|
82
82
|
vec2 halfSize = uSize * 0.5;
|
|
83
83
|
|
|
84
|
+
// CSS Proportional Border-Radius Clamping
|
|
85
|
+
float tl = uBorderRadius.x;
|
|
86
|
+
float tr = uBorderRadius.y;
|
|
87
|
+
float br = uBorderRadius.z;
|
|
88
|
+
float bl = uBorderRadius.w;
|
|
89
|
+
float fTop = uSize.x / max(tl + tr, 0.0001);
|
|
90
|
+
float fBottom = uSize.x / max(bl + br, 0.0001);
|
|
91
|
+
float fLeft = uSize.y / max(tl + bl, 0.0001);
|
|
92
|
+
float fRight = uSize.y / max(tr + br, 0.0001);
|
|
93
|
+
float f = min(1.0, min(min(fTop, fBottom), min(fLeft, fRight)));
|
|
94
|
+
vec4 clampedRadius = uBorderRadius * f;
|
|
95
|
+
|
|
84
96
|
#INJECT_UV_MODIFIER
|
|
85
97
|
|
|
86
98
|
// color decision pipeline
|
|
@@ -94,9 +106,8 @@ void main() {
|
|
|
94
106
|
#INJECT_BASE_COLOR
|
|
95
107
|
|
|
96
108
|
// Hybrid SDF
|
|
97
|
-
vec2 xRadii = mix(
|
|
109
|
+
vec2 xRadii = mix(clampedRadius.xw, clampedRadius.yz, step(0.0, p.x));
|
|
98
110
|
float r = mix(xRadii.y, xRadii.x, step(0.0, p.y));
|
|
99
|
-
r = min(r, min(halfSize.x, halfSize.y));
|
|
100
111
|
|
|
101
112
|
float d = sdRoundedBox(p, halfSize, r);
|
|
102
113
|
|
package/src/tools/parser.ts
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
import * as THREE from "three";
|
|
2
2
|
|
|
3
|
-
export function parsePixelValue(value: string | number): number {
|
|
3
|
+
export function parsePixelValue(value: string | number, baseSize: number = 0): number {
|
|
4
4
|
if (typeof value === "number") return value;
|
|
5
|
-
|
|
5
|
+
const num = parseFloat(value) || 0;
|
|
6
|
+
if (typeof value === "string" && value.includes("%")) {
|
|
7
|
+
return (num / 100) * baseSize;
|
|
8
|
+
}
|
|
9
|
+
return num;
|
|
6
10
|
}
|
|
7
11
|
|
|
8
12
|
export function parseColor(colorStr: string) {
|