Confident React App – Part 3

This is the third post on the series about Confident React App where I show how I build up the confidence in the source code of my React apps. Here’s the beginning of the story.

In this post we’ll finally start coding some React components but following some vital rules that will help us make reliable code.

Coding confidently with BDD + TDD

Research has shown that Test Driven Development is one of the most effective measures for increasing the perceived quality of the developed software: from ~40% to ~80% increase. They also measured the increase in effort/cost for applying it.

We can’t skip such an effective technique, right? But how do we do it? Well, there’s the TDD bible to study and also
“The Three Laws of TDD” to follow:

  1. You are not allowed to write any production code unless it is to make a failing unit test pass.
  2. You are not allowed to write any more of a unit test than is sufficient to fail; and compilation failures are failures.
  3. You are not allowed to write any more production code than is sufficient to pass the one failing unit test.

That’s much easier said than done. After months of practice, it still takes me a while to get into the TDD flow so I need to be very aware to not break the rules. Because of the accrued learning curve and effort, it’s easy to make mistakes and misuse it which causes some resentment. The most common issue I observe is test code that is too coupled with implementation details, especially in React apps.

I suspect that this happens because of the mindset the developer is in when doing tests first: you get framed on testing everything! However, 100% test coverage is counterproductive since not every part of your code is equally worth the test hassle and it’s also an illusion.

The Behavior Driven Development approach is my favorite because it sets the best mindset: it builds on top of TDD focusing on business/user goals and drive towards shared understanding through conversations. In other words, it’s a high-level spec that fosters communication. When we read the test cases out loud, it feels like normal speech so it fits snugly into our minds. Good software is software that fits in our heads, which is easier and faster to create good mental models about it.

Our acceptance criteria are already written in BDD style, so the devs – we – have a perfect guide to start with.

Starting from the bottom

For this particular case, I think starting from the bottom/leave components will be easier since it’s basically the list-group component from Bootstrap. We won’t need to implement all the described features – but make sure to get familiar with it.

We quickly realize that list-group is composed of multiple list-group-item‘s. A list of items! So the leaf component should be <ListGroupItem>. Since the two are closely related, let’s keep their implementation in the same file.

So we create the two files and fire up the tests:

mkdir src/components && touch src/components/listGroup.js && touch src/components/listGroup.spec.js

Don’t forget to add // @flow.

I’ve picked Airbnb’s Enzyme test renderer because it has the necessary functionality for writing the kind of tests I prefer the most. Notably, Enzyme’s got a few pitfalls to be avoided. I’ll explain why I still use shallow soon enough.

To add the tools we need:

yarn add -D enzyme enzyme-adapter-react-16 jest-enzyme && yarn flow-typed install [email protected]

And add the new file src/setupTests.js:

// @flow
import { configure } from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
import 'jest-enzyme';

configure({ adapter: new Adapter() });

A Component’s purpose

We start by describing what we’re testing: the new <ListGroupItem /> component. Its sole purpose is to return valid JSX, compliant with the component contract. The HTML + CSS defined by our external dependency, Bootstrap, is the contract that our React component must fulfill. It should be fairly simple to test because our component is just a pure function.

For now, Enzyme’s shallow is the right tool for the job.

// src/components/listGroup.spec.js
// @flow
import React from 'react';
import { shallow } from 'enzyme';
import ListGroupItem from './ListGroup';

describe('<ListGroupItem />', () => {
  it('renders according to specification', () => {
    shallow(<ListGroupItem />)
  });
});

When we run the test watcher…

yarn test

… we get our first expected red of the cycle! This is obvious since there is no implementation yet. Keeping in mind the first rule of TDD, we implement our component to make this test pass:

// src/components/listGroup.js
// @flow
import React from 'react';

function ListGroupItem () {
  return null
}

export ListGroupItem;

Now we’re in the first expected green! Doesn’t that feel good? Let’s restart the cycle and get into red again by testing something more useful. The next step is to validate the generated structure. Red:

// src/components/listGroup.spec.js
it('renders according to specification', () => {
  expect(
    shallow(<ListGroupItem />)
  ).toContainExactlyOneMatchingElement('li.list-group-item');
});

