An Intro to Enyo (Part 2): More on Components

In Part 1 of our series on Enyo we talked about components, a core concept of Enyo, in this part we will learn more about components, and how you can wire up individual parts of a component to make something truly powerful. If you haven’t read the previous part, please do so, since this tutorial will use concepts that are introduced in the first part.

Over the course of this tutorial we will have built a small application that can rotate and skew an image.

Let’s just start with a simple RotateImageComponent Enyo kind that we will use to develop all this functionality:

enyo.kind({ name: "RotateImageComponent", kind: enyo.Control, published: { rotation: 0 }, components: [ {tag: "img", name: "logo", src: "assets/enyo-logo.jpg"}, {tag: "button", content: "Rotate", onclick:"rotate"} ] }); enyo.kind({ enyo.kind({ name: "App", fit: true, components: [ {kind: "RotateComponent"} ] }); 

This much is simple, it only deals with stuff we have done before. We have a published property called rotation which is initially set to 0 and will store the rotation applied to the image in the component. The RotateComponent has two subcomponents, an image, which we have named “logo”; and a button with a label set to “Rotate”. This component is being included in our “App” which is being rendered on the page. Like before this code will go in the App.js file.

You can use any source image in place of the one we have used, just place it in the assets directory and change the code to match the file name. The file we have used is part of Enyo project, and should be called “logo.png” in the bootplate directory. You can place the image in a subdirectory called “images” if you want to be even more organized.

For now we are starting with single button that just rotates the image, and for this we have set the onclick property to “rotate”, which is the name of a method we will define soon. Right now all that will happen when you add this component is that you will get a image (if the one you have specified here exists) and a button that does nothing when clicked.

Now let’s add the rotate method. This method will contain only a single line of code, the workings of which we will explain a little later. That line is:

this.setRotation(this.rotation + 1); 

In context of the entire component, here is what it would look like:

enyo.kind({ name: "RotateComponent", kind: enyo.Control, published: { rotation: 0 }, rotate: function () { this.setRotation(this.rotation + 1); }, components: [ {tag: "img", name: "logo", src: "assets/enyo-rotate.jpg"}, {tag: "button", content: "Rotate", onclick: "rotate"} ] }); 

Already the app will do this much, it will update the value of rotation when the button is clicked. It will not actually rotate the image yet of course, but we will come to that soon enough. To do that we will need to write code that automatically rotates the image when the rotation value is changed.

Published Properties

We have talked about published properties before. They are a good way to give hooks into our component that allow accessing the state of our component or changing its properties. Enyo helps out quite a bit here by automatically enhancing published properties in several ways. Firstly it creates getters and setters for published properties.

So in the above case, Enyo will automatically create the methods setRotation and getRotation on our object. The advantage of this is that now Enyo can notify our component when a value is changed via the setter. In our case we will be able to find out when the rotation is changed, and update our component accordingly.

So how can find out when the rotation property is changed via setRotation? We simply create a method called rotationChanged, and Enyo will automatically call it when rotation is changed via setRotation. Note that it is also possible to directly change the rotation using the rotation property itself. In this case the rotationChanged method will not be called.

Rotating the image

Now for the main functionality of the application. So now we know that if we define a rotationChanged method, it will automatically be called when the rotation value is changed, so let’s define one. Here is the basic code for it:

this.$.logo.applyStyle("transform", "rotate(" + this.rotation + "deg)"); 

This code seems a little complex, so let’s explain what is going on here. First of all, this.$ gives us access to all the subcomponents that make up our component. In this case the image and the button. As you will notice, we set the name property of the image to “logo” in our code; this gives us access to the image component though this.$.logo.

The applyStyle method applies a CSS style to the particular component. In this case we are using the new CSS3 transforms to do the work. This is CSS feature that is still very new, and will only work in the newest pre-release browsers. Even in some of the latest release browsers it is prefixed, so it’s better to also apply the prefixed version of this CSS property. Here is the code for that:

this.$.logo.applyStyle("transform", "rotate(" + this.rotation + "deg)"); this.$.logo.applyStyle("-moz-transform", "rotate(" + this.rotation + "deg)"); this.$.logo.applyStyle("-o-transform", "rotate(" + this.rotation + "deg)"); this.$.logo.applyStyle("-webkit-transform", "rotate(" + this.rotation + "deg)"); this.$.logo.applyStyle("-ms-transform", "rotate(" + this.rotation + "deg)"); 

The syntax to rotate an object 10 degrees using CSS is:

.object-to-rotate { transform: rotate(10deg); } 

Transform supports other properties as well, such as scale, scaleX, scaleY, translate, translateX, translateY, skew, skewX, skewY, and matrix. We won’t cover those in detail here.

A Functioning Rotating Image Component

All that is left now is putting together all we have learned in the previous steps. Which leaves us with this:

enyo.kind({ name: "RotateComponent", kind: enyo.Control, published: { rotation: 0 }, create: function () { this.inherited(arguments); this.rotationChanged(); }, rotate: function () { this.setRotation(this.rotation+1); }, rotationChanged: function(){ this.$.logo.applyStyle("transform", "rotate(" + this.rotation + "deg)"); this.$.logo.applyStyle("-moz-transform", "rotate(" + this.rotation + "deg)"); this.$.logo.applyStyle("-o-transform", "rotate(" + this.rotation + "deg)"); this.$.logo.applyStyle("-webkit-transform", "rotate(" + this.rotation + "deg)"); this.$.logo.applyStyle("-ms-transform", "rotate(" + this.rotation + "deg)"); }, components: [ {tag: "img", name: "logo", src: "assets/enyo-logo.png"}, {tag: "button", content: "Rotate", onclick: "rotate"} ] }); enyo.kind({ name: "App", fit: true, components: [ {kind: "RotateComponent"} ] }); 

We have added just one function we have not talked about here, the create function. We discussed this in the previous article, so we won’t go into details here. The reason we added this function is to account for the fact that someone could initialize this component with the rotation set to something other than zero. So by adding one extra line, we actually ensure the rotation specified when the component is created is actually applies.

Adding the other features

Now this pretty much covers everything we wanted to cover in this part, but as promised we should also add a skew function to the app. By now you should already understand how that could be done.

So what we will do is, we will the “Rotate” button into two, one for clockwise rotation, and the other for counter clockwise rotation. We will add four buttons for skewing, two to skew in the x direction (increase / decrease) and two for the y direction. Here is what we come up with:

enyo.kind({ name: "RotateComponent", kind: enyo.Control, published: { rotation: 0, skewX: 0, skewY: 0 }, create: function () { this.inherited(arguments); this.updateTransforms(); }, rotateC: function () { this.setRotation(this.rotation + 1); }, rotateA: function () { this.setRotation(this.rotation - 1); }, skewXInc: function () { this.setSkewX(this.skewX + 1); }, skewXDex: function () { this.setSkewX(this.skewX - 1); }, skewYInc: function () { this.setSkewY(this.skewY + 1); }, skewYDec: function () { this.setSkewY(this.skewY - 1); }, rotationChanged: function () { this.updateTransforms(); }, skewXChanged: function () { this.updateTransforms(); }, skewYChanged: function () { this.updateTransforms(); }, updateTransforms: function() { this.$.logo.applyStyle("transform", "rotate(" + this.rotation + "deg) skewX(" + this.skewX + "deg) skewY(" + this.skewY + "deg)"); this.$.logo.applyStyle("-moz-transform", "rotate(" + this.rotation + "deg) skewX(" + this.skewX + "deg) skewY(" + this.skewY + "deg)"); this.$.logo.applyStyle("-o-transform", "rotate(" + this.rotation + "deg) skewX(" + this.skewX + "deg) skewY(" + this.skewY + "deg)"); this.$.logo.applyStyle("-webkit-transform", "rotate(" + this.rotation + "deg) skewX(" + this.skewX + "deg) skewY(" + this.skewY + "deg)"); this.$.logo.applyStyle("-ms-transform", "rotate(" + this.rotation + "deg) skewX(" + this.skewX + "deg) skewY(" + this.skewY + "deg)"); } , components: [ {tag: "img", name: "logo", src: "assets/enyo-logo.png"}, {tag: "button", content: "Rotate +", onclick: "rotateC"}, {tag: "button", content: "Rotate -", onclick: "rotateA"}, {tag: "button", content: "Skew X +", onclick: "skewXInc"}, {tag: "button", content: "Skew X -", onclick: "skewXDex"}, {tag: "button", content: "Skew Y +", onclick: "skewYInc"}, {tag: "button", content: "Skew Y -", onclick: "skewYDec"} ] }); enyo.kind({ name: "App", fit: true, components: [ {kind: "RotateComponent"} ] }); 

You may have noticed that we combined all transformations in one method, and just call that method when something changes. This is necessary, because otherwise setting one property will reset the others! They are all transforms and applying a skew transform after applying a rotate transform will just reset the rotate transform. This is not what we want.

In the next part we will talk about the “Onyx” set of components that come with Enyo.


Leave a Comment

Your email address will not be published. Required fields are marked *