@weasel-js/animator 1.13.0 → 1.14.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/dist/react.js CHANGED
@@ -1878,7 +1878,7 @@ function createFrame(host) {
1878
1878
  const all = table.waiting > 0 && host.tripwire();
1879
1879
  for (const anim of all ? [...animations.values()] : table.ticks()) {
1880
1880
  if (animations.get(anim.id) !== anim) continue;
1881
- const scale = clock.paused ? 0 : clock.timeScale * (anim.paused ? 0 : anim.timeScale);
1881
+ const scale = clock.paused ? 0 : clock.timeScale * clock.drive * (anim.paused ? 0 : anim.timeScale);
1882
1882
  tickDepth += 1;
1883
1883
  try {
1884
1884
  if (anim.tick(table.clockOf(anim), scale)) finished.push(anim);
@@ -1899,7 +1899,7 @@ function createFrame(host) {
1899
1899
  };
1900
1900
  return {
1901
1901
  tick(t) {
1902
- const frameDt = clock.frameT == null ? 0 : Math.max(0, t - clock.frameT);
1902
+ const frameDt = clock.frameT == null ? 0 : Math.max(0, (t - clock.frameT) * clock.drive);
1903
1903
  clock.frameT = t;
1904
1904
  framing = true;
1905
1905
  const dt = frameDt * (clock.paused ? 0 : Math.abs(clock.timeScale));
@@ -1931,7 +1931,7 @@ function createFrame(host) {
1931
1931
  };
1932
1932
  }
1933
1933
  function createAnimatorCore(host) {
1934
- const clock = { frameT: null, elapsed: 0, timeScale: 1, paused: false };
1934
+ const clock = { frameT: null, elapsed: 0, timeScale: 1, paused: false, drive: 1 };
1935
1935
  const hub = new AnimatorEventHub();
1936
1936
  const codec = createCodec((on) => {
1937
1937
  if ("move" in on) return on.move?.place;
@@ -1958,7 +1958,7 @@ function createAnimatorCore(host) {
1958
1958
  codec,
1959
1959
  hub,
1960
1960
  elapsed: () => clock.elapsed,
1961
- direction: () => clock.timeScale < 0 ? -1 : 1,
1961
+ direction: () => clock.timeScale * clock.drive < 0 ? -1 : 1,
1962
1962
  seedClock,
1963
1963
  showNow: (target) => targets.showNow(target),
1964
1964
  resting: () => targets.resting,
@@ -2044,6 +2044,14 @@ function createAnimatorCore(host) {
2044
2044
  return {
2045
2045
  api,
2046
2046
  tick: frame.tick,
2047
+ follow(t, rate) {
2048
+ const moved = clock.frameT == null ? 0 : t - clock.frameT;
2049
+ const drive = moved < 0 || moved === 0 && rate < 0 ? -1 : 1;
2050
+ if (drive !== clock.drive) table.turn(() => {
2051
+ clock.drive = drive;
2052
+ });
2053
+ frame.tick(t);
2054
+ },
2047
2055
  resumed() {
2048
2056
  clock.frameT = null;
2049
2057
  }
@@ -2055,14 +2063,27 @@ function useAnimator(opts = {}) {
2055
2063
  const optsRef = useLatest(opts);
2056
2064
  const colorOverrides = useRef(new ColorOverrideRegistry());
2057
2065
  const coreRef = useRef(null);
2066
+ const followingRef = useRef(false);
2067
+ const follow = useCallback((clock) => {
2068
+ followingRef.current = true;
2069
+ try {
2070
+ coreRef.current?.follow(clock.elapsed, clock.rate);
2071
+ } finally {
2072
+ followingRef.current = false;
2073
+ }
2074
+ }, []);
2058
2075
  const frameLoop = useVisibleRaf(
2059
2076
  useCallback((t) => {
2060
- coreRef.current?.tick(t);
2061
- }, []),
2077
+ const handle2 = optsRef.current.clock;
2078
+ if (handle2) follow(handle2.clock);
2079
+ else coreRef.current?.tick(t);
2080
+ }, [follow, optsRef]),
2062
2081
  {
2063
2082
  requestFrame: opts.requestFrame,
2064
2083
  cancelFrame: opts.cancelFrame,
2065
- onResume: () => coreRef.current?.resumed()
2084
+ onResume: () => {
2085
+ if (!optsRef.current.clock) coreRef.current?.resumed();
2086
+ }
2066
2087
  }
2067
2088
  );
2068
2089
  const frameLoopRef = useLatest(frameLoop);
@@ -2081,10 +2102,18 @@ function useAnimator(opts = {}) {
2081
2102
  }
2082
2103
  return true;
2083
2104
  };
2084
- const now = () => (optsRef.current.now ?? (typeof performance !== "undefined" ? performance.now.bind(performance) : Date.now))();
2105
+ const now = () => {
2106
+ const handle2 = optsRef.current.clock;
2107
+ if (handle2) return handle2.clock.elapsed;
2108
+ return (optsRef.current.now ?? (typeof performance !== "undefined" ? performance.now.bind(performance) : Date.now))();
2109
+ };
2085
2110
  return createAnimatorCore({
2086
2111
  now,
2087
- requestFrame: () => frameLoopRef.current.request(),
2112
+ // On a clock, a frame asked for from inside one comes with the clock's next move: one shown
2113
+ // at the same time would only ask again.
2114
+ requestFrame: () => {
2115
+ if (!followingRef.current) frameLoopRef.current.request();
2116
+ },
2088
2117
  cancelFrame: () => frameLoopRef.current.cancel(),
2089
2118
  tripwire,
2090
2119
  colorOverrides: colorOverrides.current
@@ -2093,6 +2122,14 @@ function useAnimator(opts = {}) {
2093
2122
  useInsertionEffect(() => {
2094
2123
  coreRef.current = core;
2095
2124
  }, [core]);
2125
+ const handle = opts.clock;
2126
+ useEffect(() => {
2127
+ if (!handle) return;
2128
+ core.resumed();
2129
+ return handle.onFrame(() => {
2130
+ if (core.api.isActive()) follow(handle.clock);
2131
+ });
2132
+ }, [core, handle, follow]);
2096
2133
  const { api } = core;
2097
2134
  useEffect(() => {
2098
2135
  const overrides = colorOverrides.current;