The test code above takes advantage of the cool matchers from jest-enzyme to make our code more readable. To make it green, the implementation must return the proper markup:

// src/components/listGroup.js
function ListGroupItem () {
  return <li className='list-group-item' />
}

The specification determines that every list item can have a string or an anchor as children. Let’s start with the string and assume it’s required. Red:

// src/components/listGroup.spec.js
describe('<ListGroupItem />', () => {
  it('renders according to specification', () => {
    const wrapper = shallow(<ListGroupItem>item 1</ListGroupItem>);

    expect(wrapper).toContainExactlyOneMatchingElement('li.list-group-item');
    expect(wrapper).toHaveText('item 1');
  });
});

The implementation is more interesting now: in order to guarantee that the component is used correctly, we have to validate the given props. Flow comes in handy for this task. Green:

type ListGroupItemProps = {
  children: string,
};

function ListGroupItem (props: ListGroupItemProps) {
  return <li className="list-group-item">{props.children}</li>;
}

Note that the Jest watcher does not run Flow on file changes so type errors won’t break the tests. You can use flow-watch so Flow runs in every file change. Or, if you’re using an IDE, adding a Flow extension will come in handy.

This is enough features for this component for now. Let’s move on to the next component.

Red, Green, Red, Green, …

<ListGroup> is the parent component that has many <ListGroupItem>s. Just to save some time, here’s the whole component red tests:

describe('<ListGroup />', () => {
  it('renders according to specification', () => {
    const wrapper = shallow(
      <ListGroup>
        <ListGroupItem>item 1</ListGroupItem>
        <ListGroupItem>item 2</ListGroupItem>
      </ListGroup>
    );

    expect(wrapper).toContainExactlyOneMatchingElement('ul.list-group');
    expect(wrapper.find(ListGroupItem).length).toEqual(2);
  });
});

Now we have to put both components working together and since an empty list item didn’t make sense, a list group without list items doesn’t either. Green:

// src/components/listGroup.js
type ListGroupProps = {
  children: React.ChildrenArray<React.Element<typeof ListGroupItem>>
};

export function ListGroup (props: ListGroupProps) {
  return <ul className='list-group'>{props.children}</ul>
}

In the shallows, shallows…

shallow rendering is a powerful tool and having its documentation close by is quite handy. Never using it is a missed opportunity. Facebook has some more insights about it. Here are some reasons why I believe it’s the right tool for the component tests above:

  1. The components are just pure functions, no interactions with real DOM or higher-order components.
  2. Unit tests should be fast and shallow is faster than mount and render.
  3. Unit tests should be isolated. For <ListGroupItem> it’s easy because it’s completely independent but <ListGroup> depends on <ListGroupItem> as defined by the children argument type. Using mount/render would break the isolation of the component test because it renders the children.

Facebook’s react-dom/test-utils and react-test-renderer are testing libraries used by Enzyme. Which means that all the render stack from our test environment is mocked. The final result can be fully validated only when the code runs in a web browser. We’ll add another tool for validating components on the browser in another post.

Phew! What a ride. Thanks for reading all the way here. I really appreciate it. The series is not over though: the feature is far from done and the “leave components” are not completed yet.

Stay tuned for the next post in the series.
Cheers!

P.S.: All together now! In the shallow, shallow…

Flattr this!

Confident React App – Part 2

This is the second post in the series where I show how I build up the confidence in the source code of my React apps. Please read the first post before continuing.

Communications is the key

A study of public GitHub issues found out that 78% of all software bugs are caused by specification error. Take a moment to let that sink in… That’s a big percentage! Basically, the quality of your team’s communication skills is by far the best investment you can do in order to reduce bugs in any application. The same research shows that type checking, either with Flow or TypeScript account only for ~15% of bug prevention.

So make sure your team/organization strive for creating and fostering a culture of safe and effective communication. People should communicate using a ubiquitous language which helps clarify specific jargons, contexts, and symbols. Not to mention that even improving formal language skills can have a positive impact. There’s nothing more powerful than a person who’s articulated and can think and speak.

Also the tools for communication should not be neglected. Wiki, Slack, whiteboards, Jira, Post-its, sharpies, company phones, video conference rooms and what-not are means to an important end.

Understanding the feature

