@needle-tools/engine 4.9.0-next.43185 → 4.9.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 (30) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/README.md +1 -1
  3. package/components.needle.json +1 -1
  4. package/dist/{needle-engine.bundle-CZSmogSi.min.js → needle-engine.bundle-B1gr_nQ0.min.js} +119 -119
  5. package/dist/{needle-engine.bundle-Bs33m9iI.js → needle-engine.bundle-BikYBC35.js} +5444 -5405
  6. package/dist/{needle-engine.bundle-Bf4EhG38.umd.cjs → needle-engine.bundle-DrlDKOar.umd.cjs} +128 -128
  7. package/dist/needle-engine.js +2 -2
  8. package/dist/needle-engine.min.js +1 -1
  9. package/dist/needle-engine.umd.cjs +1 -1
  10. package/lib/engine-components/Component.d.ts +8 -5
  11. package/lib/engine-components/Component.js +8 -5
  12. package/lib/engine-components/Component.js.map +1 -1
  13. package/lib/engine-components/physics/Attractor.d.ts +12 -0
  14. package/lib/engine-components/physics/Attractor.js +14 -2
  15. package/lib/engine-components/physics/Attractor.js.map +1 -1
  16. package/lib/engine-components/web/Clickthrough.d.ts +1 -0
  17. package/lib/engine-components/web/Clickthrough.js +4 -2
  18. package/lib/engine-components/web/Clickthrough.js.map +1 -1
  19. package/lib/engine-components/web/CursorFollow.d.ts +9 -0
  20. package/lib/engine-components/web/CursorFollow.js +17 -0
  21. package/lib/engine-components/web/CursorFollow.js.map +1 -1
  22. package/lib/engine-components/web/ScrollFollow.d.ts +29 -3
  23. package/lib/engine-components/web/ScrollFollow.js +91 -20
  24. package/lib/engine-components/web/ScrollFollow.js.map +1 -1
  25. package/package.json +2 -2
  26. package/src/engine-components/Component.ts +8 -5
  27. package/src/engine-components/physics/Attractor.ts +14 -4
  28. package/src/engine-components/web/Clickthrough.ts +6 -2
  29. package/src/engine-components/web/CursorFollow.ts +17 -2
  30. package/src/engine-components/web/ScrollFollow.ts +93 -23
@@ -7,14 +7,28 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
7
7
  import { serializable } from "../../engine/engine_serialization_decorator.js";
8
8
  import { getTempVector } from "../../engine/engine_three_utils.js";
9
9
  import { Behaviour } from "../Component.js";
