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>