We would like to quickly see the GitHub issues reported in our enterprise software in an integrated fashion so that we can find the necessary information fast; without switching context.

Our Customers

To achieve the desires of our customers, our (imaginary) multi-disciplinary development team has done research and a lot of discussions. Afterward, we came up with the following acceptance criteria written in our own syntax/DSL:

* Github integration *
  When the user is in the issues screen
    it must show a loading indicator while repo data is being fetched
    and repo data is available
      it must show a list of repos names
      it must show a summary of all open issues
      and there are open issues in a repo
        it must show the count of opened issues next to the repo name
      and when a repo is clicked
        and it is NOT selected
          it selects the clicked item
          and there ARE open repo issues
            it replaces the summary with the list of open repo issues
          and there ARE NOT open repo issues
            it shows the no issues message
        and it IS selected
          it deselects the clicked item
          it replaces the list of open repo issues with the summary

The trick for reading the above is that indentation must be taken into account. All sentences are appended to its parent group. Sentences on the same level of indentation are not appended. The sentences usually start with setting some pre-conditions (e.g.: data is available) or user behavior (e.g.: clicking an item) and they finish with some sort of outcome/side effect. (e.g.: selects the clicked item).

Here’s a complete example:
When the user is in the issues screen and repo data is available and a repo is clicked and it is NOT selected it selects the clicked item.

It is a bit weird at first but our team likes it and it works for us. Gherkin is a very popular choice if you don’t want to roll your own DSL.

Component specification

Our designers opted for using Twitter Bootstrap as the base of the app GUI and for the feature we’ll need the List Group component. The component’s documentation serves as specs and acceptance criteria for the React implementation that we’ll provide.

One of the variations of Bootstrap’s list-group component

So go back and read the acceptance criteria for the feature and the List Group specification. Try to imagine more use case scenarios or edge cases. Can you rewrite the sentences in a more clear way? Have you spoted an important missing thing?

In the next post we’ll dive into coding, I promise. But it will come with a twist.

Cheers!

Flattr this!

Confident React App – Part I

Kent C. Dodds has written one of the coolest pieces about software testing that I’ve read recently. It’s a great overview of the most common types of testing. The “CONFIDENCE you must have in the code you deliver” is an original light that he shines on the topic. Before you continue reading this, go read his article first. It’s well worth it!

OK, do you feel inspired too? Then let me explain and show you how I make my React codebases reliable and my confidence levels pretty high.

First step is to create our React app. I’ll use Create React App (CRA) since it’s super simple and still very popular.

npx create-react-app confident-react-app

Let’s start by fulfilling the base of Kent’s Testing Trophy requirements. For that we use a bunch of tools that will save us from most of trivial JS mistakes.

Linting

CRA uses ESLint by default which is great. From ESLint’s website: “JavaScript, being a dynamic and loosely-typed language, is especially prone to developer error. Without the benefit of a compilation process, JavaScript code is typically executed in order to find syntax or other errors. Linting tools like ESLint allow developers to discover problems with their JavaScript code without executing it.” And here’s the exact ESLint configuration used by CRA. You can see that the configuration also applies accessibility linting rules which is a really nice bonus. It helps us create code that respects some of the impaired internet users.

Static type checking

JS is a dynamic language and experienced developers can really take advantage of dynamic types and objects. But even experienced developers make silly mistakes, work with other less experienced devs and most of a JS codebase relies on static types anyway. So a codebase that uses a strong type system can benefit from detecting type error mistakes quite early. Facebook created and uses Flow, a static type checker for javascript.

OK, Flow is intrusive. In order to benefit from it, your code will look quite similar to TypeScript. To be honest, if you’re doing a small library/app and even working with other developers with limited experience, adopting Flow/TypeScript will probably not be worth it due to the added entropy. It’s a part of the nature of being a JS developer, you get burned many times until you finally learn and embrace. I personally got so used to it that most of the times I find super annoying to write so much type boilerplate.

Which one should you choose? That’s an ever going debate that I don’t know the answer to. I recommend that you try both and get the feeling for yourself. This kind of choice is just too hard to be impartial to take.

