Adapters
Adapters are a simple way to avoid having to provide an object's config (e.g. its props types) and setting up its onValuesChange()
listener.
Consider this example:
const project = Theatre.getProject("My project")
const nativeObject = document.createElement("div")
nativeObject.style.cssText = `
position: absolute;
left: calc(10vw);
bottom: 0;
width: 50px;
height: 50px;
transform-origin: center bottom;
background: #EEE;
`
document.body.appendChild(nativeObject)
const instanceName = "Box"
const timeline = project.getTimeline("Bounce", instanceName)
nativeObject.addEventListener("click", () => {
timeline.play()
})
// Create an object:
const object = timeline.getObject(
// The name of the object is "The box":
"The box",
// Leave a reference to the native object, which in this case is a div:
nativeObject,
// Define the properties of our object
{
props: {
y: {
type: "number"
},
stretch: {
type: "number"
}
}
}
)
object.onValuesChange(newValues => {
const div = object.nativeObject
div.style.transform = `translateY(${-newValues.y}px) scaleY(${
newValues.stretch
}) scaleX(${1 / newValues.stretch})`
})
We can simply remove the highlighted lines above and move them into an adapter, making it a bit easier to create more instances of the box object.
This is how our adapter will look like:
const project = Theatre.getProject("My project")
project.adapters.add({
// Adapters have unique names:
name: "MovableAndStretchable",
// This function returns true if the nativeObject is an html element and
// has the class "movableAndStretchable. If not, Theatre will look for another
// adapter to handle this object
canHandle(nativeObject) {
return (
nativeObject instanceof HTMLElement &&
nativeObject.classList.contains("movableAndStretchable")
)
},
// The config of the object (currently the only configurable aspect is an object's prop types)
getConfig(nativeObject) {
return {
props: {
x: {
type: "number"
},
y: {
type: "number"
},
stretch: {
type: "number"
}
}
}
},
// This will be called whenever an object that's "handlable" by this adapter is added
// to any timeline within this project.
start(object) {
const unsubscribe = object.onValuesChange(newValues => {
const div = object.nativeObject
div.style.transform =
`translateX(${newValues.x}px) ` +
`translateY(${-newValues.y}px) ` +
`scaleY(${newValues.stretch}) ` +
`scaleX(${1 / newValues.stretch})`
})
// we need to return a function that basically "stops" whatever is going on in the "start()" function
// This will be called if an object or its timeline are taken out of the scene
return unsubscribe
}
})
const nativeObject = document.createElement("div")
nativeObject.style.cssText = `
position: absolute;
left: calc(10vw);
bottom: 0;
width: 50px;
height: 50px;
transform-origin: center bottom;
background: #EEE;
`
// Make this div handlable by the adapter
nativeObject.classList.add("movableAndStretchable")
document.body.appendChild(nativeObject)
const instanceName = "Box"
const timeline = project.getTimeline("Bounce", instanceName)
nativeObject.addEventListener("click", () => {
timeline.play()
})
// Create an object:
const object = timeline.getObject(
// The name of the object is "The box":
"The box",
// Leave a reference to the native object, which in this case is a div:
nativeObject
// note that we do not need to provide a config anymore
)
Now our code will behave exactly as before, but upon creating more instances of "The Box", we won't need to provide any props or set up its onValuesChange()
listener.
Now let's go through the different parts of each adapter:
name
must be a string unique within this project, in this case "MovableAndStretchable".canHandle()
is a function that takes a native object and returns a boolean. If the returned value is true, Theatre will use this adapter to provide the props of this object and set up itsonValuesChange()
listener. In this case, we make sure the native object is an HTML element, and it has themovableAndStretchable
CSS class.getConfig()
will return the config of the object (its prop types). By setting upgetConfig()
, you won't need to provide aconfig
argument anymore intimeline.getObject(name, nativeObject, config)
.start()
gets called every time such an object is added to any timeline within the current project. You're free to perform any task within thestart()
method, but most commonly you'd set up theonValuesChange()
listener. It is also required that thestart()
function returns astop()
function. Thestop
function gets called whenever the object is removed from the scene.