Skip to content

Commit 5aded04

Browse files
authored
Travis/flow common web updates (#14)
* Add Hover element to build script * Update inline documentation for Player * Add generic timeline class This class is for reference, and is not yet used by any generated files from Flow * Update filename Co-authored-by: Travis <[email protected]>
1 parent 1aafea0 commit 5aded04

File tree

4 files changed

+177
-19
lines changed

4 files changed

+177
-19
lines changed
+146
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
/**
2+
* A Timeline represents a set of animations that can be applied to a DOM element.
3+
* The general structure of the Timeline class is a pattern that represents a generic way of referencing elements and resources.
4+
* This class provides placeholder methods, ince an element can have any number and structure of subelements, each of which may or may not be animated.
5+
*
6+
* TODO: If necessary, convert this class to define functions as part of its prototype, then have generated timelines inherit from this class
7+
*/
8+
9+
class Timeline {
10+
/**
11+
* @constructor
12+
*
13+
* @param {HTMLElement} rootElement
14+
* Root Element of the DOM containing the elements that will be animated in this timeline.
15+
*
16+
* @param {String} resourcesPath
17+
* The path pointing to the root of the Timeline folder.
18+
*/
19+
constructor(rootElement, elementID, resourcesPath) {
20+
this.rootElement = rootElement;
21+
this.elementID = elementID;
22+
this.resourcesPath = resourcesPath;
23+
this.imagesFolderPath = resourcesPath + "/img";
24+
this.loadFillImages();
25+
}
26+
27+
/**
28+
* Returns the timeline's duration in milliseconds.
29+
*/
30+
get duration() {
31+
return 0
32+
}
33+
34+
/**
35+
* Loads fill images for shapes
36+
*/
37+
loadFillImages() {
38+
// Any images used as fills on shapes should be created here
39+
// For example:
40+
// let pattern = this.rootElement.getElementById("shapeID-fillImage")
41+
// pattern.innerHTML = `<image ... href="${this.imagesFolderPath}/image.png" />`
42+
// pattern.setAttribute(...);
43+
}
44+
45+
/**
46+
* Loads all animations for svgs in the current timeline
47+
*/
48+
loadSVGAnimations() {
49+
this.loadSVGShapeAnimations()
50+
this.loadSVGShapeMaskAnimations()
51+
}
52+
53+
/**
54+
* Loads all shape animations
55+
*/
56+
loadSVGShapeAnimations() {
57+
// Path defs, and animations should be created here
58+
// For example:
59+
// let path = this.rootElement.getElementById("pathID")
60+
// path.d = "M0,...zM0"
61+
// path.innerHTML = `<animate .../>`
62+
63+
// Gradient defs should be created here
64+
// let defs = this.rootElement.getElementById("fillID")
65+
// defs.innerHTML = `<stop ... /> <animate .../>`
66+
}
67+
68+
/**
69+
* Loads all shape mask animations
70+
*/
71+
loadSVGShapeMaskAnimations() {
72+
// Insert SVG animations for mask paths here
73+
// For example:
74+
// this.rootElement.querySelector("#shape-maskID").innerHTML = `<path ...><animate .../></path>`
75+
}
76+
77+
/**
78+
* @return
79+
* Returns the list of svg shapes that are animated in this timeline.
80+
*/
81+
get allShapes() {
82+
return [
83+
//this.rootElement.querySelector(`#shapeID .shape-svg`),
84+
]
85+
}
86+
87+
/**
88+
* A simple, visually unnoticable animation to fix a rendering bug with Safari
89+
*/
90+
artboardAnimation() {
91+
// Workaround for Safari bug
92+
return this.rootElement.querySelector(`${this.elementID}.flow-artboard`).animate({
93+
backgroundPosition: ['0px', '1px'],
94+
}, {
95+
delay: 0,
96+
duration: 1000,
97+
});
98+
}
99+
100+
/**
101+
* Property track animations should be added here.
102+
*
103+
* Track animations are keyframe animations that represent the entire state of an element's property throughout the entire timeline.
104+
* Animations should be normalized from 0 to 1.
105+
*
106+
* shapeWidthTrack() {
107+
* const element = this.rootElement.querySelector(`#shapeID .shape`);
108+
* return element.animate({
109+
* width: ['100px', '200px'],
110+
* easing: ["ease-in-out"],
111+
* offset: [0, 1],
112+
* }, {
113+
* duration: this.duration,
114+
* composite: 'replace',
115+
* fill: 'forwards'
116+
* })
117+
* }
118+
*
119+
* In the case of animations that do not start at 0, or end at 1, there should be values at 0 and 1, for example
120+
*
121+
* shapeWidthTrack() {
122+
* const element = this.rootElement.querySelector(`#shapeID .shape`);
123+
* return element.animate({
124+
* width: ['100px', '100px', '200px', '200px'],
125+
* easing: ["linear","ease-in-out","linear"],
126+
* offset: [0, 0.25, 0.75, 1],
127+
* }, {
128+
* duration: this.duration,
129+
* composite: 'replace',
130+
* fill: 'forwards'
131+
* })
132+
* }
133+
*/
134+
135+
/**
136+
* Creates and returns all animations for this timeline.
137+
*/
138+
createAllAnimations() {
139+
return [
140+
this.artboardAnimation(),
141+
// this.shapeWidthTrack()
142+
].flat()
143+
}
144+
}
145+
146+
Object.freeze(Timeline)

Sources/FlowCommonWebFiles/player.js

+27-16
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/**
2-
* Class used for driving animations.
2+
* The Player is a generic class for handling the loading, unloading, and playback of a `Timeline` to be associated with a DOM element. A player must be able to construct a timer – using an existing DOM element or ID – which handles timing and the execution of a callback on completion.
33
*/
44
class Player {
55
/**
@@ -9,13 +9,13 @@ class Player {
99
* Animation played when the transition is triggered.
1010
*
1111
* @param {String} timer
12-
* The HTML Element or the HTML Element ID for the Timer.
12+
* The HTML Element or the HTML Element ID to be used for handling the timer (i.e. timing animation) of `self`.
1313
*
1414
* @param {Boolean} loop
15-
* True if the animation should restart upon completion false otherwise.
15+
* This property specifies that the animation should repeat upon completion.
1616
*
1717
* @param {Boolean} delay
18-
* The time in milliseconds before the animation starts.
18+
* The number of milliseconds to delay the start of playback.
1919
*
2020
* @param {function: () -> Void} callback
2121
* A callback function passed to the player that runs upon animation completion.
@@ -36,45 +36,51 @@ class Player {
3636

3737
/**
3838
* @return {Timeline}
39-
* Returns the value of timeline for `self`.
39+
* Returns the current timeline for `self`.
4040
*/
4141
get timeline() {
4242
return this._timeline;
4343
}
4444

4545
/**
4646
* @set
47-
* Sets the timeline of self to timeline, pauses the playback and sets the current time to zero.
47+
* Sets the timeline of `self` to `timeline`. When this variable is set, the player pauses playback then sets the new value. If the new value is `null` the current timing animation is removed, and default values are set in anticipation of a new timeline. If the new value is not `null` the timeline is prepped for playback.
4848
*
4949
* @param {Timeline} timeline
50-
* Animation to be played.
50+
* The timeline to be controlled by `self`.
5151
*
5252
*/
5353
set timeline(timeline) {
5454
// Work around for Safari bug. More detail provided in the
55-
// comment above the cancelAnimations function in this file.
55+
// comment above the `cancelAnimations` function in this file.
5656
this.cancelAnimations();
5757

58+
// Pause the current timeline, if it exists
5859
if (this._timeline != null) {
5960
this.pause();
6061
}
62+
6163
this._timeline = timeline;
62-
this._timeline.loadFillImages();
6364

65+
// Prepare `self` to receive a new timeline, or initiate playback.
6466
if (this._timeline === null) {
6567
this.timingAnimation = null;
6668
this.currentTime = 0;
6769
this.shouldPlay = false;
6870
} else {
71+
//Set up the timing animation, which is used to track the current playback time
6972
this.timingAnimation = this.timer.animate(
7073
{},
7174
this.timeline.duration + this.delay);
72-
73-
this.timeline.loadSVGAnimations();
7475
this.timingAnimation.currentTime = 0;
7576
this.timingAnimation.pause();
7677

78+
//Load all images, shapes, animations
79+
this.timeline.loadFillImages();
80+
this.timeline.loadSVGAnimations();
7781
this.animations = this.timeline.createAllAnimations();
82+
83+
//Prepare for playback
7884
this.shouldPlay = true;
7985
this.pause();
8086
this.setOnFinishCallback();
@@ -83,40 +89,44 @@ class Player {
8389

8490
/**
8591
* @return {Number}
86-
* Returns the duration of the timeline of self, or 0 if timeline is null.
92+
* Returns the duration of the `timeline` of `self`, or `0` if timeline is `null`.
8793
*/
8894
get duration() {
8995
return this.timeline === null ? 0 : this.timeline.duration;
9096
}
9197

9298
/**
9399
* @return {Number}
94-
* Returns the currentTime of self, or null if timeline is null.
100+
* Returns the current playback time of `self`, or `0` if `timeline` is `null`.
95101
*/
96102
get currentTime() {
97103
return this.timingAnimation === null ? 0 : this.timingAnimation.currentTime;
98104
}
99105

100106
/**
101107
* @set
102-
* Sets the currentTime of self.
108+
* Sets the current playback time (in milliseconds) of `self`. This value is propagated to all animations in the current timeline.
103109
*
104110
* @param {Number} time
105111
* A numeric value representing time in milliseconds.
106112
*/
107113
set currentTime(time) {
114+
// There should always be both a timeline and a timing animation, if not do nothing
108115
if (this.timeline === null || this.timingAnimation === null) {
109116
return;
110117
}
111118

119+
// Set the time for all animations in the timeline
112120
this.animations.forEach((animation) => {
113121
animation.currentTime = time;
114122
});
115123

124+
// Set the time for all shapes (SVG/SMIL) in the timeline
116125
this.timeline.allShapes.forEach((shape) => {
117126
shape.setCurrentTime(time / 1000);
118127
});
119128

129+
// Set the time for the timing animation
120130
this.timingAnimation.currentTime = time;
121131
}
122132

@@ -136,8 +146,7 @@ class Player {
136146
}
137147

138148
/**
139-
* Plays the animation if the timeline that belongs to `self` is not null
140-
* and the animation is not currently playing.
149+
* Plays the current timeline for `self`. If the player is currently playing, or the current timeline is `null` this function does nothing.
141150
*/
142151
play() {
143152
if (this.timeline == null || this.isPlaying() === true) {
@@ -148,6 +157,8 @@ class Player {
148157
this.animations.forEach((animation) => {
149158
animation.play();
150159
});
160+
161+
151162
this.timeline.allShapes.forEach((shape) => {
152163
const t = shape.getCurrentTime() % this.timeline.duration;
153164
shape.setCurrentTime(t);

minify.rb

+3-2
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@
55
flowCommonWebDir = "#{__dir__}/Sources/FlowCommonWebFiles"
66
waapi = "web-animations.min.js"
77
flowCommonWebFiles = [
8-
"player.js",
9-
"ToggleButton.js"
8+
"Player.js",
9+
"ToggleButton.js",
10+
"HoverElement.js"
1011
]
1112

1213
outputDir = "#{__dir__}/generated"

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "flowcommonweb",
33
"version": "1.0.0",
44
"description": "**When you export a Flow project to HTML, React, or anything Web-related, Flow includes a set of helper classes and extensions that help manage and control your animations. This page provides and overview and a link to each of these files.**",
5-
"main": "player.js",
5+
"main": "Player.js",
66
"scripts": {
77
"test": "echo \"Error: no test specified\" && exit 1"
88
},

0 commit comments

Comments
 (0)