For this series of posts, lets assume we’re building an enterprise multi-national application in a team with dozens of developers spread across the globe. We’ll take Flow to the next level by using it in strict mode. From now on, every JS file must have // @flow in its first line, even tests.

yarn add flow-bin && yarn flow init

If you run the flow command it should report errors on the tests since it doesn’t recognize Jest’s global functions – itdescribe, etc. To fix this you must inform Flow that Jest is a library with all its interface definitions. Thankfully there’s flow-typed which is a tool for installing community-supported library interface definitions.

yarn add -D flow-typed && yarn run flow-typed install [email protected]

Unfortunately these tools do not run by default with CRA’s own scripts. Let’s validate our code for every action, so this how we setup the scripts in our package.json:

"scripts": {
    "start": "yarn validate && react-scripts start",
    "build": "yarn validate && react-scripts build",
    "test": "yarn validate && react-scripts test",
    "validate": "yarn lint && yarn flow",
    "lint": "eslint src/**/*.js",
    "flow": "flow",
    "flow-typed": "flow-typed"
  }

Phew! A lot of setup and we haven’t written any line of code yet. We deserve a break. Building up the confidence in your codebase is not so simple and can be hard to build – just like in real life – but the return on the investment will be worth it.

You can find the source code on Github.

Next post we’ll talk about what we’ll be building and how.

Cheers!

Flattr this!

Flexing the Flexbox muscle

In order to better understand CSS Flexbox let us build our own
basic Grid system. It’s also a good chance to practice separation of CSS code for layout and from styling. When the layout is separated from style, the code becomes less repetitive, easier to compose and reuse. But by no means what we’re doing here is production ready! This is just a small learning exercise. You’re invited to come learn with me.

All grid frameworks out there have their own rules, some kind of API, that is different from each other. Each library has
its own flavor which is natural since everyone has different ways to solve the same problem. You should experiment with a few and see which one you like the most. Our approach is very basic but will be enough for our goals. Let’s define our rules:

  1. No JavaScript
  2. The grid is delimited by a root element. All our classes and markup only work inside of it.
  3. There are only Rows and Columns.
  4. The grid can have Rows. Rows can have Columns. Columns can have Rows.
  5. Rows always take the full available width of its parent container.
  6. Columns share the whole available width of the Row if no size is specified. They shrink and grow as necessary. They only break/wrap if there’s no available space.

We start off the first use case: 2 rows that take the full-width, each with 3 columns that share equally the width available. Let’s write the markup first:

[pastacode lang=”markup” manual=”%3Cdiv%20class%3D%22flex-grid%22%3E%0A%20%20%3Cdiv%20class%3D%22row%22%3E%0A%20%20%20%20%3Cdiv%20class%3D%22col%22%3E%0A%20%20%20%20%20%20%3Ch1%3ERow%201%20Column%201%3C%2Fh1%3E%0A%20%20%20%20%3C%2Fdiv%3E%0A%20%20%20%20%3Cdiv%20class%3D%22col%22%3E%0A%20%20%20%20%20%20%3Ch1%3ERow%201%20Column%202%3C%2Fh1%3E%0A%20%20%20%20%3C%2Fdiv%3E%0A%20%20%20%20%3Cdiv%20class%3D%22col%22%3E%0A%20%20%20%20%20%20%3Ch1%3ERow%201%20Column%203%3C%2Fh1%3E%0A%20%20%20%20%3C%2Fdiv%3E%0A%20%20%3C%2Fdiv%3E%0A%20%20%3Cdiv%20class%3D%22row%22%3E%0A%20%20%20%20%3Cdiv%20class%3D%22col%22%3E%0A%20%20%20%20%20%20%3Ch1%3ERow%202%20Column%201%3C%2Fh1%3E%0A%20%20%20%20%3C%2Fdiv%3E%0A%20%20%20%20%3Cdiv%20class%3D%22col%22%3E%0A%20%20%20%20%20%20%3Ch1%3ERow%202%20Column%202%3C%2Fh1%3E%0A%20%20%20%20%3C%2Fdiv%3E%0A%20%20%20%20%3Cdiv%20class%3D%22col%22%3E%0A%20%20%20%20%20%20%3Ch1%3ERow%202%20Column%203%3C%2Fh1%3E%0A%20%20%20%20%3C%2Fdiv%3E%0A%20%20%3C%2Fdiv%3E%0A%3C%2Fdiv%3E” message=”” highlight=”” provider=”manual”/]

