@mirage-engine/painter 0.3.0 → 1.0.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/dist/mirage-painter.js +478 -192
- package/dist/mirage-painter.umd.js +127 -67
- package/dist/src/Box/BoxGenerator.d.ts +18 -0
- package/dist/src/Painter.d.ts +5 -2
- package/dist/src/Text/FontMetricsManager.d.ts +10 -0
- package/dist/src/Text/TextGenerator.d.ts +11 -0
- package/dist/src/index.d.ts +3 -2
- package/dist/src/shaders/index.d.ts +9 -0
- package/dist/src/tools/parser.d.ts +16 -0
- package/dist/src/types/common.d.ts +8 -0
- package/package.json +1 -1
- package/src/Box/BoxGenerator.ts +286 -0
- package/src/Painter.ts +30 -26
- package/src/Text/FontMetricsManager.ts +62 -0
- package/src/Text/TextGenerator.ts +123 -0
- package/src/env.d.ts +4 -0
- package/src/index.ts +4 -3
- package/src/shaders/base/box-fragment.glsl +129 -0
- package/src/shaders/base/box-vertex.glsl +7 -0
- package/src/shaders/chunk/base-color-chunk.glsl +2 -0
- package/src/shaders/chunk/decl-chunk.glsl +4 -0
- package/src/shaders/chunk/uv-chunk.glsl +3 -0
- package/src/shaders/index.ts +16 -0
- package/src/tools/parser.ts +148 -0
- package/src/types/common.ts +10 -1
- package/dist/src/BoxGenerator.d.ts +0 -7
- package/dist/src/TextGenerator.d.ts +0 -3
- package/dist/src/dev/devShader.d.ts +0 -0
- package/src/BoxGenerator.ts +0 -217
- package/src/TextGenerator.ts +0 -85
- package/src/dev/devShader.ts +0 -0
|
@@ -1,85 +1,145 @@
|
|
|
1
|
-
(function(
|
|
2
|
-
`),
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
1
|
+
(function(d,h){typeof exports=="object"&&typeof module<"u"?h(exports,require("three")):typeof define=="function"&&define.amd?define(["exports","three"],h):(d=typeof globalThis<"u"?globalThis:d||self,h(d.MiragePainter={},d.THREE))})(this,function(d,h){"use strict";var V=Object.defineProperty;var D=(d,h,m)=>h in d?V(d,h,{enumerable:!0,configurable:!0,writable:!0,value:m}):d[h]=m;var S=(d,h,m)=>(D(d,typeof h!="symbol"?h+"":h,m),m);function m(o){const e=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(o){for(const t in o)if(t!=="default"){const n=Object.getOwnPropertyDescriptor(o,t);Object.defineProperty(e,t,n.get?n:{enumerable:!0,get:()=>o[t]})}}return e.default=o,Object.freeze(e)}const f=m(h);class y{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"),r=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",r.width=1,r.height=1,r.style.verticalAlign="baseline",n.appendChild(document.createTextNode("Hidden Text")),t.appendChild(n),t.appendChild(r),document.body.appendChild(t);const i=r.offsetTop-n.offsetTop;return document.body.removeChild(t),i}}S(y,"cache",new Map);class I extends f.MeshBasicMaterial{constructor(t,n,r,i,a=2){super({transparent:!0,side:f.FrontSide,color:16777215});S(this,"canvas");S(this,"ctx");S(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=a,this.map=new f.CanvasTexture(this.canvas),this.map.colorSpace=f.LinearSRGBColorSpace,this.map.minFilter=f.LinearFilter,this.map.magFilter=f.LinearFilter,this.updateText(t,n,r,i)}wrapText(t,n){const r=t.split(`
|
|
2
|
+
`),i=[];return r.forEach(a=>{const c=a.match(/[^\s\-]+\-?|\-|\s+/g)||[];if(c.length===0){i.push("");return}let l=c[0];for(let s=1;s<c.length;s++){const u=c[s];this.ctx.measureText(l+u).width<=n+2?l+=u:(l&&i.push(l),l=u.trimStart())}l&&i.push(l)}),i}updateText(t,n,r,i,a){a!==void 0&&(this.qualityFactor=a);const l=(window.devicePixelRatio||1)*this.qualityFactor,s=r*l,u=i*l;this.canvas.width!==s||this.canvas.height!==u?(this.canvas.width=s,this.canvas.height=u):this.ctx.clearRect(0,0,this.canvas.width,this.canvas.height),this.ctx.setTransform(l,0,0,l,0,0),this.ctx.font=n.font,this.ctx.fillStyle=n.color,this.ctx.textBaseline="alphabetic",this.ctx.globalAlpha=1;const b=this.wrapText(t,r),v=n.lineHeight,g=y.getBaseline(n.font);b.forEach((p,B)=>{const z=B*v+g;let O=0;n.textAlign==="center"?O=r/2:n.textAlign==="right"&&(O=r),this.ctx.textAlign=n.textAlign,this.ctx.fillText(p,O,z)}),this.map&&(this.map.needsUpdate=!0)}dispose(){this.map&&this.map.dispose(),super.dispose()}}function C(o){return typeof o=="number"?o:parseFloat(o)||0}function x(o){if(!o||o==="transparent")return{color:new f.Color(16777215),alpha:0};const e=o.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*([\d.]+))?\)/);if(e){const t=parseInt(e[1],10),n=parseInt(e[2],10),r=parseInt(e[3],10),i=e[4]!==void 0?parseFloat(e[4]):1;return{color:new f.Color(`rgb(${t}, ${n}, ${r})`),alpha:i}}return{color:new f.Color(o),alpha:1}}function M(o){const e=[];let t="",n=0;for(let r=0;r<o.length;r++){const i=o[r];if(i==="(")n++;else if(i===")")n--;else if(i===","&&n===0){e.push(t.trim()),t="";continue}t+=i}return t&&e.push(t.trim()),e}function R(o){if(!o||typeof o!="string"||!o.includes("linear-gradient"))return null;const e=o.match(/linear-gradient\((.*)\)/);if(!e)return null;const t=e[1],n=M(t);let r=Math.PI,i=0;const a=n[0].trim();if(a.startsWith("to "))a==="to top"?r=0:a==="to right"?r=Math.PI/2:a==="to bottom"?r=Math.PI:a==="to left"?r=Math.PI*1.5:a==="to top right"||a==="to right top"?r=Math.PI/4:a==="to bottom right"||a==="to right bottom"?r=Math.PI*.75:a==="to bottom left"||a==="to left bottom"?r=Math.PI*1.25:(a==="to top left"||a==="to left top")&&(r=Math.PI*1.75),i=1;else if(a.endsWith("deg")||a.endsWith("rad")||a.endsWith("turn")){const l=parseFloat(a);a.endsWith("deg")?r=l*(Math.PI/180):a.endsWith("rad")?r=l:a.endsWith("turn")&&(r=l*Math.PI*2),i=1}const c=[];for(let l=i;l<n.length&&!(c.length>=8);l++){const s=n[l].trim(),u=s.lastIndexOf(" ");let b=s,v=null;if(u!==-1&&!s.endsWith(")")){const p=s.substring(u+1);(p.endsWith("%")||p.endsWith("px")||!isNaN(parseFloat(p)))&&(b=s.substring(0,u).trim(),v=p)}const g=x(b);c.push({color:g.color,alpha:g.alpha,rawStop:v,stop:0})}if(c.length>0){for(let s=0;s<c.length;s++)c[s].rawStop!==null&&(c[s].stop=parseFloat(c[s].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 l=0;for(let s=1;s<c.length;s++)if(c[s].rawStop!==null||s===c.length-1){const u=s-l;if(u>1){const b=c[l].stop,g=(c[s].stop-b)/u;for(let p=1;p<u;p++)c[l+p].stop=b+g*p}l=s}}return{angle:r,stops:c}}const E=`varying vec2 vUv;
|
|
3
|
+
varying vec4 vScreenPos;
|
|
4
|
+
void main() {
|
|
5
|
+
vUv = uv;
|
|
6
|
+
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
|
|
7
|
+
vScreenPos = gl_Position;
|
|
8
|
+
}`,L=`varying vec2 vUv;
|
|
9
|
+
uniform vec2 uSize;
|
|
10
|
+
uniform vec4 uBorderRadius;
|
|
11
|
+
uniform float uBorderWidth;
|
|
12
|
+
uniform vec4 uBgColor;
|
|
13
|
+
uniform vec4 uBorderColor;
|
|
14
|
+
uniform float uOpacity;
|
|
15
|
+
|
|
16
|
+
uniform int uGradientCount;
|
|
17
|
+
uniform float uGradientAngle;
|
|
18
|
+
uniform vec4 uGradientColors[8];
|
|
19
|
+
uniform float uGradientStops[8];
|
|
20
|
+
|
|
21
|
+
#INJECT_DECLARATIONS
|
|
22
|
+
|
|
23
|
+
vec3 linearToSrgb(vec3 linearColor) {
|
|
24
|
+
vec3 linearPart = 12.92 * linearColor;
|
|
25
|
+
vec3 curvePart = 1.055 * pow(linearColor, vec3(1.0 / 2.4)) - 0.055;
|
|
26
|
+
vec3 switchCondition = step(vec3(0.0031308), linearColor);
|
|
27
|
+
return mix(linearPart, curvePart, switchCondition);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
vec3 srgbToLinear(vec3 srgbColor) {
|
|
31
|
+
vec3 linearPart = srgbColor / 12.92;
|
|
32
|
+
vec3 curvePart = pow((srgbColor + vec3(0.055)) / 1.055, vec3(2.4));
|
|
33
|
+
vec3 switchCondition = step(vec3(0.04045), srgbColor);
|
|
34
|
+
return mix(linearPart, curvePart, switchCondition);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
vec4 blendSrcOverInLinear(vec4 front, vec4 back) {
|
|
38
|
+
front.rgb = srgbToLinear(front.rgb);
|
|
39
|
+
back.rgb = srgbToLinear(back.rgb);
|
|
40
|
+
float aOut = front.a + back.a * (1.0 - front.a);
|
|
41
|
+
float safeAlpha = max(aOut, 0.0001);
|
|
42
|
+
vec3 cOut = (front.rgb * front.a + back.rgb * back.a * (1.0 - front.a)) / safeAlpha;
|
|
43
|
+
return vec4(linearToSrgb(cOut), aOut);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
vec4 blendSrcOver(vec4 front, vec4 back) {
|
|
47
|
+
float aOut = front.a + back.a * (1.0 - front.a);
|
|
48
|
+
float safeAlpha = max(aOut, 0.0001);
|
|
49
|
+
vec3 cOut = (front.rgb * front.a + back.rgb * back.a * (1.0 - front.a)) / safeAlpha;
|
|
50
|
+
return vec4(cOut, aOut);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
float sdRoundedBox(vec2 p, vec2 b, float r) {
|
|
54
|
+
vec2 q = abs(p) - b + r;
|
|
55
|
+
return min(max(q.x, q.y), 0.0) + length(max(q, 0.0)) - r;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
float sdVisualBox(vec2 p, vec2 b, float r) {
|
|
59
|
+
vec2 q = abs(p) - b + r;
|
|
60
|
+
float n = 2.01;
|
|
61
|
+
float d = pow(pow(max(q.x, 0.0), n) + pow(max(q.y, 0.0), n), 1.0/n) - r;
|
|
62
|
+
return min(max(q.x, q.y), 0.0) + d;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
vec4 calculateGradientLayer(vec2 uv) {
|
|
66
|
+
if (uGradientCount < 2) return uGradientColors[0];
|
|
67
|
+
|
|
68
|
+
vec2 dir = vec2(sin(uGradientAngle), cos(uGradientAngle));
|
|
69
|
+
vec2 p = (uv - 0.5) * uSize;
|
|
70
|
+
float proj = dot(p, dir);
|
|
71
|
+
float L = abs(uSize.x * dir.x) + abs(uSize.y * dir.y);
|
|
72
|
+
float t = clamp((proj / L) + 0.5, 0.0, 1.0);
|
|
73
|
+
|
|
74
|
+
vec4 color = uGradientColors[0];
|
|
75
|
+
for (int i = 1; i < 8; i++) {
|
|
76
|
+
if (i >= uGradientCount) break;
|
|
77
|
+
float prevStop = uGradientStops[i-1];
|
|
78
|
+
float currStop = uGradientStops[i];
|
|
79
|
+
|
|
80
|
+
float factor = clamp((t - prevStop) / (currStop - prevStop + 0.00001), 0.0, 1.0);
|
|
81
|
+
color = mix(color, uGradientColors[i], factor);
|
|
9
82
|
}
|
|
10
|
-
|
|
83
|
+
// return vec4(linearToSrgb(color.rgb), color.a);
|
|
84
|
+
return vec4(color);
|
|
85
|
+
}
|
|
11
86
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
uniform vec4 uBorderRadius;
|
|
16
|
-
uniform float uBorderWidth;
|
|
17
|
-
uniform vec3 uBgColor;
|
|
18
|
-
uniform vec3 uBorderColor;
|
|
19
|
-
uniform float uOpacity;
|
|
20
|
-
uniform float uBgOpacity;
|
|
21
|
-
uniform float uBorderOpacity;
|
|
87
|
+
void main() {
|
|
88
|
+
vec2 p = (vUv - 0.5) * uSize;
|
|
89
|
+
vec2 halfSize = uSize * 0.5;
|
|
22
90
|
|
|
23
|
-
#
|
|
91
|
+
#INJECT_UV_MODIFIER
|
|
24
92
|
|
|
25
|
-
|
|
26
|
-
|
|
93
|
+
// color decision pipeline
|
|
94
|
+
vec4 baseColor = vec4(uBgColor.rgb, uBgColor.a);
|
|
27
95
|
|
|
28
|
-
|
|
96
|
+
if (uGradientCount > 0) {
|
|
97
|
+
vec4 gradColor = calculateGradientLayer(vUv);
|
|
98
|
+
baseColor = blendSrcOver(gradColor, baseColor);
|
|
29
99
|
}
|
|
30
100
|
|
|
31
|
-
|
|
32
|
-
vec2 p = (vUv - 0.5) * uSize;
|
|
33
|
-
vec2 halfSize = uSize * 0.5;
|
|
101
|
+
#INJECT_BASE_COLOR
|
|
34
102
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
103
|
+
// Hybrid SDF
|
|
104
|
+
vec2 xRadii = mix(uBorderRadius.xw, uBorderRadius.yz, step(0.0, p.x));
|
|
105
|
+
float r = mix(xRadii.y, xRadii.x, step(0.0, p.y));
|
|
106
|
+
r = min(r, min(halfSize.x, halfSize.y));
|
|
39
107
|
|
|
40
|
-
|
|
108
|
+
float d = sdRoundedBox(p, halfSize, r);
|
|
41
109
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
float r = mix(xRadii.y, xRadii.x, step(0.0, p.y));
|
|
45
|
-
float d = sdRoundedBox(p, halfSize, r);
|
|
110
|
+
float d_smooth = sdVisualBox(p, halfSize, r);
|
|
111
|
+
float d_visual = d_smooth / fwidth(d_smooth);
|
|
46
112
|
|
|
47
|
-
|
|
48
|
-
|
|
113
|
+
// rendering pipeline
|
|
114
|
+
float bgMask = 1.0 - smoothstep(-0.5, 0.5, d_visual);
|
|
49
115
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
116
|
+
float halfBorder = uBorderWidth * 0.5;
|
|
117
|
+
float borderD = abs(d + halfBorder) - halfBorder;
|
|
118
|
+
float borderAlpha = (1.0 - smoothstep(0.0, 1.0, borderD)) * uBorderColor.a;
|
|
119
|
+
if (uBorderWidth <= 0.01) {
|
|
120
|
+
borderAlpha = 0.0;
|
|
121
|
+
}
|
|
56
122
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
123
|
+
// final blending (border + background) using blendSrcOver
|
|
124
|
+
vec4 borderLayer = vec4(uBorderColor.rgb, borderAlpha);
|
|
125
|
+
vec4 finalColor = blendSrcOver(borderLayer, baseColor);
|
|
126
|
+
// final color control (Tint, Noise)
|
|
127
|
+
#INJECT_COLOR_MODIFIER
|
|
61
128
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
vec4 finalColor = vec4(cOut, aOut);
|
|
129
|
+
float finalOpacity = finalColor.a * bgMask * uOpacity;
|
|
130
|
+
if (finalOpacity < 0.001) discard;
|
|
65
131
|
|
|
66
|
-
|
|
67
|
-
#INJECT_COLOR_MODIFIER
|
|
132
|
+
gl_FragColor = vec4(finalColor.rgb, finalOpacity);
|
|
68
133
|
|
|
69
|
-
float finalOpacity = finalColor.a * bgMask * uOpacity;
|
|
70
|
-
if (finalOpacity < 0.001) discard;
|
|
71
134
|
|
|
72
|
-
|
|
135
|
+
// #include <colorspace_fragment>
|
|
136
|
+
}
|
|
137
|
+
`,_=`uniform sampler2D uTexture;
|
|
138
|
+
varying vec4 vScreenPos;
|
|
139
|
+
uniform vec2 uTextureRepeat;
|
|
140
|
+
uniform vec2 uTextureOffset;`,F=`vec2 screenUv = (vScreenPos.xy / vScreenPos.w) * 0.5 + 0.5;
|
|
141
|
+
vec2 resultUv = screenUv;
|
|
73
142
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
uniform sampler2D uTexture;
|
|
78
|
-
varying vec4 vScreenPos;
|
|
79
|
-
`:"",l=n?`
|
|
80
|
-
vec2 screenUv = (vScreenPos.xy / vScreenPos.w) * 0.5 + 0.5;
|
|
81
|
-
vec2 resultUv = screenUv;
|
|
82
|
-
${(t==null?void 0:t.uvModifier)||""}
|
|
83
|
-
`:"",u=n?`
|
|
84
|
-
baseColor = texture2D(uTexture, resultUv);
|
|
85
|
-
`:"",d=(t==null?void 0:t.colorModifier)||"",x=w.replace("#INJECT_DECLARATIONS",a).replace("#INJECT_UV_MODIFIER",l).replace("#INJECT_BASE_COLOR",u).replace("#INJECT_COLOR_MODIFIER",d),f=g(e.backgroundColor),b=g(e.borderColor),B={uSize:{value:new c.Vector2(r,o)},uBorderRadius:{value:new c.Vector4(0,0,0,0)},uBorderWidth:{value:p(e.borderWidth)},uBgColor:{value:f.color},uBorderColor:{value:b.color},uOpacity:{value:e.opacity??1},uBgOpacity:{value:f.alpha},uBorderOpacity:{value:b.alpha},uTexture:{value:null}};return h(B.uBorderRadius.value,e.borderRadius),n&&(B.uTexture.value=i),new c.ShaderMaterial({uniforms:B,vertexShader:M,fragmentShader:x,transparent:!0,side:c.FrontSide})}function T(e,r,o,i,t){const n=g(r.backgroundColor),a=g(r.borderColor);e.uniforms.uSize.value.set(o,i),h(e.uniforms.uBorderRadius.value,r.borderRadius),e.uniforms.uBorderWidth.value=p(r.borderWidth),e.uniforms.uBgColor.value.copy(n.color),e.uniforms.uBorderColor.value.copy(a.color),e.uniforms.uOpacity.value=r.opacity??1,e.uniforms.uBgOpacity.value=n.alpha,e.uniforms.uBorderOpacity.value=a.alpha,e.uniforms.uTexture&&t!==void 0&&(e.uniforms.uTexture.value=t)}const y={create(e,r,o,i,t,n=2,a=null){if(e==="BOX")return O(r,i,t,a);if(e==="TEXT"){const l=m(o||"",r,i,t,n);return new c.MeshBasicMaterial({map:l,transparent:!0,side:c.FrontSide,color:16777215})}return new c.MeshBasicMaterial({visible:!1})},update(e,r,o,i,t,n,a=2,l){if(r==="BOX")T(e,o,t,n,l);else if(r==="TEXT"){const u=e;u.map&&u.map.dispose();const d=m(i||"",o,t,n,a);u.map=d,u.needsUpdate=!0}}};s.Painter=y,s.createBoxMaterial=O,s.createTextTexture=m,s.updateBoxMaterial=T,Object.defineProperty(s,Symbol.toStringTag,{value:"Module"})});
|
|
143
|
+
`,W=`vec4 texColor = texture2D(uTexture, resultUv);
|
|
144
|
+
baseColor = blendSrcOver(baseColor, texColor);`,k={vertexShader:E,fragmentShader:L},w={declChunk:_,uvChunk:F,baseColorChunk:W};function A(o,e,t,n=null,r){const i=n!==null||!!o.imageSrc,a=i?w.declChunk:"",c=o.isTraveler?w.uvChunk:`vec2 resultUv = vUv * uTextureRepeat + uTextureOffset;
|
|
145
|
+
`,l=i?c+((r==null?void 0:r.uvModifier)||""):"",s=i?w.baseColorChunk:"",u=(r==null?void 0:r.colorModifier)||"",b=k.fragmentShader.replace("#INJECT_DECLARATIONS",a).replace("#INJECT_UV_MODIFIER",l).replace("#INJECT_BASE_COLOR",s).replace("#INJECT_COLOR_MODIFIER",u),v=x(o.backgroundColor),g=x(o.borderColor),p={uSize:{value:new f.Vector2(e,t)},uBgColor:{value:new f.Vector4(v.color.r,v.color.g,v.color.b,v.alpha)},uBorderColor:{value:new f.Vector4(g.color.r,g.color.g,g.color.b,g.alpha)},uBorderRadius:{value:new f.Vector4(0,0,0,0)},uBorderWidth:{value:C(o.borderWidth)},uOpacity:{value:o.opacity??1},uTexture:{value:null},uTextureRepeat:{value:new f.Vector2(1,1)},uTextureOffset:{value:new f.Vector2(0,0)},uGradientCount:{value:0},uGradientAngle:{value:0},uGradientColors:{value:Array.from({length:8},()=>new f.Vector4(0,0,0,0))},uGradientStops:{value:new Float32Array(8)}};G(p.uBorderRadius.value,o.borderRadius),i&&(p.uTexture.value=n);const B=new f.ShaderMaterial({uniforms:p,vertexShader:k.vertexShader,fragmentShader:b,transparent:!0,side:f.FrontSide});return o.backgroundImage&&T(B,{backgroundImage:o.backgroundImage}),B}function P(o,e,t,n,r){const i=C(e.borderWidth);T(o,{width:t,height:n,borderRadius:e.borderRadius,borderWidth:i,backgroundColor:e.backgroundColor,borderColor:e.borderColor,opacity:e.opacity,texture:r,backgroundImage:e.backgroundImage})}function T(o,e){var n;if(e.width!==void 0&&e.height!==void 0&&o.uniforms.uSize.value.set(e.width,e.height),e.borderRadius!==void 0&&G(o.uniforms.uBorderRadius.value,e.borderRadius),e.borderWidth!==void 0&&(o.uniforms.uBorderWidth.value=e.borderWidth),e.backgroundColor!==void 0)if(Array.isArray(e.backgroundColor)){const r=o.uniforms.uBgColor.value.w;o.uniforms.uBgColor.value.set(e.backgroundColor[0],e.backgroundColor[1],e.backgroundColor[2],r)}else if(typeof e.backgroundColor=="string"){const r=x(e.backgroundColor);o.uniforms.uBgColor.value.set(r.color.r,r.color.g,r.color.b,r.alpha)}else{const r=o.uniforms.uBgColor.value.w;o.uniforms.uBgColor.value.set(e.backgroundColor.r,e.backgroundColor.g,e.backgroundColor.b,r)}if(e.borderColor!==void 0)if(Array.isArray(e.borderColor)){const r=o.uniforms.uBorderColor.value.w;o.uniforms.uBorderColor.value.set(e.borderColor[0],e.borderColor[1],e.borderColor[2],r)}else if(typeof e.borderColor=="string"){const r=x(e.borderColor);o.uniforms.uBorderColor.value.set(r.color.r,r.color.g,r.color.b,r.alpha)}else{const r=o.uniforms.uBorderColor.value.w;o.uniforms.uBorderColor.value.set(e.borderColor.r,e.borderColor.g,e.borderColor.b,r)}e.opacity!==void 0&&(o.uniforms.uOpacity.value=e.opacity),e.bgOpacity!==void 0&&(o.uniforms.uBgColor.value.w=e.bgOpacity),e.borderOpacity!==void 0&&(o.uniforms.uBorderColor.value.w=e.borderOpacity),o.uniforms.uTexture&&e.texture!==void 0&&(o.uniforms.uTexture.value=e.texture);const t=e.texture!==void 0?e.texture:(n=o.uniforms.uTexture)==null?void 0:n.value;if(t&&(t.image instanceof ImageBitmap||t.image instanceof HTMLImageElement||t.image instanceof HTMLCanvasElement)){const r=t.image.width,i=t.image.height,a=e.width??o.uniforms.uSize.value.x,c=e.height??o.uniforms.uSize.value.y;if(r&&i&&a&&c){const l=r/i,s=a/c;if(l>s){const u=s/l;o.uniforms.uTextureRepeat.value.set(u,1),o.uniforms.uTextureOffset.value.set((1-u)/2,0)}else{const u=l/s;o.uniforms.uTextureRepeat.value.set(1,u),o.uniforms.uTextureOffset.value.set(0,(1-u)/2)}}}else o.uniforms.uTextureRepeat&&(o.uniforms.uTextureRepeat.value.set(1,1),o.uniforms.uTextureOffset.value.set(0,0));if(e.backgroundImage!==void 0){const r=R(e.backgroundImage);if(r){o.uniforms.uGradientCount.value=r.stops.length,o.uniforms.uGradientAngle.value=r.angle;for(let i=0;i<8;i++)if(i<r.stops.length){const a=r.stops[i];o.uniforms.uGradientColors.value[i].set(a.color.r,a.color.g,a.color.b,a.alpha),console.log(a.color.r,a.color.g,a.color.b),o.uniforms.uGradientStops.value[i]=a.stop}else o.uniforms.uGradientColors.value[i].set(0,0,0,0),o.uniforms.uGradientStops.value[i]=1}else o.uniforms.uGradientCount.value=0}}function G(o,e){if(e==null){o.set(0,0,0,0);return}if(typeof e=="number"){o.set(e,e,e,e);return}if(Array.isArray(e)){o.set(e[0],e[1],e[2],e[3]);return}const t=e.split("/")[0].trim().split(/\s+/),n=C(t[0]),r=C(t[1]??t[0]),i=C(t[2]??t[0]),a=C(t[3]??t[1]??t[0]);o.set(n,r,i,a)}const U={create(o,e,t,n,r,i=2,a=null,c){return o==="BOX"?A(e,n,r,a,c):o==="TEXT"?new I(t||"",e,n,r,i):new f.MeshBasicMaterial({visible:!1})},update(o,e,t,n,r,i,a=2,c){e==="BOX"?P(o,t,r,i,c):e==="TEXT"&&o.updateText(n||"",t,r,i,a)},forceUpdateUniforms(o,e){T(o,e)}};d.Painter=U,d.TextGenerator=I,d.createBoxMaterial=A,d.parseColor=x,d.parseLinearGradient=R,d.parsePixelValue=C,d.setBoxUniforms=T,d.splitByComma=M,d.updateBoxMaterial=P,Object.defineProperty(d,Symbol.toStringTag,{value:"Module"})});
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { BoxStyles, ShaderHooks } from '../types';
|
|
2
|
+
import * as THREE from "three";
|
|
3
|
+
export declare function createBoxMaterial(styles: BoxStyles, width: number, height: number, texture?: THREE.Texture | null, hooks?: ShaderHooks): THREE.ShaderMaterial;
|
|
4
|
+
export declare function updateBoxMaterial(material: THREE.ShaderMaterial, styles: BoxStyles, width: number, height: number, texture?: THREE.Texture | null): void;
|
|
5
|
+
export interface BoxUniformValues {
|
|
6
|
+
width?: number;
|
|
7
|
+
height?: number;
|
|
8
|
+
borderRadius?: string | number | [number, number, number, number];
|
|
9
|
+
borderWidth?: number;
|
|
10
|
+
backgroundColor?: THREE.Color | [number, number, number] | string;
|
|
11
|
+
borderColor?: THREE.Color | [number, number, number] | string;
|
|
12
|
+
opacity?: number;
|
|
13
|
+
bgOpacity?: number;
|
|
14
|
+
borderOpacity?: number;
|
|
15
|
+
texture?: THREE.Texture | null;
|
|
16
|
+
backgroundImage?: string;
|
|
17
|
+
}
|
|
18
|
+
export declare function setBoxUniforms(material: THREE.ShaderMaterial, values: BoxUniformValues): void;
|
package/dist/src/Painter.d.ts
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
|
+
import { BoxStyles, TextStyles, ShaderHooks } from './types';
|
|
2
|
+
import { BoxUniformValues } from './Box/BoxGenerator';
|
|
1
3
|
import * as THREE from "three";
|
|
2
4
|
export declare const Painter: {
|
|
3
|
-
create(type: "BOX" | "TEXT", styles:
|
|
4
|
-
update(material: THREE.Material, type: "BOX" | "TEXT", styles:
|
|
5
|
+
create(type: "BOX" | "TEXT", styles: BoxStyles | TextStyles, content: string, width: number, height: number, quality?: number, texture?: THREE.Texture | null, shaderHooks?: ShaderHooks): THREE.Material;
|
|
6
|
+
update(material: THREE.Material, type: "BOX" | "TEXT", styles: BoxStyles | TextStyles, content: string, width: number, height: number, quality?: number, texture?: THREE.Texture | null): void;
|
|
7
|
+
forceUpdateUniforms(material: THREE.ShaderMaterial, values: BoxUniformValues): void;
|
|
5
8
|
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export declare class FontMetricsManager {
|
|
2
|
+
private static cache;
|
|
3
|
+
/**
|
|
4
|
+
* Calculates or retrieves the exact alphabetic baseline (in pixels) for the given font.
|
|
5
|
+
* @param font CSS font shorthand string (e.g., "400 16px Inter")
|
|
6
|
+
* @returns Baseline Y offset in pixels
|
|
7
|
+
*/
|
|
8
|
+
static getBaseline(font: string): number;
|
|
9
|
+
private static calculateBaselineFromDOM;
|
|
10
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { TextStyles } from '../types';
|
|
2
|
+
import * as THREE from "three";
|
|
3
|
+
export declare class TextGenerator extends THREE.MeshBasicMaterial {
|
|
4
|
+
private canvas;
|
|
5
|
+
private ctx;
|
|
6
|
+
private qualityFactor;
|
|
7
|
+
constructor(text: string, styles: TextStyles, rectWidth: number, rectHeight: number, qualityFactor?: number);
|
|
8
|
+
private wrapText;
|
|
9
|
+
updateText(text: string, styles: TextStyles, rectWidth: number, rectHeight: number, qualityFactor?: number): void;
|
|
10
|
+
dispose(): void;
|
|
11
|
+
}
|
package/dist/src/index.d.ts
CHANGED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import * as THREE from "three";
|
|
2
|
+
export declare function parsePixelValue(value: string | number): number;
|
|
3
|
+
export declare function parseColor(colorStr: string): {
|
|
4
|
+
color: THREE.Color;
|
|
5
|
+
alpha: number;
|
|
6
|
+
};
|
|
7
|
+
export declare function splitByComma(str: string): string[];
|
|
8
|
+
export declare function parseLinearGradient(value: string | null | undefined): {
|
|
9
|
+
angle: number;
|
|
10
|
+
stops: {
|
|
11
|
+
color: THREE.Color;
|
|
12
|
+
alpha: number;
|
|
13
|
+
rawStop: string | null;
|
|
14
|
+
stop: number;
|
|
15
|
+
}[];
|
|
16
|
+
} | null;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export interface TextStyles {
|
|
2
2
|
font: string;
|
|
3
|
+
fontSize: string;
|
|
3
4
|
color: string;
|
|
4
5
|
textAlign: CanvasTextAlign;
|
|
5
6
|
textBaseline: CanvasTextBaseline;
|
|
@@ -9,9 +10,16 @@ export interface TextStyles {
|
|
|
9
10
|
}
|
|
10
11
|
export interface BoxStyles {
|
|
11
12
|
backgroundColor: string;
|
|
13
|
+
backgroundImage: string;
|
|
14
|
+
imageSrc?: string;
|
|
15
|
+
isTraveler?: boolean;
|
|
12
16
|
opacity: number;
|
|
13
17
|
zIndex: number;
|
|
14
18
|
borderRadius: string;
|
|
15
19
|
borderColor: string;
|
|
16
20
|
borderWidth: string;
|
|
17
21
|
}
|
|
22
|
+
export interface ShaderHooks {
|
|
23
|
+
uvModifier?: string;
|
|
24
|
+
colorModifier?: string;
|
|
25
|
+
}
|
package/package.json
CHANGED
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
import * as THREE from "three";
|
|
2
|
+
import { BoxStyles, ShaderHooks } from "../types";
|
|
3
|
+
import {
|
|
4
|
+
parsePixelValue,
|
|
5
|
+
parseColor,
|
|
6
|
+
parseLinearGradient,
|
|
7
|
+
} from "../tools/parser";
|
|
8
|
+
import { BoxShader, BoxChunk } from "../shaders/index";
|
|
9
|
+
|
|
10
|
+
export function createBoxMaterial(
|
|
11
|
+
styles: BoxStyles,
|
|
12
|
+
width: number,
|
|
13
|
+
height: number,
|
|
14
|
+
texture: THREE.Texture | null = null,
|
|
15
|
+
hooks?: ShaderHooks,
|
|
16
|
+
): THREE.ShaderMaterial {
|
|
17
|
+
const hasTexture = texture !== null || !!styles.imageSrc;
|
|
18
|
+
|
|
19
|
+
const declChunk = hasTexture ? BoxChunk.declChunk : "";
|
|
20
|
+
const baseUvCode = styles.isTraveler ? BoxChunk.uvChunk : "vec2 resultUv = vUv * uTextureRepeat + uTextureOffset;\n";
|
|
21
|
+
const uvChunk = hasTexture ? baseUvCode + (hooks?.uvModifier || "") : "";
|
|
22
|
+
const baseColorChunk = hasTexture ? BoxChunk.baseColorChunk : "";
|
|
23
|
+
const colorModChunk = hooks?.colorModifier || "";
|
|
24
|
+
|
|
25
|
+
const fragmentShader = BoxShader.fragmentShader
|
|
26
|
+
.replace("#INJECT_DECLARATIONS", declChunk)
|
|
27
|
+
.replace("#INJECT_UV_MODIFIER", uvChunk)
|
|
28
|
+
.replace("#INJECT_BASE_COLOR", baseColorChunk)
|
|
29
|
+
.replace("#INJECT_COLOR_MODIFIER", colorModChunk);
|
|
30
|
+
|
|
31
|
+
// uniform setting
|
|
32
|
+
const parsedBg = parseColor(styles.backgroundColor);
|
|
33
|
+
const parsedBorder = parseColor(styles.borderColor);
|
|
34
|
+
const uniforms = {
|
|
35
|
+
uSize: { value: new THREE.Vector2(width, height) },
|
|
36
|
+
uBgColor: {
|
|
37
|
+
value: new THREE.Vector4(
|
|
38
|
+
parsedBg.color.r,
|
|
39
|
+
parsedBg.color.g,
|
|
40
|
+
parsedBg.color.b,
|
|
41
|
+
parsedBg.alpha,
|
|
42
|
+
),
|
|
43
|
+
},
|
|
44
|
+
uBorderColor: {
|
|
45
|
+
value: new THREE.Vector4(
|
|
46
|
+
parsedBorder.color.r,
|
|
47
|
+
parsedBorder.color.g,
|
|
48
|
+
parsedBorder.color.b,
|
|
49
|
+
parsedBorder.alpha,
|
|
50
|
+
),
|
|
51
|
+
},
|
|
52
|
+
uBorderRadius: { value: new THREE.Vector4(0, 0, 0, 0) },
|
|
53
|
+
uBorderWidth: { value: parsePixelValue(styles.borderWidth) },
|
|
54
|
+
uOpacity: { value: styles.opacity ?? 1.0 },
|
|
55
|
+
uTexture: { value: null as THREE.Texture | null },
|
|
56
|
+
uTextureRepeat: { value: new THREE.Vector2(1.0, 1.0) },
|
|
57
|
+
uTextureOffset: { value: new THREE.Vector2(0.0, 0.0) },
|
|
58
|
+
|
|
59
|
+
uGradientCount: { value: 0 },
|
|
60
|
+
uGradientAngle: { value: 0 },
|
|
61
|
+
uGradientColors: {
|
|
62
|
+
value: Array.from({ length: 8 }, () => new THREE.Vector4(0, 0, 0, 0)),
|
|
63
|
+
},
|
|
64
|
+
uGradientStops: { value: new Float32Array(8) },
|
|
65
|
+
};
|
|
66
|
+
// border radius value initialize
|
|
67
|
+
setBorderRadius(uniforms.uBorderRadius.value, styles.borderRadius);
|
|
68
|
+
|
|
69
|
+
if (hasTexture) {
|
|
70
|
+
uniforms.uTexture.value = texture;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const material = new THREE.ShaderMaterial({
|
|
74
|
+
uniforms: uniforms,
|
|
75
|
+
vertexShader: BoxShader.vertexShader,
|
|
76
|
+
fragmentShader: fragmentShader,
|
|
77
|
+
transparent: true,
|
|
78
|
+
side: THREE.FrontSide, // for better performance
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
// Initial gradient setup
|
|
82
|
+
if (styles.backgroundImage) {
|
|
83
|
+
setBoxUniforms(material, { backgroundImage: styles.backgroundImage });
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return material;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export function updateBoxMaterial(
|
|
90
|
+
material: THREE.ShaderMaterial,
|
|
91
|
+
styles: BoxStyles,
|
|
92
|
+
width: number,
|
|
93
|
+
height: number,
|
|
94
|
+
texture?: THREE.Texture | null,
|
|
95
|
+
) {
|
|
96
|
+
const parsedBorderWidth = parsePixelValue(styles.borderWidth);
|
|
97
|
+
setBoxUniforms(material, {
|
|
98
|
+
width,
|
|
99
|
+
height,
|
|
100
|
+
borderRadius: styles.borderRadius,
|
|
101
|
+
borderWidth: parsedBorderWidth,
|
|
102
|
+
backgroundColor: styles.backgroundColor,
|
|
103
|
+
borderColor: styles.borderColor,
|
|
104
|
+
opacity: styles.opacity,
|
|
105
|
+
texture,
|
|
106
|
+
backgroundImage: styles.backgroundImage,
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export interface BoxUniformValues {
|
|
111
|
+
width?: number;
|
|
112
|
+
height?: number;
|
|
113
|
+
borderRadius?: string | number | [number, number, number, number];
|
|
114
|
+
borderWidth?: number;
|
|
115
|
+
backgroundColor?: THREE.Color | [number, number, number] | string;
|
|
116
|
+
borderColor?: THREE.Color | [number, number, number] | string;
|
|
117
|
+
opacity?: number;
|
|
118
|
+
bgOpacity?: number;
|
|
119
|
+
borderOpacity?: number;
|
|
120
|
+
texture?: THREE.Texture | null;
|
|
121
|
+
backgroundImage?: string;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export function setBoxUniforms(
|
|
125
|
+
material: THREE.ShaderMaterial,
|
|
126
|
+
values: BoxUniformValues,
|
|
127
|
+
) {
|
|
128
|
+
if (values.width !== undefined && values.height !== undefined) {
|
|
129
|
+
material.uniforms.uSize.value.set(values.width, values.height);
|
|
130
|
+
}
|
|
131
|
+
if (values.borderRadius !== undefined) {
|
|
132
|
+
setBorderRadius(material.uniforms.uBorderRadius.value, values.borderRadius);
|
|
133
|
+
}
|
|
134
|
+
if (values.borderWidth !== undefined) {
|
|
135
|
+
material.uniforms.uBorderWidth.value = values.borderWidth;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if (values.backgroundColor !== undefined) {
|
|
139
|
+
if (Array.isArray(values.backgroundColor)) {
|
|
140
|
+
const currentAlpha = material.uniforms.uBgColor.value.w;
|
|
141
|
+
material.uniforms.uBgColor.value.set(
|
|
142
|
+
values.backgroundColor[0],
|
|
143
|
+
values.backgroundColor[1],
|
|
144
|
+
values.backgroundColor[2],
|
|
145
|
+
currentAlpha,
|
|
146
|
+
);
|
|
147
|
+
} else if (typeof values.backgroundColor === "string") {
|
|
148
|
+
const parsed = parseColor(values.backgroundColor);
|
|
149
|
+
material.uniforms.uBgColor.value.set(
|
|
150
|
+
parsed.color.r,
|
|
151
|
+
parsed.color.g,
|
|
152
|
+
parsed.color.b,
|
|
153
|
+
parsed.alpha,
|
|
154
|
+
);
|
|
155
|
+
} else {
|
|
156
|
+
const currentAlpha = material.uniforms.uBgColor.value.w;
|
|
157
|
+
material.uniforms.uBgColor.value.set(
|
|
158
|
+
values.backgroundColor.r,
|
|
159
|
+
values.backgroundColor.g,
|
|
160
|
+
values.backgroundColor.b,
|
|
161
|
+
currentAlpha,
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if (values.borderColor !== undefined) {
|
|
167
|
+
if (Array.isArray(values.borderColor)) {
|
|
168
|
+
const currentAlpha = material.uniforms.uBorderColor.value.w;
|
|
169
|
+
material.uniforms.uBorderColor.value.set(
|
|
170
|
+
values.borderColor[0],
|
|
171
|
+
values.borderColor[1],
|
|
172
|
+
values.borderColor[2],
|
|
173
|
+
currentAlpha,
|
|
174
|
+
);
|
|
175
|
+
} else if (typeof values.borderColor === "string") {
|
|
176
|
+
const parsed = parseColor(values.borderColor);
|
|
177
|
+
material.uniforms.uBorderColor.value.set(
|
|
178
|
+
parsed.color.r,
|
|
179
|
+
parsed.color.g,
|
|
180
|
+
parsed.color.b,
|
|
181
|
+
parsed.alpha,
|
|
182
|
+
);
|
|
183
|
+
} else {
|
|
184
|
+
const currentAlpha = material.uniforms.uBorderColor.value.w;
|
|
185
|
+
material.uniforms.uBorderColor.value.set(
|
|
186
|
+
values.borderColor.r,
|
|
187
|
+
values.borderColor.g,
|
|
188
|
+
values.borderColor.b,
|
|
189
|
+
currentAlpha,
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
if (values.opacity !== undefined) {
|
|
195
|
+
material.uniforms.uOpacity.value = values.opacity;
|
|
196
|
+
}
|
|
197
|
+
if (values.bgOpacity !== undefined) {
|
|
198
|
+
material.uniforms.uBgColor.value.w = values.bgOpacity;
|
|
199
|
+
}
|
|
200
|
+
if (values.borderOpacity !== undefined) {
|
|
201
|
+
material.uniforms.uBorderColor.value.w = values.borderOpacity;
|
|
202
|
+
}
|
|
203
|
+
if (material.uniforms.uTexture && values.texture !== undefined) {
|
|
204
|
+
material.uniforms.uTexture.value = values.texture;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const currentTex = values.texture !== undefined ? values.texture : material.uniforms.uTexture?.value;
|
|
208
|
+
if (currentTex && (currentTex.image instanceof ImageBitmap || currentTex.image instanceof HTMLImageElement || currentTex.image instanceof HTMLCanvasElement)) {
|
|
209
|
+
const imgWidth = currentTex.image.width;
|
|
210
|
+
const imgHeight = currentTex.image.height;
|
|
211
|
+
const domWidth = values.width ?? material.uniforms.uSize.value.x;
|
|
212
|
+
const domHeight = values.height ?? material.uniforms.uSize.value.y;
|
|
213
|
+
|
|
214
|
+
if (imgWidth && imgHeight && domWidth && domHeight) {
|
|
215
|
+
const imageAspect = imgWidth / imgHeight;
|
|
216
|
+
const domAspect = domWidth / domHeight;
|
|
217
|
+
|
|
218
|
+
if (imageAspect > domAspect) {
|
|
219
|
+
const repeatX = domAspect / imageAspect;
|
|
220
|
+
material.uniforms.uTextureRepeat.value.set(repeatX, 1);
|
|
221
|
+
material.uniforms.uTextureOffset.value.set((1 - repeatX) / 2, 0);
|
|
222
|
+
} else {
|
|
223
|
+
const repeatY = imageAspect / domAspect;
|
|
224
|
+
material.uniforms.uTextureRepeat.value.set(1, repeatY);
|
|
225
|
+
material.uniforms.uTextureOffset.value.set(0, (1 - repeatY) / 2);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
} else if (material.uniforms.uTextureRepeat) {
|
|
229
|
+
material.uniforms.uTextureRepeat.value.set(1, 1);
|
|
230
|
+
material.uniforms.uTextureOffset.value.set(0, 0);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
if (values.backgroundImage !== undefined) {
|
|
234
|
+
const gradient = parseLinearGradient(values.backgroundImage);
|
|
235
|
+
if (gradient) {
|
|
236
|
+
material.uniforms.uGradientCount.value = gradient.stops.length;
|
|
237
|
+
material.uniforms.uGradientAngle.value = gradient.angle;
|
|
238
|
+
for (let i = 0; i < 8; i++) {
|
|
239
|
+
if (i < gradient.stops.length) {
|
|
240
|
+
const s = gradient.stops[i];
|
|
241
|
+
material.uniforms.uGradientColors.value[i].set(
|
|
242
|
+
s.color.r,
|
|
243
|
+
s.color.g,
|
|
244
|
+
s.color.b,
|
|
245
|
+
s.alpha,
|
|
246
|
+
);
|
|
247
|
+
console.log(s.color.r, s.color.g, s.color.b);
|
|
248
|
+
material.uniforms.uGradientStops.value[i] = s.stop;
|
|
249
|
+
} else {
|
|
250
|
+
material.uniforms.uGradientColors.value[i].set(0, 0, 0, 0);
|
|
251
|
+
material.uniforms.uGradientStops.value[i] = 1.0;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
} else {
|
|
255
|
+
material.uniforms.uGradientCount.value = 0;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
function setBorderRadius(
|
|
261
|
+
target: THREE.Vector4,
|
|
262
|
+
radius?: string | number | [number, number, number, number],
|
|
263
|
+
) {
|
|
264
|
+
if (radius === undefined || radius === null) {
|
|
265
|
+
target.set(0, 0, 0, 0);
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
if (typeof radius === "number") {
|
|
270
|
+
target.set(radius, radius, radius, radius);
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
if (Array.isArray(radius)) {
|
|
275
|
+
target.set(radius[0], radius[1], radius[2], radius[3]);
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
const arr = radius.split("/")[0].trim().split(/\s+/);
|
|
280
|
+
const tl = parsePixelValue(arr[0]);
|
|
281
|
+
const tr = parsePixelValue(arr[1] ?? arr[0]);
|
|
282
|
+
const br = parsePixelValue(arr[2] ?? arr[0]);
|
|
283
|
+
const bl = parsePixelValue(arr[3] ?? arr[1] ?? arr[0]);
|
|
284
|
+
|
|
285
|
+
target.set(tl, tr, br, bl);
|
|
286
|
+
}
|