@jiujue/react-canvas-fiber 2.1.1 → 2.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -0
- package/README.zh.md +4 -0
- package/dist/index.cjs +149 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +149 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -69,6 +69,10 @@ Common props:
|
|
|
69
69
|
|
|
70
70
|
- `style?: YogaStyle`: layout box; see Layout section below
|
|
71
71
|
- `background?: string`: background fill color
|
|
72
|
+
- `backgroundImage?: string`: image URL
|
|
73
|
+
- `backgroundSize?: string`: `cover` | `contain` | `auto` | `100px 50px` | `50% 50%`
|
|
74
|
+
- `backgroundPosition?: string`: `center` | `top left` | `10px 20px` | `50% 50%`
|
|
75
|
+
- `backgroundRepeat?: string`: `repeat` | `no-repeat` | `repeat-x` | `repeat-y`
|
|
72
76
|
- `border?: string`: CSS-like border. Supported forms:
|
|
73
77
|
- `"<number>px solid <color>"` (e.g. `1px solid rgba(255,255,255,0.2)`)
|
|
74
78
|
- `"<number> <color>"` (e.g. `2 #fff`)
|
package/README.zh.md
CHANGED
|
@@ -69,6 +69,10 @@ export function Example() {
|
|
|
69
69
|
|
|
70
70
|
- `style?: YogaStyle`:布局样式,见下方「布局(Yoga 子集)」
|
|
71
71
|
- `background?: string`:背景填充色
|
|
72
|
+
- `backgroundImage?: string`:背景图片 URL
|
|
73
|
+
- `backgroundSize?: string`:`cover` | `contain` | `auto` | `100px 50px` | `50% 50%`
|
|
74
|
+
- `backgroundPosition?: string`:`center` | `top left` | `10px 20px` | `50% 50%`
|
|
75
|
+
- `backgroundRepeat?: string`:`repeat` | `no-repeat` | `repeat-x` | `repeat-y`
|
|
72
76
|
- `border?: string`:类似 CSS 的 border,支持:
|
|
73
77
|
- `"<number>px solid <color>"`(例如 `1px solid rgba(255,255,255,0.2)`)
|
|
74
78
|
- `"<number> <color>"`(例如 `2 #fff`)
|
package/dist/index.cjs
CHANGED
|
@@ -48,6 +48,8 @@ function createNode(type, props) {
|
|
|
48
48
|
};
|
|
49
49
|
if (type === "Image") {
|
|
50
50
|
node.imageInstance = null;
|
|
51
|
+
} else if (type === "View") {
|
|
52
|
+
node.backgroundImageInstance = null;
|
|
51
53
|
}
|
|
52
54
|
return node;
|
|
53
55
|
}
|
|
@@ -131,6 +133,105 @@ function drawBorder(ctx, x, y, w, h, radius, border) {
|
|
|
131
133
|
ctx.stroke();
|
|
132
134
|
ctx.restore();
|
|
133
135
|
}
|
|
136
|
+
function resolveBgRepeat(repeat) {
|
|
137
|
+
if (!repeat) return "repeat";
|
|
138
|
+
if (repeat === "no-repeat" || repeat === "repeat-x" || repeat === "repeat-y" || repeat === "repeat")
|
|
139
|
+
return repeat;
|
|
140
|
+
return "repeat";
|
|
141
|
+
}
|
|
142
|
+
function parseBgSize(size, w, h, imgW, imgH) {
|
|
143
|
+
if (!size || size === "auto") return { width: imgW, height: imgH };
|
|
144
|
+
if (size === "cover") {
|
|
145
|
+
const scale = Math.max(w / imgW, h / imgH);
|
|
146
|
+
return { width: imgW * scale, height: imgH * scale };
|
|
147
|
+
}
|
|
148
|
+
if (size === "contain") {
|
|
149
|
+
const scale = Math.min(w / imgW, h / imgH);
|
|
150
|
+
return { width: imgW * scale, height: imgH * scale };
|
|
151
|
+
}
|
|
152
|
+
const parts = size.trim().split(/\s+/);
|
|
153
|
+
let rw = imgW;
|
|
154
|
+
let rh = imgH;
|
|
155
|
+
if (parts[0]) {
|
|
156
|
+
if (parts[0].endsWith("%")) {
|
|
157
|
+
rw = w * parseFloat(parts[0]) / 100;
|
|
158
|
+
} else if (parts[0] !== "auto") {
|
|
159
|
+
rw = parseFloat(parts[0]);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
if (parts[1]) {
|
|
163
|
+
if (parts[1].endsWith("%")) {
|
|
164
|
+
rh = h * parseFloat(parts[1]) / 100;
|
|
165
|
+
} else if (parts[1] !== "auto") {
|
|
166
|
+
rh = parseFloat(parts[1]);
|
|
167
|
+
}
|
|
168
|
+
} else {
|
|
169
|
+
if (parts[0] !== "auto") {
|
|
170
|
+
rh = imgH * (rw / imgW);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
return { width: rw, height: rh };
|
|
174
|
+
}
|
|
175
|
+
function parseBgPos(pos, w, h, targetW, targetH) {
|
|
176
|
+
const parts = (pos || "").trim().split(/\s+/);
|
|
177
|
+
if (parts.length === 0 || parts.length === 1 && !parts[0]) {
|
|
178
|
+
return { x: 0, y: 0 };
|
|
179
|
+
}
|
|
180
|
+
const xStr = parts[0];
|
|
181
|
+
let yStr = parts[1];
|
|
182
|
+
if (parts.length === 1) {
|
|
183
|
+
yStr = "center";
|
|
184
|
+
}
|
|
185
|
+
function resolve(val, containerDim, imgDim) {
|
|
186
|
+
if (val === "left" || val === "top") return 0;
|
|
187
|
+
if (val === "right" || val === "bottom") return containerDim - imgDim;
|
|
188
|
+
if (val === "center") return (containerDim - imgDim) / 2;
|
|
189
|
+
if (val.endsWith("%")) {
|
|
190
|
+
return (containerDim - imgDim) * parseFloat(val) / 100;
|
|
191
|
+
}
|
|
192
|
+
if (val.endsWith("px")) {
|
|
193
|
+
return parseFloat(val);
|
|
194
|
+
}
|
|
195
|
+
return parseFloat(val) || 0;
|
|
196
|
+
}
|
|
197
|
+
return {
|
|
198
|
+
x: resolve(xStr, w, targetW),
|
|
199
|
+
y: resolve(yStr, h, targetH)
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
function drawBackgroundImage(ctx, node, x, y, w, h, radius) {
|
|
203
|
+
const img = node.backgroundImageInstance;
|
|
204
|
+
if (!img || !img.complete || img.naturalWidth === 0 || img.naturalHeight === 0) return;
|
|
205
|
+
const props = node.props;
|
|
206
|
+
const bgSize = props.backgroundSize;
|
|
207
|
+
const bgPos = props.backgroundPosition;
|
|
208
|
+
const bgRepeat = resolveBgRepeat(props.backgroundRepeat);
|
|
209
|
+
const imgW = img.naturalWidth;
|
|
210
|
+
const imgH = img.naturalHeight;
|
|
211
|
+
const { width: targetW, height: targetH } = parseBgSize(bgSize, w, h, imgW, imgH);
|
|
212
|
+
const { x: posX, y: posY } = parseBgPos(bgPos, w, h, targetW, targetH);
|
|
213
|
+
ctx.save();
|
|
214
|
+
drawRoundedRect(ctx, x, y, w, h, radius);
|
|
215
|
+
ctx.clip();
|
|
216
|
+
if (bgRepeat === "no-repeat") {
|
|
217
|
+
ctx.drawImage(img, x + posX, y + posY, targetW, targetH);
|
|
218
|
+
} else {
|
|
219
|
+
const pattern = ctx.createPattern(img, bgRepeat);
|
|
220
|
+
if (pattern) {
|
|
221
|
+
const matrix = new DOMMatrix();
|
|
222
|
+
const scaleX = targetW / imgW;
|
|
223
|
+
const scaleY = targetH / imgH;
|
|
224
|
+
matrix.translateSelf(x + posX, y + posY);
|
|
225
|
+
matrix.scaleSelf(scaleX, scaleY);
|
|
226
|
+
pattern.setTransform(matrix);
|
|
227
|
+
ctx.fillStyle = pattern;
|
|
228
|
+
ctx.beginPath();
|
|
229
|
+
ctx.rect(x, y, w, h);
|
|
230
|
+
ctx.fill();
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
ctx.restore();
|
|
234
|
+
}
|
|
134
235
|
function drawNode(state, node, offsetX, offsetY) {
|
|
135
236
|
const { ctx } = state;
|
|
136
237
|
const x = offsetX + node.layout.x;
|
|
@@ -165,6 +266,10 @@ function drawNode(state, node, offsetX, offsetY) {
|
|
|
165
266
|
ctx.fill();
|
|
166
267
|
ctx.restore();
|
|
167
268
|
}
|
|
269
|
+
const viewNode = node;
|
|
270
|
+
if (viewNode.backgroundImageInstance) {
|
|
271
|
+
drawBackgroundImage(ctx, viewNode, x, y, w, h, viewRadius);
|
|
272
|
+
}
|
|
168
273
|
if (scrollX || scrollY) {
|
|
169
274
|
viewIsScroll = true;
|
|
170
275
|
ctx.save();
|
|
@@ -645,6 +750,18 @@ var hostConfig = {
|
|
|
645
750
|
if (!img.complete) {
|
|
646
751
|
img.onload = () => rootContainer.invalidate();
|
|
647
752
|
}
|
|
753
|
+
} else if (type === "View" && props.backgroundImage) {
|
|
754
|
+
const viewNode = node;
|
|
755
|
+
const img = new Image();
|
|
756
|
+
img.crossOrigin = "anonymous";
|
|
757
|
+
img.src = props.backgroundImage;
|
|
758
|
+
if (img.dataset) {
|
|
759
|
+
img.dataset.src = props.backgroundImage;
|
|
760
|
+
}
|
|
761
|
+
viewNode.backgroundImageInstance = img;
|
|
762
|
+
if (!img.complete) {
|
|
763
|
+
img.onload = () => rootContainer.invalidate();
|
|
764
|
+
}
|
|
648
765
|
}
|
|
649
766
|
return node;
|
|
650
767
|
},
|
|
@@ -729,6 +846,38 @@ var hostConfig = {
|
|
|
729
846
|
}
|
|
730
847
|
}
|
|
731
848
|
}
|
|
849
|
+
} else if (instance.type === "View") {
|
|
850
|
+
const viewNode = instance;
|
|
851
|
+
const newBg = instance.props.backgroundImage;
|
|
852
|
+
const currentBg = viewNode.backgroundImageInstance?.dataset?.src;
|
|
853
|
+
if (newBg !== currentBg) {
|
|
854
|
+
if (!newBg) {
|
|
855
|
+
viewNode.backgroundImageInstance = null;
|
|
856
|
+
} else {
|
|
857
|
+
const img = new Image();
|
|
858
|
+
img.crossOrigin = "anonymous";
|
|
859
|
+
img.src = newBg;
|
|
860
|
+
if (img.dataset) {
|
|
861
|
+
img.dataset.src = newBg;
|
|
862
|
+
}
|
|
863
|
+
viewNode.backgroundImageInstance = img;
|
|
864
|
+
const invalidate = () => {
|
|
865
|
+
let p = viewNode;
|
|
866
|
+
while (p) {
|
|
867
|
+
if (p.type === "Root") {
|
|
868
|
+
p.container?.invalidate();
|
|
869
|
+
return;
|
|
870
|
+
}
|
|
871
|
+
p = p.parent;
|
|
872
|
+
}
|
|
873
|
+
};
|
|
874
|
+
if (!img.complete) {
|
|
875
|
+
img.onload = invalidate;
|
|
876
|
+
} else {
|
|
877
|
+
invalidate();
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
}
|
|
732
881
|
}
|
|
733
882
|
},
|
|
734
883
|
commitTextUpdate() {
|