Working with invisible elements is not that easy so let’s add some visual cues so we can track our progress. A few borders and background color will do the trick:

[pastacode lang=”css” manual=”.flex-grid%20%7B%0A%20%20background-color%3A%20%23ddd%3B%0A%7D%0A%0A.flex-grid%20.row%20%7B%0A%20%20border%3A%201px%20dotted%20red%3B%0A%20%20box-sizing%3A%20border-box%3B%0A%7D%0A%0A.flex-grid%20.row%20.col%20%7B%0A%20%20border%3A%201px%20dashed%20blue%3B%0A%20%20box-sizing%3A%20border-box%3B%0A%7D” message=”” highlight=”” provider=”manual”/]

Time to turn on the Flexbox! Just add display: flex; to .flex-grid and Boom! The layout is totally wrecked now but we’ll adjust it step-by-step.

Since our grid can only have Rows as direct children, and every row must always be on top of each other, we have to add flex-direction: column; to .flex-grid. Wait, what!? Rows as columns?! Yep. Not very intuitive at first glance. That’s why this exercise is important: it will put us out of our comfort zone and force us to really grasp the theory and put it into practice.

So what’s going on here?

When we set the flex-direction to “column“, we’re telling that we want every direct child element of the flex container to be displayed on top of each other to form a column/stack. Which is what we want: rows stacked on top of each other occupying the whole width.

Unfortunately, the columns are behaving just like Rows too. We want that all columns of a row share the available width by default. This also means that the columns must appear next to each other, in a line. We could achieve this by setting display: inline; or float: left; to the columns … but that’s not Flexbox. Since the layout of the Columns is ruled by the parent container, setting display: flex; to .flex-grid .row does one of the tricks: Columns in a row. But now the Columns only occupy the space required by its contents. To fix this we must tell the Columns to grow as much as they can by setting flex: 1; to .flex-grid .row .col. Uh la la!

Things are starting to look good but let’s first try some nesting. Replace the contents of the first Column of the first Row with the following snippet.

[pastacode lang=”markup” manual=”%3Cdiv%20class%3D%22row%22%3E%0A%20%20%3Cdiv%20class%3D%22col%22%3E%0A%20%20%20%20%3Cspan%3EInner%20Row%201%20Col%201%3C%2Fspan%3E%0A%20%20%3C%2Fdiv%3E%0A%20%20%3Cdiv%20class%3D%22col%22%3E%0A%20%20%20%20%3Ch4%3EInner%20Row%201%20Col%202%3C%2Fh4%3E%0A%20%20%3C%2Fdiv%3E%0A%3C%2Fdiv%3E%0A%3Cdiv%20class%3D%22row%22%3E%0A%20%20%3Cdiv%20class%3D%22col%22%3E%0A%20%20%20%20%3Cp%3EInner%20Row%202%3C%2Fp%3E%0A%20%20%3C%2Fdiv%3E%0A%3C%2Fdiv%3E%0A%3Cdiv%20class%3D%22row%22%3E%0A%20%20%3Cdiv%20class%3D%22col%22%3E%0A%20%20%20%20%3Cspan%3EInner%20Row%203%3C%2Fspan%3E%0A%20%20%3C%2Fdiv%3E%0A%3C%2Fdiv%3E” message=”” highlight=”” provider=”manual”/]

Beautiful, it works wonders out of the box! This truly shows the potential of Flexbox. Let’s try to add some width modifier to our columns so we can have so more control over our layouts. We’ll skip responsive breakpoints for now and apply the widths for all sizes:

  • .all-20: 1/5 (20%)
  • .all-25: 1/4 (25%)
  • .all-33: 1/3 (33%)
  • .all-50: 1/2 (50%)
  • .all-60: 3/5 (60%)
  • .all-66: 2/3 (66%)
  • .all-75: 3/4 (75%)

Another important detail: when we specify the column width, that’s the min and max width it should have -a fixed proportion. So we add the following selectors and rules:

