Reacting to touch and animation



Reacting to touch and animation

1 2


react-touch-animate

A little reveal.js presentation talking about React

On Github josephinehall / react-touch-animate

Reacting to touch and animation

Josephine Hall / @jojoconstance

Reppin' Icelab and the ACT

I'm going to be talking today about some techniques we at Icelab used recently on a React project dealing with css animations, and responding to user touch events.

React...

User interfaces, components and state

So what do you need to know about React?

  • It's a javascript library for building user interfaces.
  • It has its own syntax extension called JSX, which looks like js mixed with xml
  • A React app is made up of components, which reprsent a contained piece of UI functionality
  • Each component is responsible for determining how it will be rendered, what data it will use, and what it will do.
  • The *state* of a component is a key concept - modifying the state changes how a component is rendered.
var Button = React.createClass({
  render: function() {
    if (this.state.disabled){
      //don't display the button
    } else {
      return (
        <div onClick={this._getLocation} className="find-button">
          <span className="find-button__text">Nearby</span>
          <i className="find-button__icon"/>
        </div>
      );
    }
  },
  _getLocation: function() {
    AppDispatcher.trigger("geolocator:request");
    this.setState({
      disabled: true
    });
  }
});

React.renderComponent(
  <Button />
  document.body
);

In this component responding to user input (via _getLoaction) modifies the state of the component, triggering a re-render. React components *know* their data and can figure out their state rather than inferring it from the DOM.

For me, it was helpful to think of components as objects (object oriented programming), rather than a series of maniuplations on dom elements.

A good example is an image carousel - when you create the carousel, you pass in the images that will be rendered as a property. The component then takes that data and figures out how it needs to be rendered. This might include things like calculating the width of wrapper divs based on the number of imaged you passed in.

Can't touch this

(DOM)

Not strictly true. But best to avoid as much as possible. What if you want a way to add different classes to a component to respond to input from the user, like touching.

JSX block:

<div>
     <TouchPreview previewClassName="link--preview">
         <a href="/foo">
            Hello
        </a>
     </TouchPreview>
 </div>

CSS:

a {
    color: blue;
}

.link--preview a {
    color: red;
    text-decoration: underline;
}
TouchPreview = React.createClass({
//sets the default preview class name if not passed in by this.props
  getDefaultProps: function() {
    return {
      previewClassName: "preview"
    };
  },
  getInitialState: function() {
    return {
      preview: false
    };
  },
  render: function() {
//extend the built in React events to include our behaviour
    var props = _.extend({
      onTouchStart: this._startPreview,
      onMouseOver:  this._startPreview,
      onTouchEnd:   this._endPreview,
      onTouchMove:  this._endPreview,
      onMouseOut:   this._endPreview
    }, this.props);
//set the class property to be our preview class
    props.className = (this.state.preview) ?
    									this.props.previewClassName : "";
//clear out the props so that the class can be different next time
    delete props.previewClassName;
//return a span with all these properties
// set on it, preserving the child elements
    return React.DOM.span(props, this.props.children);
  },
  _startPreview: function(e) {
    this.setState({
      preview: true
    });
  },
  _endPreview: function(e) {
    this.setState({
      preview: false
    });
  }
});
Since JSX is JavaScript, identifiers such as class and for are discouraged as XML attribute names. Instead, React DOM components expect attributes like className and htmlFor, respectively.

Animating a list that might get re-rendered, say what??

We wanted to make nice smooth transitions on large nested lists that could be re-rendered while they’re animated

Using it...

_renderResultType: function(type) {
var results = this._renderResults(this.props.latLng, type);
return (
  <AnimationItem key={type+"group"} prefix="result-group">
    <div className="results-list-body__group">
      {results}
    </div>
  </AnimationItem>
  );
}
var AnimationItem = React.createClass({
  componentWillEnter: function(done) {
    this.el = this.getDOMNode();
    this.$el = $(this.el);
    // Before state applied immediately
    this.$el.addClass(this.props.prefix+"-enter-before");
    // Ensure parent class changes propagate in time
    requestAnimationFrame(function() {
      this.$el
        .removeClass(this.props.prefix+"-enter-before")
        .addClass(this.props.prefix+"-enter");

      requestAnimationFrame(function() {
        getComputedStyle(this.el);
        this.$el.addClass(this.props.prefix+"-enter-active");
        Arrival.complete(this.$el, done);
      }.bind(this));
    }.bind(this));
  },
  componentDidEnter: function() {
    this.$el
      .removeClass(this.props.prefix+"-enter")
      .removeClass(this.props.prefix+"-enter-active");
  },
  componentWillLeave: function(done) {
    this.el = this.getDOMNode();
    this.$el = $(this.el);
    // Before state applied immediately
    this.$el.addClass(this.props.prefix+"-leave-before");
    requestAnimationFrame(function() {
      this.$el
        .removeClass(this.props.prefix+"-leave-before")
        .addClass(this.props.prefix+"-leave");
      requestAnimationFrame(function() {
        this.$el.addClass(this.props.prefix+"-leave-active");
        Arrival.complete(this.$el, done);
      }.bind(this));
    }.bind(this));
  },
  componentDidLeave: function() {
    this.$el
      .removeClass(this.props.prefix+"-leave")
      .removeClass(this.props.prefix+"-leave-active");
  },
  render: function() {
    return (
      React.DOM.div(
        {className: "animation-item", key: this.props.key},
        this.props.children
      )
    )
  }
});

ReactTransitionGroup - gives us the extra lifecycle hooks

We have to do multiple requestAnimationFrame calls to make sure that the browser has rendered class attributes further up the tree that we’re relying on. In this app we add a little .direction--up or .direction--down class to the layout block to signify if you’re moving up or down the perceived navigation stack as well.

jQuery! We needed a way to actually manipulate the classes. We only had to use it in two or three places across the whole app - once you get used to how React works you find you don't really need it

If we made this again, we'd probably give javascript specified animations a go.

TouchPreview component

Animation Item codepen

All due credit for these goes go my esteemed colleague Max (@makenosound), he knows a thing or two