@quenty/blend 2.2.0 → 2.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.
- package/CHANGELOG.md +20 -0
- package/package.json +14 -14
- package/src/Shared/Blend/Blend.lua +101 -10
- package/src/Shared/Blend/BlendDefaultProps.lua +100 -106
- package/src/Shared/Blend/SpringObject.lua +227 -0
- package/src/Shared/Test/BlendSingle.story.lua +45 -0
- package/test/scripts/Server/ServerMain.server.lua +3 -2
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,26 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
# [2.3.0](https://github.com/Quenty/NevermoreEngine/compare/@quenty/blend@2.2.1...@quenty/blend@2.3.0) (2022-01-17)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### Features
|
|
10
|
+
|
|
11
|
+
* Add a SpringObject to blend, which is a much heavier object than a spring, but simplifies API usage significantly ([cb59db0](https://github.com/Quenty/NevermoreEngine/commit/cb59db0ed4297ecec842b0820a485e1aa0c8ad70))
|
|
12
|
+
* Add Blend.Attached and fix children not being unsubscribed correctly (memory leak fix) ([f4fa4c2](https://github.com/Quenty/NevermoreEngine/commit/f4fa4c2ebce9be6e16a7ab1492afaf87fe81b8aa))
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
## [2.2.1](https://github.com/Quenty/NevermoreEngine/compare/@quenty/blend@2.2.0...@quenty/blend@2.2.1) (2022-01-16)
|
|
19
|
+
|
|
20
|
+
**Note:** Version bump only for package @quenty/blend
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
|
|
6
26
|
# [2.2.0](https://github.com/Quenty/NevermoreEngine/compare/@quenty/blend@2.1.1...@quenty/blend@2.2.0) (2022-01-07)
|
|
7
27
|
|
|
8
28
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@quenty/blend",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.3.0",
|
|
4
4
|
"description": "Declarative UI system.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"Roblox",
|
|
@@ -27,23 +27,23 @@
|
|
|
27
27
|
"access": "public"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@quenty/acceltween": "^2.0
|
|
31
|
-
"@quenty/brio": "^3.
|
|
32
|
-
"@quenty/instanceutils": "^3.
|
|
33
|
-
"@quenty/loader": "^3.
|
|
34
|
-
"@quenty/maid": "^2.0
|
|
35
|
-
"@quenty/promise": "^3.
|
|
36
|
-
"@quenty/rx": "^3.
|
|
30
|
+
"@quenty/acceltween": "^2.1.0",
|
|
31
|
+
"@quenty/brio": "^3.8.0",
|
|
32
|
+
"@quenty/instanceutils": "^3.8.0",
|
|
33
|
+
"@quenty/loader": "^3.4.0",
|
|
34
|
+
"@quenty/maid": "^2.1.0",
|
|
35
|
+
"@quenty/promise": "^3.6.0",
|
|
36
|
+
"@quenty/rx": "^3.8.0",
|
|
37
37
|
"@quenty/signal": "^2.1.0",
|
|
38
|
-
"@quenty/spring": "^3.
|
|
38
|
+
"@quenty/spring": "^3.3.0",
|
|
39
39
|
"@quenty/steputils": "^3.0.1",
|
|
40
40
|
"@quenty/string": "^2.2.1",
|
|
41
|
-
"@quenty/valuebaseutils": "^3.
|
|
42
|
-
"@quenty/valueobject": "^3.
|
|
41
|
+
"@quenty/valuebaseutils": "^3.8.0",
|
|
42
|
+
"@quenty/valueobject": "^3.8.0"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
|
-
"@quenty/contentproviderutils": "^3.
|
|
46
|
-
"@quenty/playerthumbnailutils": "^3.
|
|
45
|
+
"@quenty/contentproviderutils": "^3.6.0",
|
|
46
|
+
"@quenty/playerthumbnailutils": "^3.7.0"
|
|
47
47
|
},
|
|
48
|
-
"gitHead": "
|
|
48
|
+
"gitHead": "c094ba8f4e128cdff08919d89de226d3d65247ce"
|
|
49
49
|
}
|
|
@@ -9,9 +9,11 @@ local AccelTween = require("AccelTween")
|
|
|
9
9
|
local BlendDefaultProps = require("BlendDefaultProps")
|
|
10
10
|
local Brio = require("Brio")
|
|
11
11
|
local Maid = require("Maid")
|
|
12
|
+
local MaidTaskUtils = require("MaidTaskUtils")
|
|
12
13
|
local Observable = require("Observable")
|
|
13
14
|
local Promise = require("Promise")
|
|
14
15
|
local Rx = require("Rx")
|
|
16
|
+
local BrioUtils = require("BrioUtils")
|
|
15
17
|
local RxInstanceUtils = require("RxInstanceUtils")
|
|
16
18
|
local RxValueBaseUtils = require("RxValueBaseUtils")
|
|
17
19
|
local Signal = require("Signal")
|
|
@@ -21,8 +23,6 @@ local StepUtils = require("StepUtils")
|
|
|
21
23
|
local ValueBaseUtils = require("ValueBaseUtils")
|
|
22
24
|
local ValueObject = require("ValueObject")
|
|
23
25
|
local ValueObjectUtils = require("ValueObjectUtils")
|
|
24
|
-
local MaidTaskUtils = require("MaidTaskUtils")
|
|
25
|
-
local RxBrioUtils = require("RxBrioUtils")
|
|
26
26
|
|
|
27
27
|
local Blend = {}
|
|
28
28
|
|
|
@@ -114,9 +114,9 @@ end
|
|
|
114
114
|
return verb .. " " .. name
|
|
115
115
|
end)
|
|
116
116
|
|
|
117
|
-
computed:Subscribe(function(sentence)
|
|
117
|
+
maid:GiveTask(computed:Subscribe(function(sentence)
|
|
118
118
|
print(sentence)
|
|
119
|
-
end) --> "hi alice"
|
|
119
|
+
end)) --> "hi alice"
|
|
120
120
|
|
|
121
121
|
nameState.Value = "bob" --> "hi bob"
|
|
122
122
|
verbState.Value = "bye" --> "bye bob"
|
|
@@ -212,6 +212,30 @@ function Blend.OnEvent(eventName)
|
|
|
212
212
|
end
|
|
213
213
|
end
|
|
214
214
|
|
|
215
|
+
--[=[
|
|
216
|
+
Uses the constructor to attach a class or resource to the actual object
|
|
217
|
+
for the lifetime of the subscription of that object.
|
|
218
|
+
@param constructor T
|
|
219
|
+
@return (parent: Instance) -> Observable<T>
|
|
220
|
+
]=]
|
|
221
|
+
function Blend.Attached(constructor)
|
|
222
|
+
return function(parent)
|
|
223
|
+
return Observable.new(function(sub)
|
|
224
|
+
local maid = Maid.new()
|
|
225
|
+
|
|
226
|
+
local resource = constructor(parent)
|
|
227
|
+
|
|
228
|
+
if MaidTaskUtils.isValidTask(resource) then
|
|
229
|
+
maid:GiveTask(resource)
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
sub:Fire(resource)
|
|
233
|
+
|
|
234
|
+
return maid
|
|
235
|
+
end)
|
|
236
|
+
end;
|
|
237
|
+
end
|
|
238
|
+
|
|
215
239
|
--[=[
|
|
216
240
|
Similiar to Fusion's ComputedPairs, where the changes are cached, and the lifetime limited.
|
|
217
241
|
@param source Observable<T> | any
|
|
@@ -427,7 +451,7 @@ end
|
|
|
427
451
|
function Blend.toEventObservable(value)
|
|
428
452
|
if Observable.isObservable(value) then
|
|
429
453
|
return value
|
|
430
|
-
elseif typeof(value) == "RBXScriptSignal" then
|
|
454
|
+
elseif typeof(value) == "RBXScriptSignal" or Signal.isSignal(value) then
|
|
431
455
|
return Rx.fromSignal(value)
|
|
432
456
|
else
|
|
433
457
|
return nil
|
|
@@ -451,7 +475,11 @@ function Blend.toEventHandler(value)
|
|
|
451
475
|
end
|
|
452
476
|
end
|
|
453
477
|
elseif type(value) == "table" then
|
|
454
|
-
if value
|
|
478
|
+
if Signal.isSignal(value) then
|
|
479
|
+
return function(...)
|
|
480
|
+
value:Fire(...)
|
|
481
|
+
end
|
|
482
|
+
elseif value.ClassName == "ValueObject" then
|
|
455
483
|
return function(result)
|
|
456
484
|
value.Value = result
|
|
457
485
|
end
|
|
@@ -520,6 +548,48 @@ function Blend.Children(parent, value)
|
|
|
520
548
|
end
|
|
521
549
|
end
|
|
522
550
|
|
|
551
|
+
--[=[
|
|
552
|
+
An event emitter that emits the instance that was actually created. This is
|
|
553
|
+
useful for a variety of things.
|
|
554
|
+
|
|
555
|
+
Using this to track an instance
|
|
556
|
+
|
|
557
|
+
```lua
|
|
558
|
+
local currentCamera = Blend.State()
|
|
559
|
+
|
|
560
|
+
return Blend.New "ViewportFrame" {
|
|
561
|
+
CurrentCamera = currentCamera;
|
|
562
|
+
[Blend.Children] = {
|
|
563
|
+
self._current;
|
|
564
|
+
Blend.New "Camera" {
|
|
565
|
+
[Blend.Instance] = currentCamera;
|
|
566
|
+
};
|
|
567
|
+
};
|
|
568
|
+
};
|
|
569
|
+
```
|
|
570
|
+
|
|
571
|
+
You can also use this to execute code against an instance.
|
|
572
|
+
|
|
573
|
+
```lua
|
|
574
|
+
return Blend.New "Frame" {
|
|
575
|
+
[Blend.Instance] = function(frame)
|
|
576
|
+
print("We got a new frame!")
|
|
577
|
+
end;
|
|
578
|
+
};
|
|
579
|
+
```
|
|
580
|
+
|
|
581
|
+
Note that if you subscribe twice to the resulting observable, the internal function
|
|
582
|
+
will execute twice.
|
|
583
|
+
|
|
584
|
+
@param parent Instance
|
|
585
|
+
@return Observable<Instance>
|
|
586
|
+
]=]
|
|
587
|
+
function Blend.Instance(parent)
|
|
588
|
+
return Observable.new(function(sub)
|
|
589
|
+
sub:Fire(parent)
|
|
590
|
+
end)
|
|
591
|
+
end
|
|
592
|
+
|
|
523
593
|
--[=[
|
|
524
594
|
Ensures the computed version of a value is limited by lifetime instead
|
|
525
595
|
of multiple. Used in conjunction with [Blend.Children] and [Blend.Computed].
|
|
@@ -532,7 +602,7 @@ end
|
|
|
532
602
|
Blend.New "ScreenGui" {
|
|
533
603
|
Parent = game.Players.LocalPlayer.PlayerGui;
|
|
534
604
|
[Blend.Children] = {
|
|
535
|
-
Blend.Single(
|
|
605
|
+
Blend.Single(Blend.Computed(percentVisible, function()
|
|
536
606
|
-- you generally would not want to do this anyway because this reconstructs a new frame
|
|
537
607
|
-- every frame.
|
|
538
608
|
|
|
@@ -550,7 +620,28 @@ end
|
|
|
550
620
|
@return Observable<Brio<Instance>>
|
|
551
621
|
@within Blend
|
|
552
622
|
]=]
|
|
553
|
-
Blend.Single
|
|
623
|
+
function Blend.Single(observable)
|
|
624
|
+
return Observable.new(function(sub)
|
|
625
|
+
local maid = Maid.new()
|
|
626
|
+
|
|
627
|
+
maid:GiveTask(observable:Subscribe(function(result)
|
|
628
|
+
if Brio.isBrio(result) then
|
|
629
|
+
local copy = BrioUtils.clone(result)
|
|
630
|
+
maid._current = copy
|
|
631
|
+
sub:Fire(copy)
|
|
632
|
+
return copy
|
|
633
|
+
end
|
|
634
|
+
|
|
635
|
+
local current = Brio.new(result)
|
|
636
|
+
maid._current = current
|
|
637
|
+
sub:Fire(current)
|
|
638
|
+
|
|
639
|
+
return current
|
|
640
|
+
end))
|
|
641
|
+
|
|
642
|
+
return maid
|
|
643
|
+
end)
|
|
644
|
+
end
|
|
554
645
|
|
|
555
646
|
--[=[
|
|
556
647
|
Observes children and ensures that the value is cleaned up
|
|
@@ -676,7 +767,7 @@ function Blend._observeChildren(value)
|
|
|
676
767
|
return Observable.new(function(sub)
|
|
677
768
|
local maid = Maid.new()
|
|
678
769
|
|
|
679
|
-
value:Subscribe(function(result)
|
|
770
|
+
maid:GiveTask(value:Subscribe(function(result)
|
|
680
771
|
if typeof(result) == "Instance" then
|
|
681
772
|
-- lifetime of subscription
|
|
682
773
|
maid:GiveTask(result)
|
|
@@ -710,7 +801,7 @@ function Blend._observeChildren(value)
|
|
|
710
801
|
sub:Fire(...)
|
|
711
802
|
end, function()
|
|
712
803
|
-- Drop completion, other inner components may have completed.
|
|
713
|
-
end)
|
|
804
|
+
end))
|
|
714
805
|
|
|
715
806
|
return maid
|
|
716
807
|
end)
|
|
@@ -29,109 +29,103 @@ SOFTWARE.
|
|
|
29
29
|
|
|
30
30
|
]]
|
|
31
31
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
SortOrder = Enum.SortOrder.LayoutOrder;
|
|
133
|
-
},
|
|
134
|
-
}
|
|
135
|
-
else
|
|
136
|
-
return {}
|
|
137
|
-
end
|
|
32
|
+
return {
|
|
33
|
+
ScreenGui = {
|
|
34
|
+
ResetOnSpawn = false,
|
|
35
|
+
ZIndexBehavior = "Sibling"
|
|
36
|
+
},
|
|
37
|
+
|
|
38
|
+
BillboardGui = {
|
|
39
|
+
ResetOnSpawn = false,
|
|
40
|
+
ZIndexBehavior = "Sibling"
|
|
41
|
+
},
|
|
42
|
+
|
|
43
|
+
SurfaceGui = {
|
|
44
|
+
ResetOnSpawn = false,
|
|
45
|
+
ZIndexBehavior = "Sibling",
|
|
46
|
+
|
|
47
|
+
SizingMode = "PixelsPerStud",
|
|
48
|
+
PixelsPerStud = 50
|
|
49
|
+
},
|
|
50
|
+
|
|
51
|
+
Frame = {
|
|
52
|
+
BackgroundColor3 = Color3.new(1, 1, 1),
|
|
53
|
+
BorderColor3 = Color3.new(0, 0, 0),
|
|
54
|
+
BorderSizePixel = 0
|
|
55
|
+
},
|
|
56
|
+
|
|
57
|
+
ScrollingFrame = {
|
|
58
|
+
BackgroundColor3 = Color3.new(1, 1, 1),
|
|
59
|
+
BorderColor3 = Color3.new(0, 0, 0),
|
|
60
|
+
BorderSizePixel = 0,
|
|
61
|
+
|
|
62
|
+
ScrollBarImageColor3 = Color3.new(0, 0, 0)
|
|
63
|
+
},
|
|
64
|
+
|
|
65
|
+
TextLabel = {
|
|
66
|
+
BackgroundColor3 = Color3.new(1, 1, 1),
|
|
67
|
+
BorderColor3 = Color3.new(0, 0, 0),
|
|
68
|
+
BorderSizePixel = 0,
|
|
69
|
+
|
|
70
|
+
Font = "SourceSans",
|
|
71
|
+
Text = "",
|
|
72
|
+
TextColor3 = Color3.new(0, 0, 0),
|
|
73
|
+
TextSize = 14
|
|
74
|
+
},
|
|
75
|
+
|
|
76
|
+
TextButton = {
|
|
77
|
+
BackgroundColor3 = Color3.new(1, 1, 1),
|
|
78
|
+
BorderColor3 = Color3.new(0, 0, 0),
|
|
79
|
+
BorderSizePixel = 0,
|
|
80
|
+
|
|
81
|
+
AutoButtonColor = false,
|
|
82
|
+
|
|
83
|
+
Font = "SourceSans",
|
|
84
|
+
Text = "",
|
|
85
|
+
TextColor3 = Color3.new(0, 0, 0),
|
|
86
|
+
TextSize = 14
|
|
87
|
+
},
|
|
88
|
+
|
|
89
|
+
TextBox = {
|
|
90
|
+
BackgroundColor3 = Color3.new(1, 1, 1),
|
|
91
|
+
BorderColor3 = Color3.new(0, 0, 0),
|
|
92
|
+
BorderSizePixel = 0,
|
|
93
|
+
|
|
94
|
+
ClearTextOnFocus = false,
|
|
95
|
+
|
|
96
|
+
Font = "SourceSans",
|
|
97
|
+
Text = "",
|
|
98
|
+
TextColor3 = Color3.new(0, 0, 0),
|
|
99
|
+
TextSize = 14
|
|
100
|
+
},
|
|
101
|
+
|
|
102
|
+
ImageLabel = {
|
|
103
|
+
BackgroundColor3 = Color3.new(1, 1, 1),
|
|
104
|
+
BorderColor3 = Color3.new(0, 0, 0),
|
|
105
|
+
BorderSizePixel = 0
|
|
106
|
+
},
|
|
107
|
+
|
|
108
|
+
ImageButton = {
|
|
109
|
+
BackgroundColor3 = Color3.new(1, 1, 1),
|
|
110
|
+
BorderColor3 = Color3.new(0, 0, 0),
|
|
111
|
+
BorderSizePixel = 0,
|
|
112
|
+
|
|
113
|
+
AutoButtonColor = false
|
|
114
|
+
},
|
|
115
|
+
|
|
116
|
+
ViewportFrame = {
|
|
117
|
+
BackgroundColor3 = Color3.new(1, 1, 1),
|
|
118
|
+
BorderColor3 = Color3.new(0, 0, 0),
|
|
119
|
+
BorderSizePixel = 0
|
|
120
|
+
},
|
|
121
|
+
|
|
122
|
+
VideoFrame = {
|
|
123
|
+
BackgroundColor3 = Color3.new(1, 1, 1),
|
|
124
|
+
BorderColor3 = Color3.new(0, 0, 0),
|
|
125
|
+
BorderSizePixel = 0
|
|
126
|
+
},
|
|
127
|
+
|
|
128
|
+
UIListLayout = {
|
|
129
|
+
SortOrder = Enum.SortOrder.LayoutOrder;
|
|
130
|
+
},
|
|
131
|
+
}
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
--[=[
|
|
2
|
+
@class SpringObject
|
|
3
|
+
]=]
|
|
4
|
+
|
|
5
|
+
local require = require(script.Parent.loader).load(script)
|
|
6
|
+
|
|
7
|
+
local RunService= game:GetService("RunService")
|
|
8
|
+
|
|
9
|
+
local Spring = require("Spring")
|
|
10
|
+
local Maid = require("Maid")
|
|
11
|
+
local Signal = require("Signal")
|
|
12
|
+
local StepUtils = require("StepUtils")
|
|
13
|
+
local Observable = require("Observable")
|
|
14
|
+
local SpringUtils = require("SpringUtils")
|
|
15
|
+
local Blend = require("Blend")
|
|
16
|
+
local Rx = require("Rx")
|
|
17
|
+
|
|
18
|
+
local SpringObject = {}
|
|
19
|
+
SpringObject.ClassName = "SpringObject"
|
|
20
|
+
SpringObject.__index = SpringObject
|
|
21
|
+
|
|
22
|
+
--[=[
|
|
23
|
+
Constructs a new SpringObject.
|
|
24
|
+
@param target T
|
|
25
|
+
@param speed number | Observable<number> | ValueObject<number> | NumberValue | any
|
|
26
|
+
@param damper number | Observable<number> | NumberValue | any
|
|
27
|
+
@return Spring<T>
|
|
28
|
+
]=]
|
|
29
|
+
function SpringObject.new(target, speed, damper)
|
|
30
|
+
local self = setmetatable({
|
|
31
|
+
_maid = Maid.new();
|
|
32
|
+
Changed = Signal.new();
|
|
33
|
+
}, SpringObject)
|
|
34
|
+
|
|
35
|
+
--[=[
|
|
36
|
+
Event fires when the spring value changes
|
|
37
|
+
@prop Changed Signal<()> -- Fires whenever the spring initially changes state
|
|
38
|
+
@within ValueObject
|
|
39
|
+
]=]
|
|
40
|
+
self._maid:GiveTask(self.Changed)
|
|
41
|
+
|
|
42
|
+
if target then
|
|
43
|
+
self.Target = target
|
|
44
|
+
else
|
|
45
|
+
self:_getSpringForType(0)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
if speed then
|
|
49
|
+
self.Speed = speed
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
if damper then
|
|
53
|
+
self.Damper = damper
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
return self
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
--[=[
|
|
60
|
+
Returns whether an object is a SpringObject.
|
|
61
|
+
@param value any
|
|
62
|
+
@return boolean
|
|
63
|
+
]=]
|
|
64
|
+
function SpringObject.isSpringObject(value)
|
|
65
|
+
return type(value) == "table" and getmetatable(value) == SpringObject
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
--[=[
|
|
69
|
+
Observes the spring animating
|
|
70
|
+
@return Observable<T>
|
|
71
|
+
]=]
|
|
72
|
+
function SpringObject:ObserveRenderStepped()
|
|
73
|
+
return self:ObserveOnSignal(RunService.RenderStepped)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
--[=[
|
|
77
|
+
Observes the spring animating
|
|
78
|
+
@param signal RBXScriptSignal
|
|
79
|
+
@return Observable<T>
|
|
80
|
+
]=]
|
|
81
|
+
function SpringObject:ObserveOnSignal(signal)
|
|
82
|
+
return Observable.new(function(sub)
|
|
83
|
+
local maid = Maid.new()
|
|
84
|
+
|
|
85
|
+
local startAnimate, stopAnimate = StepUtils.bindToSignal(signal, function()
|
|
86
|
+
local animating, position = SpringUtils.animating(self._currentSpring)
|
|
87
|
+
sub:Fire(SpringUtils.fromLinearIfNeeded(position))
|
|
88
|
+
return animating
|
|
89
|
+
end)
|
|
90
|
+
|
|
91
|
+
maid:GiveTask(stopAnimate)
|
|
92
|
+
maid:GiveTask(self.Changed:Connect(startAnimate))
|
|
93
|
+
startAnimate()
|
|
94
|
+
|
|
95
|
+
return maid
|
|
96
|
+
end)
|
|
97
|
+
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
--[=[
|
|
101
|
+
Impulses the spring, increasing velocity by the amount given. This is useful to make something shake,
|
|
102
|
+
like a Mac password box failing.
|
|
103
|
+
|
|
104
|
+
@param velocity T -- The velocity to impulse with
|
|
105
|
+
@return ()
|
|
106
|
+
]=]
|
|
107
|
+
function SpringObject:Impulse(velocity)
|
|
108
|
+
self._currentSpring:Impulse(SpringUtils.toLinearIfNeeded(velocity))
|
|
109
|
+
self.Changed:Fire()
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
--[=[
|
|
113
|
+
Instantly skips the spring forwards by that amount time
|
|
114
|
+
@param delta number -- Time to skip forwards
|
|
115
|
+
@return ()
|
|
116
|
+
]=]
|
|
117
|
+
function SpringObject:TimeSkip(delta)
|
|
118
|
+
assert(type(delta) == "number", "Bad delta")
|
|
119
|
+
|
|
120
|
+
self._currentSpring:TimeSkip(delta)
|
|
121
|
+
self.Changed:Fire()
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
function SpringObject:__index(index)
|
|
125
|
+
if index == "Value" or index == "Position" or index == "p" then
|
|
126
|
+
return SpringUtils.fromLinearIfNeeded(self._currentSpring.Value)
|
|
127
|
+
elseif index == "Velocity" or index == "v" then
|
|
128
|
+
return SpringUtils.fromLinearIfNeeded(self._currentSpring.Velocity)
|
|
129
|
+
elseif index == "Target" or index == "t" then
|
|
130
|
+
return SpringUtils.fromLinearIfNeeded(self._currentSpring.Target)
|
|
131
|
+
elseif index == "Damper" or index == "d" then
|
|
132
|
+
return self._currentSpring.Damper
|
|
133
|
+
elseif index == "Speed" or index == "s" then
|
|
134
|
+
return self._currentSpring.Speed
|
|
135
|
+
elseif index == "Clock" then
|
|
136
|
+
return self._currentSpring.Clock
|
|
137
|
+
elseif SpringObject[index] then
|
|
138
|
+
return SpringObject[index]
|
|
139
|
+
else
|
|
140
|
+
error(("%q is not a member of SpringObject"):format(tostring(index)))
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
function SpringObject:__newindex(index, value)
|
|
145
|
+
if index == "Value" or index == "Position" or index == "p" then
|
|
146
|
+
local observable = Blend.toPropertyObservable(value) or Rx.of(value)
|
|
147
|
+
|
|
148
|
+
self._maid._valueSub = observable:Subscribe(function(unconverted)
|
|
149
|
+
local converted = SpringUtils.toLinearIfNeeded(unconverted)
|
|
150
|
+
self:_getSpringForType(converted).Value = converted
|
|
151
|
+
self.Changed:Fire()
|
|
152
|
+
end)
|
|
153
|
+
elseif index == "Velocity" or index == "v" then
|
|
154
|
+
local observable = Blend.toPropertyObservable(value) or Rx.of(value)
|
|
155
|
+
|
|
156
|
+
self._maid._velocitySub = observable:Subscribe(function(unconverted)
|
|
157
|
+
local converted = SpringUtils.toLinearIfNeeded(unconverted)
|
|
158
|
+
|
|
159
|
+
self:_getSpringForType(0*converted).Velocity = converted
|
|
160
|
+
self.Changed:Fire()
|
|
161
|
+
end)
|
|
162
|
+
elseif index == "Target" or index == "t" then
|
|
163
|
+
local observable = Blend.toPropertyObservable(value) or Rx.of(value)
|
|
164
|
+
|
|
165
|
+
self._maid._targetSub = observable:Subscribe(function(unconverted)
|
|
166
|
+
local converted = SpringUtils.toLinearIfNeeded(unconverted)
|
|
167
|
+
self:_getSpringForType(converted).Target = converted
|
|
168
|
+
|
|
169
|
+
self.Changed:Fire()
|
|
170
|
+
end)
|
|
171
|
+
elseif index == "Damper" or index == "d" then
|
|
172
|
+
local observable = assert(Blend.toNumberObservable(value), "Invalid damper")
|
|
173
|
+
|
|
174
|
+
self._maid._damperSub = observable:Subscribe(function(unconverted)
|
|
175
|
+
assert(type(unconverted) == "number", "Bad damper")
|
|
176
|
+
|
|
177
|
+
self._currentSpring.Damper = unconverted
|
|
178
|
+
self.Changed:Fire()
|
|
179
|
+
end)
|
|
180
|
+
elseif index == "Speed" or index == "s" then
|
|
181
|
+
local observable = assert(Blend.toNumberObservable(value), "Invalid speed")
|
|
182
|
+
|
|
183
|
+
self._maid._speedSub = observable:Subscribe(function(unconverted)
|
|
184
|
+
assert(type(unconverted) == "number", "Bad damper")
|
|
185
|
+
|
|
186
|
+
self._currentSpring.Speed = unconverted
|
|
187
|
+
self.Changed:Fire()
|
|
188
|
+
end)
|
|
189
|
+
elseif index == "Clock" then
|
|
190
|
+
assert(type(value) == "function", "Bad clock value")
|
|
191
|
+
self._currentSpring.Clock = value
|
|
192
|
+
self.Changed:Fire()
|
|
193
|
+
else
|
|
194
|
+
error(("%q is not a member of SpringObject"):format(tostring(index)))
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
function SpringObject:_getSpringForType(converted)
|
|
199
|
+
if rawget(self, "_currentSpring") == nil then
|
|
200
|
+
-- only happens on init
|
|
201
|
+
rawset(self, "_currentSpring", Spring.new(converted))
|
|
202
|
+
return self._currentSpring
|
|
203
|
+
else
|
|
204
|
+
local currentType = typeof(SpringUtils.fromLinearIfNeeded(self._currentSpring.Value))
|
|
205
|
+
if currentType == typeof(converted) then
|
|
206
|
+
return self._currentSpring
|
|
207
|
+
else
|
|
208
|
+
local oldDamper = self._currentSpring.d
|
|
209
|
+
local oldSpeed = self._currentSpring.s
|
|
210
|
+
|
|
211
|
+
self._currentSpring = Spring.new(converted)
|
|
212
|
+
self._currentSpring.Speed = oldSpeed
|
|
213
|
+
self._currentSpring.Damper = oldDamper
|
|
214
|
+
return self._currentSpring
|
|
215
|
+
end
|
|
216
|
+
end
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
--[=[
|
|
220
|
+
Cleans up the BaseObject and sets the metatable to nil
|
|
221
|
+
]=]
|
|
222
|
+
function SpringObject:Destroy()
|
|
223
|
+
self._maid:DoCleaning()
|
|
224
|
+
setmetatable(self, nil)
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
return SpringObject
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
--[[
|
|
2
|
+
@class BlendSingle.story
|
|
3
|
+
]]
|
|
4
|
+
|
|
5
|
+
local require = require(game:GetService("ServerScriptService"):FindFirstChild("LoaderUtils", true).Parent).load(script)
|
|
6
|
+
|
|
7
|
+
local Maid = require("Maid")
|
|
8
|
+
local Observable = require("Observable")
|
|
9
|
+
local Blend = require("Blend")
|
|
10
|
+
|
|
11
|
+
return function(target)
|
|
12
|
+
local maid = Maid.new()
|
|
13
|
+
|
|
14
|
+
local state = Blend.State("a")
|
|
15
|
+
|
|
16
|
+
local result = Blend.Single(Blend.Dynamic(state, function(text)
|
|
17
|
+
return Blend.New "TextLabel" {
|
|
18
|
+
Parent = target;
|
|
19
|
+
Text = text;
|
|
20
|
+
Size = UDim2.new(1, 0, 1, 0);
|
|
21
|
+
BackgroundTransparency = 0.5;
|
|
22
|
+
[function()
|
|
23
|
+
return Observable.new(function()
|
|
24
|
+
local internal = Maid.new()
|
|
25
|
+
|
|
26
|
+
print("Made for", text)
|
|
27
|
+
internal:GiveTask(function()
|
|
28
|
+
print("Cleaning up", text)
|
|
29
|
+
end)
|
|
30
|
+
|
|
31
|
+
return internal
|
|
32
|
+
end)
|
|
33
|
+
end] = true;
|
|
34
|
+
}
|
|
35
|
+
end))
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
maid:GiveTask(result:Subscribe())
|
|
39
|
+
|
|
40
|
+
state.Value = "b"
|
|
41
|
+
|
|
42
|
+
return function()
|
|
43
|
+
maid:DoCleaning()
|
|
44
|
+
end
|
|
45
|
+
end
|