[pastacode lang=”css” manual=”.flex-grid%20.row%20.col.all-20%20%7B%0A%20%20max-width%3A%2020%25%3B%0A%20%20width%3A%2020%25%3B%0A%7D%0A%0A.flex-grid%20.row%20.col.all-25%20%7B%0A%20%20max-width%3A%2025%25%3B%0A%20%20width%3A%2025%25%3B%0A%7D%0A%0A.flex-grid%20.row%20.col.all-33%20%7B%0A%20%20max-width%3A%2033%25%3B%0A%20%20width%3A%2033%25%3B%0A%7D%0A%0A.flex-grid%20.row%20.col.all-50%20%7B%0A%20%20max-width%3A%2050%25%3B%0A%20%20width%3A%2050%25%3B%0A%7D%0A%0A.flex-grid%20.row%20.col.all-60%20%7B%0A%20%20max-width%3A%2060%25%3B%0A%20%20width%3A%2060%25%3B%0A%7D%0A%0A.flex-grid%20.row%20.col.all-66%20%7B%0A%20%20max-width%3A%2066%25%3B%0A%20%20width%3A%2066%25%3B%0A%7D%0A%0A.flex-grid%20.row%20.col.all-75%20%7B%0A%20%20max-width%3A%2075%25%3B%0A%20%20width%3A%2075%25%3B%0A%7D” message=”” highlight=”” provider=”manual”/]

So if we add .all-25 to Column 2 of Row 1, it shrinks to 1/4 of the Row width as expected and the other two Columns grow. But if instead of .all-25 we apply .all-75 it doesn’t work, they all occupy 1/3 of the width. This is a bit trickier.

When we set flex: 1; it’s actually a short-hand for setting flex-grow: 1; flex-shrink: 1; flex-basis: 0%; The most important detail being flex-basis: 0%;. This means that the element will not enforce its min size. And since all other Columns have the same rule, the result is that they only grow to the max width which is equally divided. To make sure the Column has the width it was supposed to have, we got to set flex-basis: 100%; to all width modifiers. And now it’s working!

But what happens when we apply .all-25 to Column 1 of Row 1? The first two Columns (1/4 + 3/4 = 1) should occupy 100% of the Row’s width but why the hell Column 3 is still in the same Row?! Why didn’t it break to the second line? Well, that’s because we didn’t tell the Row to allow it. And to do it we must set flex-wrap: wrap; to .flex-grid .row.

Another interesting feature we can add to our Flexbox grid system is the ability to align Columns. And who controls the alignment? The parent container: Row! We can create the following modifier with the Flexbox property justify-content to put Columns at the horizontal center of the Row:

[pastacode lang=”css” manual=”.flex-grid%20.row.hor-center%20%7B%0A%20%20justify-content%3A%20center%3B%0A%7D” message=”” highlight=”” provider=”manual”/]

It’s important to note that this only works if the Columns have a defined width. How can you know what’s the center of the Row if the Column is occupying the whole width? Once the Column has a width, say 50%, the Row can calculate how much space is left and position the Column at its horizontal center.

But you want to put the Columns at the vertical center of the Row, right? For that, we need a different Flexbox prop: align-items. If justify-content works by placing elements on the X/horizontal axis, align-items works on the Y/vertical axis. So we add a new Row modifier to center columns vertically:

[pastacode lang=”css” manual=”.flex-grid%20.row.vert-center%20%7B%0A%20%20align-items%3A%20center%3B%0A%7D” message=”” highlight=”” provider=”manual”/]

There you go! A super basic base for a grid system implemented with Flexbox. Just enough features so we can practice some core concepts and solidify them in our CSS knowledge base.

The whole CSS for our basic Flexbox grid system:

