In order to run transitions sequentially, you would normally have to use the callback provided by node.transition(). This example shows how to create your own Node plugin based on promises that lets you chain CSS transitions.
Plugins are a way to add functionality to Node without modifying its existing methods. They also are usually subclasses of Plugin.Base that contain various methods to interact with in a different way with a node. In our case we will skip the use of Plugin.Base to focus on returning promises from a plugin method.
The plan is to create a Promise subclass that represents a Node and store one of these promises in the plugin instance. Then the plugin's transition method will return a new promise based on the one already stored.
Promises represent a value. Since we want to chain transitions on a Node we need to create a Promise sublcass that represents a Node. Promises can be extended the same way as any other YUI class by using Y.extend.
// NodePromise will represent a YUI Node
function NodePromise() {
NodePromise.superclass.constructor.apply(this, arguments);
}
Y.extend(NodePromise, Y.Promise);
The next step is to add the transition() method to this promise and have it return a promise that is fulfilled when the transition is completed.
// This method takes the same "config" parameter as Node's transition method
// but returns a NodePromise instead
NodePromise.prototype.transition = function (config) {
// We call this.then to ensure the promise is fulfilled.
// Since we will be creating a chain of transitions this means we will be
// waiting for the previous transition to end
return this.then(function (node) {
// As noted in the user guide, returning a promise inside the then()
// callback causes the promise returned by then() to be synced with this
// new promise. This is a way to control when the returned promise is
// fulfilled
return new Y.Promise(function (fulfill, reject) {
node.transition(config, function () {
// The transition is done, signal the promise that all is ready
// by fulfilling it with the same node
fulfill(node);
});
});
});
};
Our plugin is a very simple class that contains a NodePromise. In order for it to let us write chains of transitions like node.promise.transition(config1).transition(config2) we will add a transition method to it that simply points to the NodePromise's same method.
function PromisePlugin(config) {
// Create a private NodePromise instance that points to the plugin host
this._promise = new NodePromise(function (fulfill) {
// Since this is a Node plugin, config.host will be an instance of Node
fulfill(config.host);
});
}
// Set up the plugin's namespace
PromisePlugin.NS = 'promise';
PromisePlugin.prototype.transition = function (config) {
// Simply point to the private promise's transition method
return this._promise.transition(config);
};
Now that we have the plugin ready, we can easily chain transitions from the plugin instance:
var square = Y.one('#square');
square.plug(PromisePlugin);
// run a sequence of transitions
square.promise
.transition({width: '300px'})
.transition({height: '300px'})
.transition({left: '200px'});
<button id="without-plugin">Without Plugin</button> <button id="with-plugin">With Plugin</button> <div id="square"></div>
<style>
#square {
width: 100px;
height: 100px;
background: gray;
position: relative;
margin: 20px;
}
</style>
<script>
YUI().use('promise', 'transition', 'node-pluginhost', function (Y) {
// NodePromise will represent a YUI Node
function NodePromise() {
NodePromise.superclass.constructor.apply(this, arguments);
}
Y.extend(NodePromise, Y.Promise);
// This method takes the same "config" parameter as Node's transition method
// but returns a NodePromise instead
NodePromise.prototype.transition = function (config) {
// We call this.then to ensure the promise is fulfilled.
// Since we will be creating a chain of transitions this means we will be
// waiting for the previous transition to end
return this.then(function (node) {
// As noted in the user guide, returning a promise inside the then()
// callback causes the promise returned by then() to be synced with this
// new promise. This is a way to control when the returned promise is
// fulfilled
return new Y.Promise(function (fulfill, reject) {
node.transition(config, function () {
// The transition is done, signal the promise that all is ready
// by fulfilling it with the same node
fulfill(node);
});
});
});
};
function PromisePlugin(config) {
// Create a private NodePromise instance that points to the plugin host
this._promise = new NodePromise(function (fulfill) {
// Since this is a Node plugin, config.host will be an instance of Node
fulfill(config.host);
});
}
// Set up the plugin's namespace
PromisePlugin.NS = 'promise';
PromisePlugin.prototype.transition = function (config) {
// Simply point to the private promise's transition method
return this._promise.transition(config);
};
var square = Y.one('#square');
square.plug(PromisePlugin);
function resetStyles() {
square.setStyles({
width: '100px',
height: '100px',
left: '0'
});
}
Y.one('#without-plugin').on('click', function () {
resetStyles();
square
.transition({width: '300px'})
.transition({height: '300px'})
.transition({left: '200px'});
});
Y.one('#with-plugin').on('click', function () {
resetStyles();
square.promise
.transition({width: '300px'})
.transition({height: '300px'})
.transition({left: '200px'});
});
});
</script>