pixi-effects 0.41.0 → 0.42.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.
@@ -1732,6 +1732,23 @@ function inspectScene(root, frame, time, canvas, opts = {}) {
1732
1732
  }
1733
1733
 
1734
1734
  // src/core/pick.ts
1735
+ var scratch;
1736
+ var saidBlind = /* @__PURE__ */ new Set();
1737
+ var readResourceAlpha = (resource, x, y) => {
1738
+ try {
1739
+ if (scratch === void 0) {
1740
+ const c = document.createElement("canvas");
1741
+ c.width = c.height = 1;
1742
+ scratch = c.getContext("2d", { willReadFrequently: true });
1743
+ }
1744
+ if (!scratch) return null;
1745
+ scratch.clearRect(0, 0, 1, 1);
1746
+ scratch.drawImage(resource, x, y, 1, 1, 0, 0, 1, 1);
1747
+ return scratch.getImageData(0, 0, 1, 1).data[3] / 255;
1748
+ } catch {
1749
+ return null;
1750
+ }
1751
+ };
1735
1752
  function pointInQuad(q, x, y) {
1736
1753
  let pos = 0, neg = 0;
1737
1754
  for (let i = 0; i < 4; i++) {
@@ -1756,11 +1773,14 @@ var above = (a, b) => {
1756
1773
  }
1757
1774
  return b.length - a.length;
1758
1775
  };
1759
- function pickScene(root, x, y, opts = {}) {
1776
+ function pickScene(root, x, y, opts = {}, deps = {}) {
1777
+ if (opts.alpha !== void 0 && !(opts.alpha >= 0 && opts.alpha <= 1)) throw new Error(`pixi-effects: pick: alpha must be a number from 0 to 1 (a pixel at or below it is not a hit), got ${opts.alpha}`);
1778
+ const readAlpha = deps.readAlpha ?? readResourceAlpha;
1760
1779
  const found = [];
1780
+ const names = (list, name, path) => list.some((e) => e === path || name !== void 0 && e === name || path.startsWith(e + "/") || path.split("/").slice(0, -1).includes(e));
1761
1781
  const wanted = (name, path) => {
1762
- if (opts.ignore && (opts.ignore.includes(path) || name !== void 0 && opts.ignore.includes(name))) return false;
1763
- if (opts.only && !(opts.only.includes(path) || name !== void 0 && opts.only.includes(name))) return false;
1782
+ if (opts.ignore && names(opts.ignore, name, path)) return false;
1783
+ if (opts.only && !names(opts.only, name, path)) return false;
1764
1784
  return true;
1765
1785
  };
1766
1786
  const inside = (b, p) => p.x >= b.x && p.x <= b.x + b.width && p.y >= b.y && p.y <= b.y + b.height;
@@ -1790,8 +1810,34 @@ function pickScene(root, x, y, opts = {}) {
1790
1810
  const lb = d.getLocalBounds();
1791
1811
  if (inside(lb, local)) {
1792
1812
  const three = typeof seq.pickAt === "function" ? seq.pickAt((local.x - lb.x) / lb.width, (local.y - lb.y) / lb.height) : void 0;
1793
- if (three) hit = { name, path, type: seq.spec.type, kind: "three", point, three };
1794
- else if (three === void 0 && (typeof d.containsPoint !== "function" || d.containsPoint(local))) hit = { name, path, type: seq.spec.type, kind: "flat", point, local: { x: local.x - lb.x, y: local.y - lb.y } };
1813
+ if (three) {
1814
+ const planeOf = seq.dragPlane;
1815
+ if (planeOf) {
1816
+ three.grab = (plane = "screen") => {
1817
+ const along = planeOf.call(seq, three.point, plane);
1818
+ return { move: (cx, cy) => {
1819
+ const lp = d.toLocal({ x: cx, y: cy });
1820
+ const b = d.getLocalBounds();
1821
+ return along((lp.x - b.x) / b.width, (lp.y - b.y) / b.height);
1822
+ } };
1823
+ };
1824
+ }
1825
+ hit = { name, path, type: seq.spec.type, kind: "three", point, three };
1826
+ } else if (three === void 0 && (typeof d.containsPoint !== "function" || d.containsPoint(local))) {
1827
+ let opaque = true;
1828
+ const tex = opts.alpha !== void 0 && seq.spec.type !== "shader" ? d.texture : void 0;
1829
+ if (tex?.source?.resource) {
1830
+ const res = tex.source.resolution ?? 1;
1831
+ const a = readAlpha(tex.source.resource, (tex.frame.x + (local.x - lb.x) / lb.width * tex.frame.width) * res, (tex.frame.y + (local.y - lb.y) / lb.height * tex.frame.height) * res);
1832
+ if (a === null) {
1833
+ if (!saidBlind.has(path)) {
1834
+ saidBlind.add(path);
1835
+ console.warn(`pixi-effects: pick: the picture of layer "${path}" cannot be read for { alpha } (a picture from another origin: load it with crossorigin="anonymous" or from the same site), so the box counts as the picture`);
1836
+ }
1837
+ } else opaque = a > opts.alpha;
1838
+ }
1839
+ if (opaque) hit = { name, path, type: seq.spec.type, kind: "flat", point, local: { x: local.x - lb.x, y: local.y - lb.y } };
1840
+ }
1795
1841
  }
1796
1842
  }
1797
1843
  if (!hit) return;
@@ -1805,6 +1851,82 @@ function pickScene(root, x, y, opts = {}) {
1805
1851
  return found.sort((a, b) => above(a.order, b.order)).map((f) => f.hit);
1806
1852
  }
1807
1853
 
1854
+ // src/core/hover.ts
1855
+ var keyOf = (h) => h ? `${h.path}${h.three ? `#${h.three.object}` : ""}` : null;
1856
+ function startHover(host, opts = {}) {
1857
+ const { canvas } = host;
1858
+ const request = host.requestFrame ?? ((fn) => requestAnimationFrame(fn));
1859
+ const cancel = host.cancelFrame ?? ((id) => cancelAnimationFrame(id));
1860
+ const cursor = opts.cursor === true ? "pointer" : opts.cursor || "";
1861
+ const pickOpts = {};
1862
+ if (opts.only) pickOpts.only = opts.only;
1863
+ if (opts.ignore) pickOpts.ignore = opts.ignore;
1864
+ let current = null;
1865
+ let last = null;
1866
+ let loop = null;
1867
+ let said = false;
1868
+ const call = (fn, hit) => {
1869
+ if (!fn) return;
1870
+ try {
1871
+ fn(hit);
1872
+ } catch (err) {
1873
+ if (!said) {
1874
+ said = true;
1875
+ console.warn("pixi-effects: movie.hover: a handler threw (said once) \u2014", err);
1876
+ }
1877
+ }
1878
+ };
1879
+ const setCursor = (on) => {
1880
+ if (cursor) canvas.style.cursor = on ? cursor : "";
1881
+ };
1882
+ const evaluate = () => {
1883
+ if (!last) return;
1884
+ const [x, y] = host.point(last);
1885
+ const hit = host.pick(x, y, pickOpts);
1886
+ if (keyOf(hit) === keyOf(current)) {
1887
+ current = hit ?? current;
1888
+ return;
1889
+ }
1890
+ const was = current;
1891
+ current = hit;
1892
+ if (was) call(opts.onLeave, was);
1893
+ if (hit) call(opts.onEnter, hit);
1894
+ setCursor(!!hit);
1895
+ };
1896
+ const tick = () => {
1897
+ loop = null;
1898
+ if (last) {
1899
+ evaluate();
1900
+ loop = request(tick);
1901
+ }
1902
+ };
1903
+ const onMove = (event) => {
1904
+ last = event;
1905
+ evaluate();
1906
+ if (loop === null) loop = request(tick);
1907
+ };
1908
+ const onLeave = () => {
1909
+ last = null;
1910
+ if (loop !== null) {
1911
+ cancel(loop);
1912
+ loop = null;
1913
+ }
1914
+ const was = current;
1915
+ current = null;
1916
+ if (was) call(opts.onLeave, was);
1917
+ setCursor(false);
1918
+ };
1919
+ canvas.addEventListener("pointermove", onMove);
1920
+ canvas.addEventListener("pointerdown", onMove);
1921
+ canvas.addEventListener("pointerleave", onLeave);
1922
+ return () => {
1923
+ canvas.removeEventListener("pointermove", onMove);
1924
+ canvas.removeEventListener("pointerdown", onMove);
1925
+ canvas.removeEventListener("pointerleave", onLeave);
1926
+ onLeave();
1927
+ };
1928
+ }
1929
+
1808
1930
  // src/core/timelineChart.ts
1809
1931
  var GROUP_MIN = 4;
1810
1932
  var MAX_KEYS = 200;
@@ -2859,6 +2981,14 @@ var Movie = class {
2859
2981
  await this._goto(frame);
2860
2982
  this.emit("seeked", { frame: this.currentFrame, totalFrames: this.totalFrames });
2861
2983
  }
2984
+ /**
2985
+ * Draw the frame on screen again, quietly (no `seeking` / `seeked`). For a paused movie when something outside it that a layer reads has changed: a `three` layer whose `update` reads the page's own
2986
+ * state (a dragged object's place) shows it after `await movie.redraw()`. A playing movie needs no call: the next frame draws it.
2987
+ */
2988
+ async redraw() {
2989
+ this._requireReady("redraw");
2990
+ await this._quietly(() => this.gotoFrame(this.currentFrame, true));
2991
+ }
2862
2992
  /** Run `fn` with `seeking` / `seeked` silenced: the movie is moving its own playhead, not answering a seek. */
2863
2993
  async _quietly(fn) {
2864
2994
  this._quiet++;
@@ -3072,11 +3202,23 @@ var Movie = class {
3072
3202
  }
3073
3203
  /** Every layer under the canvas point, the one on top first (see `pick`). */
3074
3204
  pickAll(x, y, opts = {}) {
3075
- chunkTWZNOUM7_cjs.warnUnknownOptions("movie.pick()", opts, ["only", "ignore"]);
3205
+ chunkTWZNOUM7_cjs.warnUnknownOptions("movie.pick()", opts, ["only", "ignore", "alpha"]);
3076
3206
  this._requireReady("pick");
3077
3207
  if (!Number.isFinite(x) || !Number.isFinite(y)) throw new Error(`pixi-effects: movie.pick(x, y) needs two numbers (canvas pixels), got ${x}, ${y}. For a pointer event: movie.pick(...movie.canvasPoint(event))`);
3078
3208
  return pickScene(this._rootSequence, x, y, opts);
3079
3209
  }
3210
+ /**
3211
+ * Which layer the pointer is over, as callbacks: `onEnter(hit)` when the layer (or, in a `three` layer, the object) under the pointer changes to another, `onLeave(hit)` for the one it
3212
+ * left (also when the pointer leaves the picture). While the pointer rests inside, it is asked again on every frame, so a picture that moves under a still pointer is followed.
3213
+ * `{ only, ignore }` as for `pick`; `cursor: true` (or a CSS cursor) shows it while something is hovered. Returns the function that stops it. A page-side tool: an exported video has no pointer.
3214
+ */
3215
+ hover(opts = {}) {
3216
+ chunkTWZNOUM7_cjs.warnUnknownOptions("movie.hover()", opts, ["onEnter", "onLeave", "cursor", "only", "ignore"]);
3217
+ this._requireReady("hover");
3218
+ const canvas = this.app?.canvas;
3219
+ if (!canvas) throw new Error("pixi-effects: movie.hover needs the movie's canvas");
3220
+ return startHover({ canvas, pick: (x, y, o) => this.pick(x, y, o), point: (e) => this.canvasPoint(e) }, opts);
3221
+ }
3080
3222
  /** A pointer event's place in canvas pixels (the canvas may be shown larger or smaller than its own size): `movie.pick(...movie.canvasPoint(event))`. */
3081
3223
  canvasPoint(event) {
3082
3224
  this._requireReady("canvasPoint");
@@ -4607,5 +4749,5 @@ exports.splitText = splitText;
4607
4749
  exports.stagger = stagger;
4608
4750
  exports.wiggle = wiggle;
4609
4751
  exports.withFade = withFade;
4610
- //# sourceMappingURL=chunk-RN5FWGGM.cjs.map
4611
- //# sourceMappingURL=chunk-RN5FWGGM.cjs.map
4752
+ //# sourceMappingURL=chunk-WVZLAIKK.cjs.map
4753
+ //# sourceMappingURL=chunk-WVZLAIKK.cjs.map