[pastacode lang=”css” manual=”.flex-grid%20%7B%0A%20%20%20background-color%3A%20%23ddd%3B%0A%20%20%20display%3A%20flex%3B%0A%20%20%20flex-direction%3A%20column%3B%0A%20%7D%0A%0A.flex-grid%20.row%20%7B%0A%20%20%20border%3A%201px%20dotted%20red%3B%0A%20%20%20box-sizing%3A%20border-box%3B%0A%20%20%20display%3A%20flex%3B%0A%20%20%20flex-wrap%3A%20wrap%3B%0A%20%7D%0A%0A.flex-grid%20.row.hor-center%20%7B%0A%20%20justify-content%3A%20center%3B%0A%7D%0A%0A.flex-grid%20.row.vert-center%20%7B%0A%20%20align-items%3A%20center%3B%0A%7D%0A%0A.flex-grid%20.row%20.col%20%7B%0A%20%20%20border%3A%201px%20dashed%20blue%3B%0A%20%20%20box-sizing%3A%20border-box%3B%0A%20%20%20flex%3A%201%3B%0A%20%20%20padding-right%3A%20.5em%3B%0A%20%7D%0A%0A.flex-grid%20.row%20.col%3Alast-child%20%7B%0A%20%20%20padding-right%3A%200%3B%0A%20%7D%0A%0A.flex-grid%20.row%20.col.all-20%20%7B%0A%20%20%20flex-basis%3A%20100%25%3B%0A%20%20%20max-width%3A%2020%25%3B%0A%20%20%20width%3A%2020%25%3B%0A%20%7D%0A%0A.flex-grid%20.row%20.col.all-25%20%7B%0A%20%20%20flex-basis%3A%20100%25%3B%0A%20%20%20max-width%3A%2025%25%3B%0A%20%20%20width%3A%2025%25%3B%0A%20%7D%0A%0A.flex-grid%20.row%20.col.all-33%20%7B%0A%20%20%20flex-basis%3A%20100%25%3B%0A%20%20%20max-width%3A%2033%25%3B%0A%20%20%20width%3A%2033%25%3B%0A%20%7D%0A%0A.flex-grid%20.row%20.col.all-50%20%7B%0A%20%20%20flex-basis%3A%20100%25%3B%0A%20%20%20max-width%3A%2050%25%3B%0A%20%20%20width%3A%2050%25%3B%0A%20%7D%0A%0A.flex-grid%20.row%20.col.all-60%20%7B%0A%20%20%20flex-basis%3A%20100%25%3B%0A%20%20%20max-width%3A%2060%25%3B%0A%20%20%20width%3A%2060%25%3B%0A%20%7D%0A%0A.flex-grid%20.row%20.col.all-66%20%7B%0A%20%20%20flex-basis%3A%20100%25%3B%0A%20%20%20max-width%3A%2066%25%3B%0A%20%20%20width%3A%2066%25%3B%0A%20%7D%0A%0A.flex-grid%20.row%20.col.all-75%20%7B%0A%20%20%20flex-basis%3A%20100%25%3B%0A%20%20%20max-width%3A%2075%25%3B%0A%20%20%20width%3A%2075%25%3B%0A%20%7D%0A” message=”” highlight=”” provider=”manual”/]

This is a very limited implementation and we can still achieve some interesting results:

Keep on honing your skills while having some fun.
Cheers!

Flattr this!

Still having fun with Flash

I would like to share some little piece of code that I wrote recently in AS3. It’s been quite a while since last time I did anything with Flash technology and I must admit that I had loads of fun. Maybe too much. I still can’t believe that this technology is dying in favour of HTML considering only the technical parts. I really support what the Web and all it’s technology represents and how much it empowers people who care to learn it. I really get inspired by the folks at Mozilla and how hard they work to keep the web an open competitive place for everyone.

But dealing with browser glitches and wimps, render engines optimisations, thousands of mobile phones, hunting for bugs on hundreds of CSS rules and so on is just a freaking nightmare! Specially when you are on the frontline on release day. It’s totally doable, I get paid really well to do it, but it is not fun. There’s also that dread that every single day there’s a newer technology supposedly much better the one you’ve been learning on the past 24h. There are so many frameworks and libraries and plugins to handle every device/browser/os. Or maybe it’s your client/project/boss that demands some specific technology. Flash and Java have the same problem but it’s much much smaller. Instead of choosing between 5 Flash libraries for animating something on the screen, you have to choose between 15 in HTML. By the way, those are fictional numbers just so that you can feel the dread. Or is it really?

In my opinion, Flash will not go away anytime soon. It changed too many things, it runs in too many environments, it works for many projects, there are too many experts and tools and books and blogs and knowledge. You can’t kill something this big that easy. And it’s also a lot of fun!

