tiny-essentials 1.17.0 → 1.17.1

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.
@@ -7041,6 +7041,19 @@ const {
7041
7041
  areElsCollRight: TinyHtml_areElsCollRight,
7042
7042
  } = collision_namespaceObject;
7043
7043
 
7044
+ /**
7045
+ * Callback invoked on each animation frame with the current scroll position,
7046
+ * normalized animation time (`0` to `1`), and a completion flag.
7047
+ *
7048
+ * @typedef {(progress: { x: number, y: number, isComplete: boolean, time: number }) => void} OnScrollAnimation
7049
+ */
7050
+
7051
+ /**
7052
+ * A list of supported easing function names for smooth animations.
7053
+ *
7054
+ * @typedef {'linear' | 'easeInQuad' | 'easeOutQuad' | 'easeInOutQuad' | 'easeInCubic' | 'easeOutCubic' | 'easeInOutCubic'} Easings
7055
+ */
7056
+
7044
7057
  /**
7045
7058
  * Represents a raw Node element or an instance of TinyHtml.
7046
7059
  * This type is used to abstract interactions with both plain elements
@@ -9449,7 +9462,7 @@ class TinyHtml_TinyHtml {
9449
9462
  */
9450
9463
  static setWinScrollTop(value) {
9451
9464
  if (typeof value !== 'number') throw new TypeError('The value must be a number.');
9452
- window.scrollTo({ top: value });
9465
+ TinyHtml_TinyHtml.setScrollTop(window, value);
9453
9466
  }
9454
9467
 
9455
9468
  /**
@@ -9458,7 +9471,7 @@ class TinyHtml_TinyHtml {
9458
9471
  */
9459
9472
  static setWinScrollLeft(value) {
9460
9473
  if (typeof value !== 'number') throw new TypeError('The value must be a number.');
9461
- window.scrollTo({ left: value });
9474
+ TinyHtml_TinyHtml.setScrollLeft(window, value);
9462
9475
  }
9463
9476
 
9464
9477
  /**
@@ -9775,6 +9788,30 @@ class TinyHtml_TinyHtml {
9775
9788
 
9776
9789
  //////////////////////////////////////////////////
9777
9790
 
9791
+ /**
9792
+ * Applies an animation to one or multiple TinyElement instances.
9793
+ *
9794
+ * @param {TinyElement|TinyElement[]} el - A single TinyElement or an array of TinyElements to animate.
9795
+ * @param {Keyframe[] | PropertyIndexedKeyframes | null} keyframes - The keyframes used to define the animation.
9796
+ * @param {number | KeyframeAnimationOptions} [ops] - Timing or configuration options for the animation.
9797
+ * @returns {TinyElement|TinyElement[]}
9798
+ */
9799
+ static animate(el, keyframes, ops) {
9800
+ TinyHtml_TinyHtml._preElems(el, 'animate').forEach((elem) => elem.animate(keyframes, ops));
9801
+ return el;
9802
+ }
9803
+
9804
+ /**
9805
+ * Applies an animation to one or multiple TinyElement instances.
9806
+ *
9807
+ * @param {Keyframe[] | PropertyIndexedKeyframes | null} keyframes - The keyframes used to define the animation.
9808
+ * @param {number | KeyframeAnimationOptions} [ops] - Timing or configuration options for the animation.
9809
+ * @returns {TinyElement|TinyElement[]}
9810
+ */
9811
+ animate(keyframes, ops) {
9812
+ return TinyHtml_TinyHtml.animate(this, keyframes, ops);
9813
+ }
9814
+
9778
9815
  /**
9779
9816
  * Gets the offset of the element relative to the document.
9780
9817
  * @param {TinyElement} el - Target element.
@@ -9930,26 +9967,147 @@ class TinyHtml_TinyHtml {
9930
9967
  }
9931
9968
 
9932
9969
  /**
9933
- * Sets the vertical scroll position.
9934
- * @param {TinyElementAndWindow|TinyElementAndWindow[]} el - Element or window.
9935
- * @param {number} value - Scroll top value.
9936
- * @returns {TinyElementAndWindow|TinyElementAndWindow[]}
9970
+ * Collection of easing functions used for scroll and animation calculations.
9971
+ * Each function receives a normalized time value (`t` from 0 to 1) and returns the eased progress.
9972
+ *
9973
+ * @type {Record<string, (t: number) => number>}
9937
9974
  */
