exodeui-react-native 1.2.1 → 1.3.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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/engine.ts +36 -24
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "exodeui-react-native",
3
- "version": "1.2.1",
3
+ "version": "1.3.0",
4
4
  "description": "React Native runtime for ExodeUI animations",
5
5
  "main": "index.js",
6
6
  "files": [
package/src/engine.ts CHANGED
@@ -1535,8 +1535,19 @@ export class ExodeUIEngine {
1535
1535
 
1536
1536
  canvas.save();
1537
1537
  canvas.translate(cx, cy);
1538
- if (rotation !== 0) canvas.rotate(rotation * Math.PI / 180, 0, 0);
1539
- if (scaleX !== 1 || scaleY !== 1) canvas.scale(scaleX, scaleY);
1538
+
1539
+ // Rotate around the center of the object
1540
+ if (rotation !== 0) {
1541
+ canvas.translate(w / 2, h / 2);
1542
+ canvas.rotate(rotation * Math.PI / 180, 0, 0);
1543
+ canvas.translate(-w / 2, -h / 2);
1544
+ }
1545
+
1546
+ if (scaleX !== 1 || scaleY !== 1) {
1547
+ canvas.translate(w / 2, h / 2);
1548
+ canvas.scale(scaleX, scaleY);
1549
+ canvas.translate(-w / 2, -h / 2);
1550
+ }
1540
1551
 
1541
1552
  if (this._renderCount % 120 === 1) {
1542
1553
  console.log(`[ExodeUIEngine] - Drawing object: ${obj.name || obj.id} (${obj.type}), pos: ${cx.toFixed(1)},${cy.toFixed(1)}`);
@@ -1638,15 +1649,15 @@ export class ExodeUIEngine {
1638
1649
  const segments = geom.segments || obj.segments || [];
1639
1650
 
1640
1651
  if (enableSegments && segments.length > 0) {
1641
- let offsetX = -w/2;
1652
+ let offsetX = 0;
1642
1653
  const totalWidth = segments.reduce((acc: number, seg: any) => {
1643
1654
  const font = this.getFont(seg.fontSize || geom.fontSize || 14, seg.fontFamily || geom.fontFamily || 'System');
1644
1655
  return acc + font.getTextWidth(seg.text);
1645
1656
  }, 0);
1646
1657
 
1647
1658
  const align = geom.textAlign || 'left';
1648
- if (align === 'center') offsetX = -totalWidth / 2;
1649
- else if (align === 'right') offsetX = w/2 - totalWidth;
1659
+ if (align === 'center') offsetX = (w - totalWidth) / 2;
1660
+ else if (align === 'right') offsetX = w - totalWidth;
1650
1661
 
1651
1662
  segments.forEach((seg: any) => {
1652
1663
  const font = this.getFont(seg.fontSize || geom.fontSize || 14, seg.fontFamily || geom.fontFamily || 'System');
@@ -1655,7 +1666,9 @@ export class ExodeUIEngine {
1655
1666
  try { paint.setColor(Skia.Color(col.replace(/\s+/g, ''))); } catch { paint.setColor(Skia.Color('#ffffff')); }
1656
1667
  paint.setAlphaf((state?.opacity ?? 1) * (seg.fill?.opacity ?? 1));
1657
1668
 
1658
- canvas.drawText(seg.text, offsetX, 0, paint, font);
1669
+ const fontSize = seg.fontSize || geom.fontSize || 14;
1670
+ const y = fontSize; // Basic baseline
1671
+ canvas.drawText(seg.text, offsetX, y, paint, font);
1659
1672
  offsetX += font.getTextWidth(seg.text);
1660
1673
  });
1661
1674
  return;
@@ -1682,12 +1695,12 @@ export class ExodeUIEngine {
1682
1695
 
1683
1696
  const align = geom.textAlign || obj.textAlign || 'center';
1684
1697
  const textWidth = font.getTextWidth(text);
1685
- let x = -w/2;
1686
- if (align === 'center') x = -textWidth / 2;
1687
- else if (align === 'right') x = w/2 - textWidth;
1698
+ let x = 0;
1699
+ if (align === 'center') x = (w - textWidth) / 2;
1700
+ else if (align === 'right') x = w - textWidth;
1688
1701
 
1689
1702
  // Check baseline
1690
- const y = geom.verticalAlign === 'center' || !geom.verticalAlign ? fontSize / 3 : fontSize / 2;
1703
+ const y = geom.verticalAlign === 'center' || !geom.verticalAlign ? (h + fontSize/2) / 2 : fontSize;
1691
1704
  canvas.drawText(text, x, y, paint, font);
1692
1705
  }
1693
1706
 
@@ -1722,8 +1735,8 @@ export class ExodeUIEngine {
1722
1735
  paint.setAlphaf(state?.opacity ?? 1);
1723
1736
  canvas.drawImageRect(
1724
1737
  img,
1725
- { x: 0, y: 0, width: img.width(), height: img.height() },
1726
- { x: -w/2, y: -h/2, width: w, height: h },
1738
+ Skia.XYWHRect(0, 0, img.width(), img.height()),
1739
+ Skia.XYWHRect(0, 0, w, h),
1727
1740
  paint
1728
1741
  );
1729
1742
  } else {
@@ -2308,8 +2321,8 @@ export class ExodeUIEngine {
2308
2321
  const style = state.style || obj.style;
2309
2322
 
2310
2323
  const path = Skia.Path.Make();
2324
+ const rect = Skia.XYWHRect(0, 0, w, h);
2311
2325
  if (geometry.type === 'Rectangle') {
2312
- const rect = { x: -w/2, y: -h/2, width: w, height: h };
2313
2326
  if (state.cornerRadius !== undefined || geometry.corner_radius !== undefined) {
2314
2327
  const rawCr = state.cornerRadius ?? geometry.corner_radius;
2315
2328
  const cr = Array.isArray(rawCr) ? rawCr[0] : Number(rawCr);
@@ -2318,11 +2331,11 @@ export class ExodeUIEngine {
2318
2331
  path.addRect(rect);
2319
2332
  }
2320
2333
  } else if (geometry.type === 'Ellipse') {
2321
- path.addOval({ x: -w/2, y: -h/2, width: w, height: h });
2334
+ path.addOval(rect);
2322
2335
  } else if (geometry.type === 'Triangle') {
2323
- path.moveTo(0, -h/2);
2324
- path.lineTo(w/2, h/2);
2325
- path.lineTo(-w/2, h/2);
2336
+ path.moveTo(w / 2, 0);
2337
+ path.lineTo(w, h);
2338
+ path.lineTo(0, h);
2326
2339
  path.close();
2327
2340
  } else if (geometry.type === 'Star') {
2328
2341
  const ir = geometry.inner_radius || 20;
@@ -2331,8 +2344,8 @@ export class ExodeUIEngine {
2331
2344
  for (let i = 0; i < sp * 2; i++) {
2332
2345
  const a = (i * Math.PI / sp) - (Math.PI / 2);
2333
2346
  const rad = i % 2 === 0 ? or : ir;
2334
- const px = rad * Math.cos(a);
2335
- const py = rad * Math.sin(a);
2347
+ const px = w/2 + rad * Math.cos(a);
2348
+ const py = h/2 + rad * Math.sin(a);
2336
2349
  if (i === 0) path.moveTo(px, py);
2337
2350
  else path.lineTo(px, py);
2338
2351
  }
@@ -2346,14 +2359,14 @@ export class ExodeUIEngine {
2346
2359
  if (style.fill.type === 'LinearGradient' && style.fill.stops && style.fill.stops.length >= 2) {
2347
2360
  const colors = style.fill.stops.map((s: any) => Skia.Color(s.color.replace(/\s+/g, '')));
2348
2361
  const offsets = style.fill.stops.map((s: any) => s.offset);
2349
- const start = style.fill.start ? { x: -w/2 + style.fill.start[0] * w, y: -h/2 + style.fill.start[1] * h } : { x: -w/2, y: 0 };
2350
- const end = style.fill.end ? { x: -w/2 + style.fill.end[0] * w, y: -h/2 + style.fill.end[1] * h } : { x: w/2, y: 0 };
2362
+ const start = style.fill.start ? { x: style.fill.start[0] * w, y: style.fill.start[1] * h } : { x: 0, y: 0 };
2363
+ const end = style.fill.end ? { x: style.fill.end[0] * w, y: style.fill.end[1] * h } : { x: w, y: 0 };
2351
2364
  paint.setShader(Skia.Shader.MakeLinearGradient(Skia.Point(start.x, start.y), Skia.Point(end.x, end.y), colors, offsets, TileMode.Clamp));
2352
2365
  hasShader = true;
2353
2366
  } else if (style.fill.type === 'RadialGradient' && style.fill.stops && style.fill.stops.length >= 2) {
2354
2367
  const colors = style.fill.stops.map((s: any) => Skia.Color(s.color.replace(/\s+/g, '')));
2355
2368
  const offsets = style.fill.stops.map((s: any) => s.offset);
2356
- const center = style.fill.center ? { x: -w/2 + style.fill.center[0] * w, y: -h/2 + style.fill.center[1] * h } : { x: 0, y: 0 };
2369
+ const center = style.fill.center ? { x: style.fill.center[0] * w, y: style.fill.center[1] * h } : { x: w/2, y: h/2 };
2357
2370
  const radius = (style.fill.radius || 0.5) * Math.max(w, h);
2358
2371
  paint.setShader(Skia.Shader.MakeRadialGradient(Skia.Point(center.x, center.y), radius, colors, offsets, TileMode.Clamp));
2359
2372
  hasShader = true;
@@ -2361,7 +2374,6 @@ export class ExodeUIEngine {
2361
2374
  const img = this.getOrDecodeImage(style.fill.url);
2362
2375
  if (img) {
2363
2376
  const matrix = Skia.Matrix();
2364
- matrix.translate(-w/2, -h/2);
2365
2377
  matrix.scale(w / img.width(), h / img.height());
2366
2378
  paint.setShader(img.makeShaderOptions(0, 0, 0, 0, matrix));
2367
2379
  hasShader = true;
@@ -2379,7 +2391,7 @@ export class ExodeUIEngine {
2379
2391
 
2380
2392
  // Apply clipping for Frame types before drawing
2381
2393
  if (obj.type === 'Frame' || (obj as any).clipContent) {
2382
- canvas.clipRect({ x: -w/2, y: -h/2, width: w, height: h }, ClipOp.Intersect, true);
2394
+ canvas.clipRect(rect, ClipOp.Intersect, true);
2383
2395
  }
2384
2396
 
2385
2397
  if (style.shadow && style.shadow.opacity > 0) {