Enough sentimental blabber! I implemented a small super efficient image scroller using a few 2D rendering tricks of the platform. Using Stage3D would probably give even better results but it’s out of my reach for now. This little program works really well for navigating huge panoramic images smooooothly.  Check it out for yourself. The PanoViewer Demo is a release build so make sure to use a Release Flash Player plugin. You can get a really amazing effect if your monitor has very low response time and delay. My monitor failed miserably on these tests so the effect beauty is really diminished.

There are two projects: PanoViewer and Panomax.

PanoViewer

It’s the Flash application that handles the logic of loading assets, rendering buttons and initializing the Panomax instance. It’s important to notice the SWF metadata tag with the frameRate = “1” parameter. This specifies the frame rate of the application, in frames per second. The default value is 24. Lowering it’s value gives the runtime a much longer Elastic Racetrack , roughly 1/2 second for each phase, which also means lower loads on the system. This is a key concept to understand and we’ll exploit it.

The CachingImageLoader is a very simple external image loader with very basic caching functionality and tries to deal with Security Sandbox details. The CTAButton – Call To Action – is a custom button class with different style and it makes sure the text label always fits its boundaries.

Panomax

Here’s where the magic happens. The loaded panoramic image (Bitmap) and other parameters are passed to the Panomax instance. It calculates all boundaries, configure the interactions and starts a Timer. On the TimerEvent handler function that all matrix transformations are calculated and where a part of the panoramic image is painted on the canvas. If it was not obvious yet, painting a small portion of an image is much faster than moving a gigantic image around. We just need 1 Matrix object and 1 call to BitmapData.draw(…). Things start to get very interesting when we add the horizontal looping (function onTimerXLoop). To help us understand how it works I made this schematic:

Panomax schematics

You have to start from the middle, where gTx = 0. If you go reading up, gTx increases and the image is moving to the left. If you go reading down, the image is moving to the right. Let’s not forget that Matrix transformations (tx, ty) signs means that negative is to the left and positive is to the right. Here’s an awesome Matrix transformation tutorial if you are not familiar. Since the image loops horizontally, the are some states in which it needs to be rendered twice: one on the far left and the other on the far right. For that we use 2 Matrix objects and 2 BitmapData.draw(…) calls which is the heaviest state for the system.

There are some clever tricks that we can do with the transformations of the matrixes in order to use simple formulas. For instance, once the left edge of image A moves out on the right edge of the canvas, it looks exactly like image B with its right edge on the right edge of the canvas which also looks exactly like image A with it’s right edge on the right edge of the canvas. So we can swap/render B with A! Clearly, from my code, I couldn’t figure out the simple formulas so I ended up with complicated ones.

Now, for the last trick, we need to analyse just 1 line of code: e.updateAfterEvent(); // for immediate rendering! To understand this you really have to dominate the concept of the Elastic Racetrack that I mentioned earlier. With the app frame rate reduced to 1, our async code (the TimerEvent handler function) has more time to run and most probably won’t interfere with the other phases of the racetrack. So the Matrix calculations are done, the images are rendered on the canvas but the application frame is not updated yet because the rendering phase is still many milliseconds away. Thus we force the rendering with plenty of time to do so. With enough time for both racetrack phases, what we get is butter smooth scrolling performance!

This is it! I hope you liked it this tutorial. The code is on Github so go ahead and study and fork it!

Panorama Image Credits

  1. SonyCenter 360 panorama: Homepage, Download, Author: François Reincke

    Full resolution: 3,500 × 1,096 pixels, file size: 922 KB, MIME type: image/jpeg, License: CC-BY-SA 3.0.

  2. Trafalgar Square: Homepage, DownloadAuthor: David Iliff alias DiliffFull resolution: 9.932 × 2.075 pixels, File size: 5.67 MB, MIME type: image/jpeg, Camera location: 51° 30′ 29.39″ N, 0° 7′ 41.31″ W, License: CC-BY-SA 3.0.
  3. Lac de Joux: Homepage, Download,Author: Lausanne De Suisse alias 100zaxFull resolution: 6.000 × 697 pixels, File size: 759 KB, MIME type: image/jpeg, Camera location: ??? N, ??? W,License: CC-BY-SA 3.0.

Flattr this!