Security News
pnpm 10.0.0 Blocks Lifecycle Scripts by Default
pnpm 10 blocks lifecycle scripts by default to improve security, addressing supply chain attack risks but sparking debate over compatibility and workflow changes.
superfly-timeline
Advanced tools
A collection of rules as well as a resolver for placing objects on a virtual timeline.
The SuperFly-Timeline is a collection of rules as well as a resolver for placing objects on a virtual timeline. It uses the concept of timing objects in sequences -– absolute or relative timings -– which resolves recursively in nested structures. This means it supports grouping, combinations of timing between groups, and objects within groups. It also supports logical conditions instead of timed conditions.
It is used in the Sofie TV News Studio Automation System.
$ npm install --save superfly-timeline
Can be run in the browser using browserify or the like.
var Timeline = require('superfly-timeline')
// The input to the timeline is an array of objects:
const myTimeline = [
{
id: 'video0',
layer: 'L1',
enable: {
start: 10,
end: 100,
},
content: {},
},
{
// This object defines a graphic, to be overlaid on the video
id: 'graphic0',
layer: 'L2',
enable: {
start: '#video0.start + 10', // 10 after video0 starts
duration: 10,
},
content: {},
classes: ['graphics'],
},
{
id: 'graphic1',
layer: 'L2',
enable: {
start: '#graphic0.end + 10', // 10 after graphic0 ends
duration: 15,
},
content: {},
classes: ['graphics'],
},
{
id: 'graphicBackground',
layer: 'L3',
enable: {
while: '!.graphics', // When no graphics are playing
},
content: {},
},
]
// When we have a new timeline, the first thing to do is to "Resolve" it.
// This calculates all timings of the objects in the timeline.
const options = {
time: 0,
}
const resolvedTimeline = Timeline.Resolver.resolveTimeline(myTimeline, options)
// Use the resolved timeline and pre-calculate states, instance collisions, etc.
const resolvedStates = Timeline.Resolver.resolveAllStates(resolvedTimeline)
// Fetch the state at time 10:
const state0 = Timeline.Resolver.getState(resolvedStates, 10)
console.log(
`At the time ${state0.time}, the active objects are "${_.map(state0.layers, (o, l) => `${o.id} at layer ${l}`).join(
', '
)}"`
)
// Fetch the state at time 25:
const state1 = Timeline.Resolver.getState(resolvedStates, 25)
console.log(
`At the time ${state1.time}, the active objects are "${_.map(state1.layers, (o, l) => `${o.id} at layer ${l}`).join(
', '
)}"`
)
console.log(
`The object "graphicBackground" will play at [${_.map(
resolvedTimeline.objects['graphicBackground'].resolved.instances,
(instance) => `${instance.start} to ${instance.end}`
).join(', ')}]`
)
const nextEvent = state1.nextEvents[0]
console.log(
`After the time ${state1.time}, the next event to happen will be at time ${nextEvent.time}. The event is related to the object "${nextEvent.objId}"`
)
// Output:
// At the time 10, the active objects are "video0 at layer L1, graphicBackground at layer L3"
// At the time 25, the active objects are "video0 at layer L1, graphic0 at layer L2"
// The object "graphicBackground" will play at "0 to 20, 30 to 40, 55 to null"
// After the time 25, the next event to happen will be at time 30. The event is related to the object "graphic0"
The logic is set by setting properties in the .enable
property.
Property | Description |
---|---|
.start | The start time of the object. (cannot be combined with .while ) |
.end | The end time of the object (cannot be combined with .while or .duration ). |
.while | Enables the object WHILE expression is true (ie sets both the start and end). (cannot be combined with .start , .end or .duration ) |
.duration | The duration of an object |
.repeating | Makes the object repeat with given interval |
If .end
, .duration
or .while
is not set, the object will run indefinitely.
Examples
{
enable: {
start: '#abc.end + 5', // Start 5 seconds after #abc ends
duration: '#abc.duration' // Have the same duration as #abc
}
}
{
enable: {
while: '#abc', // Enable while #abc is enabled
}
}
All objects will be resolved to a layer in the calculated state. There are a few rules:
.priority
will win.Example
{
id: 'A',
layer: 'L1',
enable: {
start: 10,
end: 100
},
content: {},
},
{
id: 'B',
layer: 'L1',
enable: {
start: 50,
end: 10
},
content: {},
}
// This will cause the timeline to be:
// A on layer L1 for 10 - 50
// B on layer L1 for 50 - 60
// A on layer L1 for 60 - 100 (since B has stopped)
Example | Description |
---|---|
#objId | Reference to the object that has the specified .id |
.className | Reference to any object that has the class-name in its .classes |
$layerName | Reference to any object that is on the specified layer (.layer) |
The references listed above can be modified:
Example | Description |
---|---|
#objId.start | Refer to the start of the object |
#objId.end | Refer to the end of the object |
#objId.duration | Refer to the duration of the object |
The references can be combined using basic math expressions ( + - * / % ) and logical operators ( & | ! )
Examples
{
enable: {
start: '#abc.start + #abc.duration / 2', // Start halfway in
}
}
{
enable: {
while: '#sun & #moon & !#jupiter', // Enable while #sun and #moon, but not #jupiter
}
}
It is also possible to add keyframes to an object. A keyframe can have the same logics as normal timeline objects, and when "enabled", it applies it's .content
on its parent object's .content
.
Example
{
id: 'myObj',
layer: 'L1',
enable: {
start: 10,
end: 100
},
content: {
opacity: 100
},
keyframes: [{
id: 'kf0',
enable: {
start: 5, // relative to parent, so will start at 15
duration: 10
},
content: {
opacity: 0
}
}]
}
// This will cause the object to be
// * Enabled between 10 - 100
// * Have opacity = 100 between 10 - 15, and 25 - 100
// * Have opacity = 0 between 15 - 25
It is also possible to add groups that contain other objects as children. The children will always be capped within their parent.
Groups can work in 2 ways:
.layer
assigned to it (or it's set to ''). A transparent group does not "collide" with other objects, nor be visible in the calculated state. But its children objects will always be put on the timeline..layer
assigned to it. This means that the group works the same way as normal objects, and can collide with them. The children of the group will only be enabled while the parent is enabled.Example
{
id: 'myGroup',
layer: '',
enable: {
start: 10,
duration: 10,
repeat: 20 // Repeat every 20 seconds, so will start at 10, 30, 50 etc...
},
content: {},
isGroup: true,
children: [{
id: 'child0',
layer: 'L1',
enable: {
start: 2, // will repeat with parent, so will start at 12, 32, 52 etc...
duration: null // Duration not set, but will be capped in parent, so will end at 20, 40, 60 etc...
},
content: {},
}]
}
Please note that in the examples above the times have been defined in seconds. This is for readability only, you may use whatever time-base you like (like milliseconds) in your implementation.
FAQs
A collection of rules as well as a resolver for placing objects on a virtual timeline.
The npm package superfly-timeline receives a total of 1,323 weekly downloads. As such, superfly-timeline popularity was classified as popular.
We found that superfly-timeline demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 0 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
pnpm 10 blocks lifecycle scripts by default to improve security, addressing supply chain attack risks but sparking debate over compatibility and workflow changes.
Product
Socket now supports uv.lock files to ensure consistent, secure dependency resolution for Python projects and enhance supply chain security.
Research
Security News
Socket researchers have discovered multiple malicious npm packages targeting Solana private keys, abusing Gmail to exfiltrate the data and drain Solana wallets.