9938
- static setScrollTop(el, value) {
9939
- if (typeof value !== 'number') throw new TypeError('ScrollTop value must be a number.');
9940
- TinyHtml_TinyHtml._preElemsAndWindow(el, 'setScrollTop').forEach((elem) => {
9941
- if (TinyHtml_TinyHtml.isWindow(elem)) {
9942
- elem.scrollTo(elem.pageXOffset, value);
9975
+ static easings = {
9976
+ linear: (t) => t,
9977
+ easeInQuad: (t) => t * t,
9978
+ easeOutQuad: (t) => t * (2 - t),
9979
+ easeInOutQuad: (t) => (t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t),
9980
+ easeInCubic: (t) => t * t * t,
9981
+ easeOutCubic: (t) => --t * t * t + 1,
9982
+ easeInOutCubic: (t) => (t < 0.5 ? 4 * t * t * t : (t - 1) * (2 * t - 2) * (2 * t - 2) + 1),
9983
+ };
9984
+
9985
+ /**
9986
+ * Smoothly scrolls one or more elements (or the window) to the specified X and Y coordinates
9987
+ * using a custom duration and easing function.
9988
+ *
9989
+ * If `duration` or a valid `easing` is not provided, the scroll will be performed immediately.
9990
+ *
9991
+ * @param {TinyElementAndWindow | TinyElementAndWindow[]} el - A single element, array of elements, or the window to scroll.
9992
+ * @param {Object} [settings={}] - Configuration object for the scroll animation.
9993
+ * @param {number} [settings.targetX] - The horizontal scroll target in pixels.
9994
+ * @param {number} [settings.targetY] - The vertical scroll target in pixels.
9995
+ * @param {number} [settings.duration] - The duration of the animation in milliseconds.
9996
+ * @param {Easings} [settings.easing] - The easing function name to use for the scroll animation.
9997
+ * @param {OnScrollAnimation} [settings.onAnimation] - Optional callback invoked on each animation
9998
+ * frame with the current scroll position, normalized animation time (`0` to `1`), and a completion flag.
9999
+ * @returns {TinyElementAndWindow|TinyElementAndWindow[]}
10000
+ * @throws {TypeError} If `el` is not a valid element, array, or window.
10001
+ * @throws {TypeError} If `targetX` or `targetY` is defined but not a number.
10002
+ * @throws {TypeError} If `duration` is defined but not a number.
10003
+ * @throws {TypeError} If `easing` is defined but not a valid easing function name.
10004
+ * @throws {TypeError} If `onAnimation` is defined but not a function.
10005
+ */
10006
+ static scrollToXY(el, { targetX, targetY, duration, easing, onAnimation } = {}) {
10007
+ if (targetX !== undefined && typeof targetX !== 'number')
10008
+ throw new TypeError('`targetX` must be a number if provided.');
10009
+ if (targetY !== undefined && typeof targetY !== 'number')
10010
+ throw new TypeError('`targetY` must be a number if provided.');
10011
+ if (duration !== undefined && typeof duration !== 'number')
10012
+ throw new TypeError('`duration` must be a number if provided.');
10013
+ if (easing !== undefined && typeof easing !== 'string')
10014
+ throw new TypeError('`easing` must be a string if provided.');
10015
+ if (easing !== undefined && typeof TinyHtml_TinyHtml.easings[easing] !== 'function')
10016
+ throw new TypeError(`Unknown easing function: "${easing}".`);
10017
+ if (onAnimation !== undefined && typeof onAnimation !== 'function')
10018
+ throw new TypeError('`onAnimation` must be a function if provided.');
10019
+
10020
+ /**
10021
+ * Performs an instant scroll to the given coordinates.
10022
+ *
10023
+ * @param {ElementAndWindow} elem - The element or window to scroll.
10024
+ * @param {number} newX - The final horizontal scroll position.
10025
+ * @param {number} newY - The final vertical scroll position.
10026
+ * @param {number} time - Normalized progress value.
10027
+ */
10028
+ const executeScroll = (elem, newX, newY, time) => {
10029
+ if (elem instanceof Window) {
10030
+ window.scrollTo(newX, newY);
9943
10031
  } else if (elem.nodeType === 9) {
9944
10032
  // @ts-ignore
9945
- elem.defaultView.scrollTo(elem.defaultView.pageXOffset, value);
10033
+ elem.defaultView.scrollTo(newX, newY);
9946
10034
  } else {
9947
- elem.scrollTop = value;
10035
+ const startX = elem instanceof Window ? window.scrollX : elem.scrollLeft;
10036
+ const startY = elem instanceof Window ? window.scrollY : elem.scrollTop;
10037
+ if (startX !== newX) elem.scrollLeft = newX;
10038
+ if (startY !== newY) elem.scrollTop = newY;
10039
+ }
10040
+ if (typeof onAnimation === 'function')
10041
+ onAnimation({ x: newX, y: newY, isComplete: time >= 1, time });
10042
+ };
10043
+
10044
+ TinyHtml_TinyHtml._preElemsAndWindow(el, 'scrollToXY').forEach((elem) => {
10045
+ const startX = elem instanceof Window ? window.scrollX : elem.scrollLeft;
10046
+ const startY = elem instanceof Window ? window.scrollY : elem.scrollTop;
10047
+ const targX = targetX ?? startX;
10048
+ const targY = targetY ?? startY;
10049
+
10050
+ const changeX = targX - startX;
10051
+ const changeY = targY - startY;
10052
+
10053
+ const ease = (typeof easing === 'string' && TinyHtml_TinyHtml.easings[easing]) || null;
10054
+ if (typeof duration !== 'number' || typeof ease !== 'function')
10055
+ return executeScroll(elem, targX, targY, 1);
10056
+ const startTime = performance.now();
10057
+ const dur = duration ?? 0;
10058
+
10059
+ /**
10060
+ * Animates the scroll position based on easing and time.
10061
+ *
10062
+ * @param {number} currentTime - Timestamp provided by requestAnimationFrame.
10063
+ */
10064
+ function animateScroll(currentTime) {
10065
+ if (typeof ease !== 'function') return;
10066
+ const time = Math.min(1, (currentTime - startTime) / dur);
10067
+ const easedTime = ease(time);
10068
+
10069
+ const newX = startX + changeX * easedTime;
10070
+ const newY = startY + changeY * easedTime;
10071
+ executeScroll(elem, newX, newY, time);
10072
+
10073
+ if (time < 1) requestAnimationFrame(animateScroll);
9948
10074
  }
10075
+
10076
+ requestAnimationFrame(animateScroll);
9949
10077
  });
9950
10078
  return el;
9951
10079
  }
9952
10080
 
10081
+ /**
10082
+ * Smoothly scrolls one or more elements (or the window) to the specified X and Y coordinates
10083
+ * using a custom duration and easing function.
10084
+ *
10085
+ * If `duration` or a valid `easing` is not provided, the scroll will be performed immediately.
10086
+ *
10087
+ * @param {Object} [settings={}] - Configuration object for the scroll animation.
10088
+ * @param {number} [settings.targetX] - The horizontal scroll target in pixels.
10089
+ * @param {number} [settings.targetY] - The vertical scroll target in pixels.
10090
+ * @param {number} [settings.duration] - The duration of the animation in milliseconds.
10091
+ * @param {Easings} [settings.easing] - The easing function name to use for the scroll animation.
10092
+ * @param {OnScrollAnimation} [settings.onAnimation] - Optional callback invoked on each animation
10093
+ * frame with the current scroll position, normalized animation time (`0` to `1`), and a completion flag.
10094
+ * @returns {TinyElementAndWindow|TinyElementAndWindow[]}
10095
+ */
10096
+ scrollToXY({ targetX, targetY, duration, easing, onAnimation } = {}) {
10097
+ return TinyHtml_TinyHtml.scrollToXY(this, { targetX, targetY, duration, easing, onAnimation });
10098
+ }
10099
+
10100
+ /**
10101
+ * Sets the vertical scroll position.
10102
+ * @param {TinyElementAndWindow|TinyElementAndWindow[]} el - Element or window.
10103
+ * @param {number} value - Scroll top value.
10104
+ * @returns {TinyElementAndWindow|TinyElementAndWindow[]}
10105
+ */
10106
+ static setScrollTop(el, value) {
10107
+ if (typeof value !== 'number') throw new TypeError('ScrollTop value must be a number.');
10108
+ return TinyHtml_TinyHtml.scrollToXY(el, { targetY: value });
10109
+ }
10110
+
9953
10111
  /**
9954
10112
  * Sets the vertical scroll position.
9955
10113
  * @param {number} value - Scroll top value.
@@ -9967,17 +10125,7 @@ class TinyHtml_TinyHtml {
9967
10125
  */
9968
10126
  static setScrollLeft(el, value) {
9969
10127
  if (typeof value !== 'number') throw new TypeError('ScrollLeft value must be a number.');
9970
- TinyHtml_TinyHtml._preElemsAndWindow(el, 'setScrollLeft').forEach((elem) => {
9971
- if (TinyHtml_TinyHtml.isWindow(elem)) {
9972
- elem.scrollTo(value, elem.pageYOffset);
9973
- } else if (elem.nodeType === 9) {
9974
- // @ts-ignore
9975
- elem.defaultView.scrollTo(value, elem.defaultView.pageYOffset);
9976
- } else {
9977
- elem.scrollLeft = value;
9978
- }
9979
- });
9980
- return el;
10128
+ return TinyHtml_TinyHtml.scrollToXY(el, { targetX: value });
9981
10129
  }
9982
10130
 
9983
10131
  /**