10
+ /**
11
+ * The CursorFollow component makes the object follow the cursor (or touch) position on screen.
12
+ */
10
13
  export class CursorFollow extends Behaviour {
11
14
  /**
12
15
  * Damping for the movement, set to 0 for instant movement
13
16
  * @default 0
14
17
  */
15
18
  damping = 0;
19
+ /**
20
+ * If true, the initial distance to the camera is maintained when following the cursor.
21
+ * @default true
22
+ */
23
+ keepDistance = true;
24
+ awake() {
25
+ this._distance = -1;
26
+ }
16
27
  _distance = -1;
17
28
  updateDistance() {
29
+ if (this.keepDistance && this._distance !== -1) {
30
+ return;
31
+ }
18
32
  this._distance = this.gameObject.worldPosition.distanceTo(this.context.mainCamera.worldPosition);
19
33
  }
20
34
  /** @internal */
@@ -43,4 +57,7 @@ export class CursorFollow extends Behaviour {
43
57
  __decorate([
44
58
  serializable()
45
59
  ], CursorFollow.prototype, "damping", void 0);
60
+ __decorate([
61
+ serializable()
62
+ ], CursorFollow.prototype, "keepDistance", void 0);
46
63
  //# sourceMappingURL=CursorFollow.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"CursorFollow.js","sourceRoot":"","sources":["../../../src/engine-components/web/CursorFollow.ts"],"names":[],"mappings":";;;;;;AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,gDAAgD,CAAC;AAC9E,OAAO,EAAE,aAAa,EAAE,MAAM,oCAAoC,CAAC;AACnE,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAG5C,MAAM,OAAO,YAAa,SAAQ,SAAS;IAEvC;;;OAGG;IAEH,OAAO,GAAW,CAAC,CAAC;IAGZ,SAAS,GAAW,CAAC,CAAC,CAAC;IAC/B,cAAc;QACV,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,UAAU,CAAC,aAAa,CAAC,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,aAAa,CAAC,CAAC;IACrG,CAAC;IAGD,gBAAgB;IAChB,MAAM;QACF,8DAA8D;QAC9D,IAAI,CAAC,cAAc,EAAE,CAAC;QAEtB,yEAAyE;QACzE,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,eAAe,CAAC;QAClD,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC;QACvC,MAAM,cAAc,GAAG,MAAM,CAAC,aAAa,CAAC;QAE5C,iDAAiD;QACjD,MAAM,YAAY,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;QAC5E,YAAY,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC,SAAS,EAAE,CAAC;QAE7C,oDAAoD;QACpD,MAAM,WAAW,GAAG,YAAY,CAAC,cAAc,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;QACpF,IAAI,IAAI,CAAC,OAAO,GAAG,CAAC,EAAE;YAClB,MAAM,GAAG,GAAG,IAAI,CAAC,UAAU,CAAC,aAAa,CAAC;YAC1C,GAAG,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC;YAClE,IAAI,CAAC,UAAU,CAAC,aAAa,GAAG,GAAG,CAAC;SACvC;aACI;YACD,IAAI,CAAC,UAAU,CAAC,aAAa,GAAG,WAAW,CAAC;SAC/C;IAEL,CAAC;CAEJ;AApCG;IADC,YAAY,EAAE;6CACK"}
1
+ {"version":3,"file":"CursorFollow.js","sourceRoot":"","sources":["../../../src/engine-components/web/CursorFollow.ts"],"names":[],"mappings":";;;;;;AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,gDAAgD,CAAC;AAC9E,OAAO,EAAE,aAAa,EAAE,MAAM,oCAAoC,CAAC;AACnE,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAE5C;;GAEG;AACH,MAAM,OAAO,YAAa,SAAQ,SAAS;IAEvC;;;OAGG;IAEH,OAAO,GAAW,CAAC,CAAC;IAEpB;;;OAGG;IAEH,YAAY,GAAY,IAAI,CAAC;IAE7B,KAAK;QACD,IAAI,CAAC,SAAS,GAAG,CAAC,CAAC,CAAC;IACxB,CAAC;IAEO,SAAS,GAAW,CAAC,CAAC,CAAC;IAE/B,cAAc;QACV,IAAI,IAAI,CAAC,YAAY,IAAI,IAAI,CAAC,SAAS,KAAK,CAAC,CAAC,EAAE;YAC5C,OAAO;SACV;QACD,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,UAAU,CAAC,aAAa,CAAC,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,aAAa,CAAC,CAAC;IACrG,CAAC;IAED,gBAAgB;IAChB,MAAM;QACF,8DAA8D;QAC9D,IAAI,CAAC,cAAc,EAAE,CAAC;QAEtB,yEAAyE;QACzE,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,eAAe,CAAC;QAClD,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC;QACvC,MAAM,cAAc,GAAG,MAAM,CAAC,aAAa,CAAC;QAE5C,iDAAiD;QACjD,MAAM,YAAY,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;QAC5E,YAAY,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC,SAAS,EAAE,CAAC;QAE7C,oDAAoD;QACpD,MAAM,WAAW,GAAG,YAAY,CAAC,cAAc,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;QACpF,IAAI,IAAI,CAAC,OAAO,GAAG,CAAC,EAAE;YAClB,MAAM,GAAG,GAAG,IAAI,CAAC,UAAU,CAAC,aAAa,CAAC;YAC1C,GAAG,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC;YAClE,IAAI,CAAC,UAAU,CAAC,aAAa,GAAG,GAAG,CAAC;SACvC;aACI;YACD,IAAI,CAAC,UAAU,CAAC,aAAa,GAAG,WAAW,CAAC;SAC/C;IAEL,CAAC;CAEJ;AAjDG;IADC,YAAY,EAAE;6CACK;AAOpB;IADC,YAAY,EAAE;kDACc"}
@@ -11,9 +11,22 @@ type ScrollFollowEvent = {
11
11
  preventDefault: () => void;
12
12
  defaultPrevented: boolean;
13
13
  };
14
+ /**
15
+ * The ScrollFollow component allows you to link the scroll position of the page (or a specific element) to one or more target objects.
16
+ * This can be used to create scroll-based animations, audio playback, or other effects. For example you can link the scroll position to a timeline (PlayableDirector) to create scroll-based storytelling effects or to an Animator component to change the animation state based on scroll.
17
+ *
18
+ * @link Example at https://scrollytelling-2-z23hmxby7c6x-u30ld.needle.run/
19
+ */
14
20
  export declare class ScrollFollow extends Behaviour {
15
21
  /**
16
22
  * Target object(s) to follow the scroll position of the page. If null, the main camera is used.
23
+ *
24
+ * Supported target types:
25
+ * - PlayableDirector (timeline), the scroll position will be mapped to the timeline time
26
+ * - Animator, the scroll position will be set to a float parameter named "scroll"
27
+ * - Animation, the scroll position will be mapped to the animation time
28
+ * - AudioSource, the scroll position will be mapped to the audio time
29
+ * - Any object with a `scroll` property (number or function)
17
30
  */
18
31
  target: object[] | object | null;
19
32
  /**
@@ -21,7 +34,18 @@ export declare class ScrollFollow extends Behaviour {
21
34
  * @default 0
22
35
  */
23
36
  damping: number;
24
- mode: "window";
37
+ /**
38
+ * If true, the scroll value will be inverted (e.g. scrolling down will result in a value of 0)
39
+ * @default false
40
+ */
41
+ invert: boolean;
42
+ /**
43
+ * If set, the scroll position will be read from the specified element instead of the window.
44
+ * Use a CSS selector to specify the element, e.g. `#my-scrollable-div` or `.scroll-container`.
45
+ * @default null
46
+ */
47
+ htmlSelector: string | null;
48
+ private mode;
25
49
  /**
26
50
  * Event fired when the scroll position changes
27
51
  */
@@ -39,7 +63,9 @@ export declare class ScrollFollow extends Behaviour {
39
63
  onDisable(): void;
40
64
  /** @internal */
41
65
  lateUpdate(): void;
42
- private updateCurrentScroll;
43
- private applyScroll;
66
+ private _lastSelectorValue;
67
+ private _lastSelectorElement;
68
+ private updateCurrentScrollValue;
69
+ private static applyScroll;
44
70
  }
45
71
  export {};
@@ -5,16 +5,33 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
5
5
  return c > 3 && r && Object.defineProperty(target, key, r), r;
6
6
  };
7
7
  import { Object3D } from "three";
8
- import { serializable } from "../../engine/engine_serialization.js";
9
8
  import { Mathf } from "../../engine/engine_math.js";
10
- import { Behaviour } from "../Component.js";
11
- import { PlayableDirector } from "../timeline/PlayableDirector.js";
12
- import { EventList } from "../EventList.js";
9
+ import { serializable } from "../../engine/engine_serialization.js";
10
+ import { getBoundingBox } from "../../engine/engine_three_utils.js";
13
11
  import { Animation } from "../Animation.js";
12
+ import { Animator } from "../Animator.js";
14
13
  import { AudioSource } from "../AudioSource.js";
14
+ import { Behaviour } from "../Component.js";
15
+ import { EventList } from "../EventList.js";
16
+ import { Light } from "../Light.js";
17
+ import { SplineWalker } from "../splines/SplineWalker.js";
18
+ import { PlayableDirector } from "../timeline/PlayableDirector.js";
19
+ /**
20
+ * The ScrollFollow component allows you to link the scroll position of the page (or a specific element) to one or more target objects.
21
+ * This can be used to create scroll-based animations, audio playback, or other effects. For example you can link the scroll position to a timeline (PlayableDirector) to create scroll-based storytelling effects or to an Animator component to change the animation state based on scroll.
22
+ *
23
+ * @link Example at https://scrollytelling-2-z23hmxby7c6x-u30ld.needle.run/
24
+ */
15
25
  export class ScrollFollow extends Behaviour {
16
26
  /**
17
27
  * Target object(s) to follow the scroll position of the page. If null, the main camera is used.
28
+ *
29
+ * Supported target types:
30
+ * - PlayableDirector (timeline), the scroll position will be mapped to the timeline time
31
+ * - Animator, the scroll position will be set to a float parameter named "scroll"
32
+ * - Animation, the scroll position will be mapped to the animation time
33
+ * - AudioSource, the scroll position will be mapped to the audio time
34
+ * - Any object with a `scroll` property (number or function)
18
35
  */
19
36
  target = null;
20
37
  /**
@@ -22,6 +39,17 @@ export class ScrollFollow extends Behaviour {
22
39
  * @default 0
23
40
  */
24
41
  damping = 0;
42
+ /**
43
+ * If true, the scroll value will be inverted (e.g. scrolling down will result in a value of 0)
44
+ * @default false
45
+ */
46
+ invert = false;
47
+ /**
48
+ * If set, the scroll position will be read from the specified element instead of the window.
49
+ * Use a CSS selector to specify the element, e.g. `#my-scrollable-div` or `.scroll-container`.
50
+ * @default null
51
+ */
52
+ htmlSelector = null;
25
53
  mode = "window";
26
54
  /**
27
55
  * Event fired when the scroll position changes
@@ -38,20 +66,23 @@ export class ScrollFollow extends Behaviour {
38
66
  applied_value = -1;
39
67
  /** @internal */
40
68
  onEnable() {
41
- window.addEventListener("wheel", this.updateCurrentScroll, { passive: true });
69
+ window.addEventListener("wheel", this.updateCurrentScrollValue, { passive: true });
42
70
  this.applied_value = -1;
43
- this.updateCurrentScroll();
44
71
  }
45
72
  /** @internal */
46
73
  onDisable() {
47
- window.removeEventListener("wheel", this.updateCurrentScroll);
74
+ window.removeEventListener("wheel", this.updateCurrentScrollValue);
48
75
  }
49
76
  /** @internal */
50
77
  lateUpdate() {
78
+ this.updateCurrentScrollValue();
51
79
  // apply damping if any
52
80
  if (this.damping > 0) {
53
81
  this.current_value = Mathf.lerp(this.current_value, this.target_value, this.context.time.deltaTime / this.damping);
54
82
  }
83
+ else {
84
+ this.current_value = this.target_value;
85
+ }
55
86
  if (this.current_value !== this.applied_value) {
56
87
  this.applied_value = this.current_value;
57
88
  let defaultPrevented = false;
@@ -69,50 +100,84 @@ export class ScrollFollow extends Behaviour {
69
100
  }
70
101
  // if not prevented apply scroll
71
102
  if (!defaultPrevented) {
103
+ const value = this.invert ? 1 - this.current_value : this.current_value;
72
104
  // apply scroll to target(s)
73
105
  if (Array.isArray(this.target)) {
74
- this.target.forEach(t => t && this.applyScroll(t));
106
+ this.target.forEach(t => t && ScrollFollow.applyScroll(t, value));
75
107
  }
76
108
  else if (this.target) {
77
- this.applyScroll(this.target);
109
+ ScrollFollow.applyScroll(this.target, value);
78
110
  }
79
111
  }
80
112
  }
81
113
  }
82
- updateCurrentScroll = () => {
114
+ _lastSelectorValue = null;
115
+ _lastSelectorElement = null;
116
+ updateCurrentScrollValue = () => {
83
117
  switch (this.mode) {
84
118
  case "window":
85
- this.target_value = window.scrollY / (document.body.scrollHeight - window.innerHeight);
119
+ if (this.htmlSelector?.length) {
120
+ if (this.htmlSelector !== this._lastSelectorValue) {
121
+ this._lastSelectorElement = document.querySelector(this.htmlSelector);
122
+ this._lastSelectorValue = this.htmlSelector;
123
+ }
124
+ if (this._lastSelectorElement) {
125
+ const rect = this._lastSelectorElement.getBoundingClientRect();
126
+ this.target_value = -rect.top / (rect.height - window.innerHeight);
127
+ if (isNaN(this.target_value) || !isFinite(this.target_value))
128
+ this.target_value = 0;
129
+ break;
130
+ }
131
+ }
132
+ else {
133
+ this.target_value = window.scrollY / (document.body.scrollHeight - window.innerHeight);
134
+ }
86
135
  if (isNaN(this.target_value) || !isFinite(this.target_value))
87
136
  this.target_value = 0;
88
137
  break;
89
138
  }
90
- if (this.damping <= 0) {
91
- this.current_value = this.target_value;
92
- }
93
139
  };
94
- applyScroll(target) {
140
+ static applyScroll(target, value) {
95
141
  if (!target)
96
142
  return;
97
143
  if (target instanceof PlayableDirector) {
98
- target.time = this.current_value * target.duration;
144
+ target.time = value * target.duration;
99
145
  if (!target.isPlaying)
100
146
  target.evaluate();
101
147
  }
148
+ else if (target instanceof Animator) {
149
+ target.setFloat("scroll", value);
150
+ }
102
151
  else if (target instanceof Animation) {
103
- target.time = this.current_value * target.duration;
152
+ target.time = value * target.duration;
104
153
  }
105
154
  else if (target instanceof AudioSource) {
106
155
  if (!target.duration)
107
156
  return;
108
- target.time = this.current_value * target.duration;
157
+ target.time = value * target.duration;
158
+ }
159
+ else if (target instanceof SplineWalker) {
160
+ target.position01 = value;
161
+ }
162
+ else if (target instanceof Light) {
163
+ target.intensity = value;
164
+ }
165
+ else if (target instanceof Object3D) {
166
+ // When objects are assigned they're expected to move vertically based on scroll
167
+ if (target["needle:scrollbounds"] === undefined) {
168
+ target["needle:scrollbounds"] = getBoundingBox(target) || null;
169
+ }
170
+ const bounds = target["needle:scrollbounds"];
171
+ if (bounds) {
172
+ target.position.y = -bounds.min.y - value * (bounds.max.y - bounds.min.y);
173
+ }
109
174
  }
110
175
  else if ("scroll" in target) {
111
176
  if (typeof target.scroll === "number") {
112
- target.scroll = this.current_value;
177
+ target.scroll = value;
113
178
  }
114
179
  else if (typeof target.scroll === "function") {
115
- target.scroll(this.current_value);
180
+ target.scroll(value);
116
181
  }
117
182
  }
118
183
  }
@@ -123,6 +188,12 @@ __decorate([
123
188
  __decorate([
124
189
  serializable()
125
190
  ], ScrollFollow.prototype, "damping", void 0);
191
+ __decorate([
192
+ serializable()
193
+ ], ScrollFollow.prototype, "invert", void 0);
194
+ __decorate([
195
+ serializable()
196
+ ], ScrollFollow.prototype, "htmlSelector", void 0);
126
197
  __decorate([
127
198
  serializable()
128
199
  ], ScrollFollow.prototype, "mode", void 0);
@@ -1 +1 @@
1
- {"version":3,"file":"ScrollFollow.js","sourceRoot":"","sources":["../../../src/engine-components/web/ScrollFollow.ts"],"names":[],"mappings":";;;;;;AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACjC,OAAO,EAAE,YAAY,EAAE,MAAM,sCAAsC,CAAC;AACpE,OAAO,EAAE,KAAK,EAAE,MAAM,6BAA6B,CAAC;AACpD,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AACnE,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAchD,MAAM,OAAO,YAAa,SAAQ,SAAS;IAEvC;;OAEG;IAEH,MAAM,GAA6B,IAAI,CAAC;IAExC;;;OAGG;IAEH,OAAO,GAAW,CAAC,CAAC;IAGpB,IAAI,GAAa,QAAQ,CAAC;IAE1B;;OAEG;IAEH,OAAO,GAAiC,IAAI,SAAS,EAAqB,CAAC;IAE3E;;OAEG;IACH,IAAI,YAAY;QACZ,OAAO,IAAI,CAAC,aAAa,CAAC;IAC9B,CAAC;IAEO,aAAa,GAAW,CAAC,CAAC;IAC1B,YAAY,GAAW,CAAC,CAAC;IACzB,aAAa,GAAW,CAAC,CAAC,CAAC;IAEnC,gBAAgB;IAChB,QAAQ;QACJ,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,IAAI,CAAC,mBAAmB,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;QAC9E,IAAI,CAAC,aAAa,GAAG,CAAC,CAAC,CAAC;QACxB,IAAI,CAAC,mBAAmB,EAAE,CAAC;IAC/B,CAAC;IAED,gBAAgB;IAChB,SAAS;QACL,MAAM,CAAC,mBAAmB,CAAC,OAAO,EAAE,IAAI,CAAC,mBAAmB,CAAC,CAAC;IAClE,CAAC;IAED,gBAAgB;IAChB,UAAU;QAEN,uBAAuB;QACvB,IAAI,IAAI,CAAC,OAAO,GAAG,CAAC,EAAE;YAClB,IAAI,CAAC,aAAa,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,IAAI,CAAC,YAAY,EAAE,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC;SACtH;QAED,IAAI,IAAI,CAAC,aAAa,KAAK,IAAI,CAAC,aAAa,EAAE;YAC3C,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC,aAAa,CAAC;YAExC,IAAI,gBAAgB,GAAG,KAAK,CAAC;YAC7B,IAAI,IAAI,CAAC,OAAO,CAAC,aAAa,GAAG,CAAC,EAAE;gBAChC,oBAAoB;gBACpB,MAAM,KAAK,GAAsB;oBAC7B,IAAI,EAAE,QAAQ;oBACd,KAAK,EAAE,IAAI,CAAC,aAAa;oBACzB,SAAS,EAAE,IAAI;oBACf,cAAc,EAAE,GAAG,EAAE,GAAG,KAAK,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAAC,CAAC;oBACxD,gBAAgB,EAAE,KAAK;iBAC1B,CAAC;gBACF,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;gBAC3B,gBAAgB,GAAG,KAAK,CAAC,gBAAgB,CAAC;aAC7C;YAED,gCAAgC;YAChC,IAAI,CAAC,gBAAgB,EAAE;gBAEnB,4BAA4B;gBAC5B,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE;oBAC5B,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC;iBACtD;qBACI,IAAI,IAAI,CAAC,MAAM,EAAE;oBAClB,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;iBACjC;aACJ;SACJ;IACL,CAAC;IAGO,mBAAmB,GAAG,GAAG,EAAE;QAE/B,QAAQ,IAAI,CAAC,IAAI,EAAE;YACf,KAAK,QAAQ;gBACT,IAAI,CAAC,YAAY,GAAG,MAAM,CAAC,OAAO,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,YAAY,GAAG,MAAM,CAAC,WAAW,CAAC,CAAC;gBACvF,IAAI,KAAK,CAAC,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,YAAY,CAAC;oBAAE,IAAI,CAAC,YAAY,GAAG,CAAC,CAAC;gBACpF,MAAM;SACb;QAED,IAAI,IAAI,CAAC,OAAO,IAAI,CAAC,EAAE;YACnB,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC,YAAY,CAAC;SAC1C;IAEL,CAAC,CAAA;IAGO,WAAW,CAAC,MAAc;QAE9B,IAAI,CAAC,MAAM;YAAE,OAAO;QAEpB,IAAI,MAAM,YAAY,gBAAgB,EAAE;YACpC,MAAM,CAAC,IAAI,GAAG,IAAI,CAAC,aAAa,GAAG,MAAM,CAAC,QAAQ,CAAC;YACnD,IAAI,CAAC,MAAM,CAAC,SAAS;gBAAE,MAAM,CAAC,QAAQ,EAAE,CAAC;SAC5C;aACI,IAAI,MAAM,YAAY,SAAS,EAAE;YAClC,MAAM,CAAC,IAAI,GAAG,IAAI,CAAC,aAAa,GAAG,MAAM,CAAC,QAAQ,CAAC;SACtD;aACI,IAAI,MAAM,YAAY,WAAW,EAAE;YACpC,IAAI,CAAC,MAAM,CAAC,QAAQ;gBAAE,OAAO;YAC7B,MAAM,CAAC,IAAI,GAAG,IAAI,CAAC,aAAa,GAAG,MAAM,CAAC,QAAQ,CAAC;SACtD;aACI,IAAI,QAAQ,IAAI,MAAM,EAAE;YACzB,IAAI,OAAO,MAAM,CAAC,MAAM,KAAK,QAAQ,EAAE;gBACnC,MAAM,CAAC,MAAM,GAAG,IAAI,CAAC,aAAa,CAAC;aACtC;iBACI,IAAI,OAAO,MAAM,CAAC,MAAM,KAAK,UAAU,EAAE;gBAC1C,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;aACrC;SACJ;IACL,CAAC;CAEJ;AA1HG;IADC,YAAY,CAAC,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;4CACI;AAOxC;IADC,YAAY,EAAE;6CACK;AAGpB;IADC,YAAY,EAAE;0CACW;AAM1B;IADC,YAAY,CAAC,SAAS,CAAC;6CACmD"}
1
+ {"version":3,"file":"ScrollFollow.js","sourceRoot":"","sources":["../../../src/engine-components/web/ScrollFollow.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@needle-tools/engine",
3
- "version": "4.9.0-next.43185",
3
+ "version": "4.9.0",
4
4
  "description": "Needle Engine is a web-based runtime for 3D apps. It runs on your machine for development with great integrations into editors like Unity or Blender - and can be deployed onto any device! It is flexible, extensible and networking and XR are built-in.",
5
5
  "main": "dist/needle-engine.min.js",
6
6
  "exports": {
@@ -168,4 +168,4 @@
168
168
  "module": "lib/needle-engine.js",
169
169
  "typings": "lib/needle-engine.d.ts",
170
170
  "types": "lib/needle-engine.d.ts"
171
- }
171
+ }
@@ -543,22 +543,25 @@ export abstract class GameObject extends Object3D implements Object3D, IGameObje
543
543
 
544
544
 
545
545
  /**
546
- * Needle Engine component base class. Component's are the main building blocks of the Needle Engine.
546
+ * Needle Engine component's are the main building blocks of the Needle Engine.
547
547
  * Derive from {@link Behaviour} to implement your own using the provided lifecycle methods.
548
548
  * Components can be added to any {@link Object3D} using {@link addComponent} or {@link GameObject.addComponent}.
549
549
  *
550
- * The most common lifecycle methods are {@link update}, {@link awake}, {@link start}, {@link onEnable}, {@link onDisable} and {@link onDestroy}.
550
+ * **Component lifecycle event methods:**
551
+ * {@link awake}, {@link start}, {@link onEnable}, {@link onDisable}, {@link onDestroy}, {@link earlyUpdate}, {@link update}, {@link lateUpdate}, {@link onBeforeRender}, {@link onAfterRender}.
551
552
  *
552
- * XR specific callbacks include {@link onEnterXR}, {@link onLeaveXR}, {@link onUpdateXR}, {@link onXRControllerAdded} and {@link onXRControllerRemoved}.
553
+ * **XR event methods:**
554
+ * {@link onEnterXR}, {@link onLeaveXR}, {@link onUpdateXR}, {@link onXRControllerAdded} and {@link onXRControllerRemoved}.
553
555
  *
554
- * To receive pointer events implement {@link onPointerDown}, {@link onPointerUp}, {@link onPointerEnter}, {@link onPointerExit} and {@link onPointerMove}.
556
+ * **Input event methods:**
557
+ * {@link onPointerDown}, {@link onPointerUp}, {@link onPointerEnter}, {@link onPointerExit} and {@link onPointerMove}.
555
558
  *
556
559
  * @example
557
560
  * ```typescript
558
561
  * import { Behaviour } from "@needle-tools/engine";
559
562
  * export class MyComponent extends Behaviour {
560
563
  * start() {
561
- * console.log("Hello World");
564
+ * console.log("Hello World", this.gameObject.name);
562
565
  * }
563
566
  * update() {
564
567
  * console.log("Frame", this.context.time.frame);
@@ -1,9 +1,20 @@
1
+ import { serializable } from "../../engine/engine_serialization_decorator.js";
1
2
  import { Behaviour } from "../Component.js";
2
3
  import { Rigidbody } from "../RigidBody.js";
3
- import { serializable } from "../../engine/engine_serialization_decorator.js";
4
-
5
4
 
6
5
 
6
+ /**
7
+ * Used to attract Rigidbodies towards the position of this component.
8
+ * Add Rigidbodies to the `targets` array to have them be attracted.
9
+ * You can use negative strength values to create a repulsion effect.
10
+ *
11
+ * @example Attractor component attracting a Rigidbody
12
+ * ```ts
13
+ * const attractor = object.addComponent(Attractor);
14
+ * attractor.strength = 5; // positive value to attract
15
+ * attractor.radius = 10; // only attract within 10 units
16
+ * attractor.targets.push(rigidbody); // add the Rigidbody to be attracted
17
+ */
7
18
  export class Attractor extends Behaviour {
8
19
 
9
20
  @serializable()
@@ -15,11 +26,10 @@ export class Attractor extends Behaviour {
15
26
  @serializable(Rigidbody)
16
27
  targets: Rigidbody[] = [];
17
28
 
18
-
19
29
  update() {
20
30
  const wp = this.gameObject.worldPosition;
21
31
  const factor = -this.strength * this.context.time.deltaTime;
22
- this.targets.forEach(t => {
32
+ this.targets?.forEach(t => {
23
33
  if(!t) return;
24
34
  const dir = t.gameObject.worldPosition.sub(wp);
25
35
  const length = dir.length();
@@ -1,6 +1,6 @@
1
1
  import { NEPointerEvent } from "../../engine/engine_input.js";
2
- import { Behaviour } from "../Component.js";
3
2
  import { onStart } from "../../engine/engine_lifecycle_api.js";
3
+ import { Behaviour } from "../Component.js";
4
4
 
5
5
  // Automatically add ClickThrough component if "clickthrough" attribute is present on the needle-engine element
6
6
  onStart(ctx => {
@@ -23,6 +23,9 @@ onStart(ctx => {
23
23
  * @link Example https://stackblitz.com/~/github.com/needle-engine/sample-3d-over-html
24
24
  */
25
25
  export class ClickThrough extends Behaviour {
26
+
27
+ private _previousPointerEvents: string = 'all';
28
+
26
29
  onEnable() {
27
30
  // Register for pointer down and pointer move event
28
31
  this.context.input.addEventListener('pointerdown', this.onPointerEvent);
@@ -30,12 +33,13 @@ export class ClickThrough extends Behaviour {
30
33
  queue: 100,
31
34
  });
32
35
  window.addEventListener("touchend", this.onTouchEnd, { passive: true });
36
+ this._previousPointerEvents = this.context.domElement.style.pointerEvents;
33
37
  }
34
38
  onDisable() {
35
39
  this.context.input.removeEventListener('pointerdown', this.onPointerEvent);
36
40
  this.context.input.removeEventListener('pointermove', this.onPointerEvent);
37
41
  window.removeEventListener("touchend", this.onTouchEnd);
38
- this.context.domElement.style.pointerEvents = 'all';
42
+ this.context.domElement.style.pointerEvents = this._previousPointerEvents;
39
43
  }
40
44
  onPointerEnter() {
41
45
  /** do nothing, necessary to raycast children */
@@ -2,7 +2,9 @@ import { serializable } from "../../engine/engine_serialization_decorator.js";
2
2
  import { getTempVector } from "../../engine/engine_three_utils.js";
3
3
  import { Behaviour } from "../Component.js";
4
4
 
5
-
5
+ /**
6
+ * The CursorFollow component makes the object follow the cursor (or touch) position on screen.
7
+ */
6
8
  export class CursorFollow extends Behaviour {
7
9
 
8
10
  /**
@@ -12,13 +14,26 @@ export class CursorFollow extends Behaviour {
12
14
  @serializable()
13
15
  damping: number = 0;
14
16
 
17
+ /**
18
+ * If true, the initial distance to the camera is maintained when following the cursor.
19
+ * @default true
20
+ */
21
+ @serializable()
22
+ keepDistance: boolean = true;
23
+
24
+ awake() {
25
+ this._distance = -1;
26
+ }
15
27
 
16
28
  private _distance: number = -1;
29
+
17
30
  updateDistance() {
31
+ if (this.keepDistance && this._distance !== -1) {
32
+ return;
33
+ }
18
34
  this._distance = this.gameObject.worldPosition.distanceTo(this.context.mainCamera.worldPosition);
19
35
  }
20
36
 
21
-
22
37
  /** @internal */
23
38
  update() {
24
39
  // continuously update distance in case camera or object moves