OAM and React & SVG Starter

OAM

DemoSource

Last April Ainsley Wagoner posted this awesome dribbble shot. I loved the idea – it reminded me of colour field paintings. I reached out to her and a few months later we started working together to built it.

ooaamm ainsley
OAM site dribbble shot

Around this time I had started to learn how to use redux. So, instead of building yet another To Do app I decided to learn redux by building OAM. This turned out to be a great experience. I learnt a lot about, React, redux and animating SVG.

React SVG Starter

I’ve been using React, SVG and redux for a lot of projects lately. Therefore, in order to save myself some time I made a React Redux SVG Starter. It’s based off of Rangle.io’s react-redux-starter and comes with the usual stuff like Webpack, Babel, HMR, Eslint, etc. In addition to this I’ve also setup a few components and reducers to get started quickly.

The Canvas

To setup the root SVG node I created a Canvas component. I learnt a few lessons here. First of all use children to keep the Canvas component light. This way the it doesn’t need to know about the state of the shapes being rendered. It is simply responsible for maintaining the <svg> element and sizing it to fill the window.

Secondly, using viewBox allows us to detach the canvas coordinate system from any pixel values. Thanks to this all the shapes can be described using relative coordinates yet, the SVG scales to whatever size you want.

const Canvas = ({ w, h, children }) => {
  const viewBox = [0, 0, w, h].join(' ');

  return (
    <svg
      version="1.1"
      xmlns="http://www.w3.org/2000/svg"
      width="100%"
      height="100%"
      viewBox={viewBox}
      style={styles}
    >
      {children}
    </svg>
  );
};

That state for window and view box sizes is stored in the canvas reducer. The App container binds the window resize events to redux actions for updating this state.

The view box is calculated using this formula:

const w = width >= height ? 100 : (width * 100) / height;
const h = height > width ? 100 : (height * 100) / width;

This means that the longer side of SVG is 100 and the shorter side will be some value between 0 - 100 based on the aspect ratio. I found this made it easier to size & place any shapes and achieve responsive behaviour.

Animations

Chrome is going to deprecate SMIL so, I tried to use CSS for animations as much as possible. This came with a lot of cross browser issues. Eventually I gave up and switched to using GSAP for all animations. In the future I plan experiment more with React Motion and Web Animation API.

Sound

To load the sounds I used the webpack file loader which works great. For triggering them I initially used the HTML5 Audio API before switching to Howler.

import oam1 from '../audio/OAM_1.mp3';
const Sounds = { base: new Audio(oam1) };
Sounds.base.play();

With the Audio API, I found that if I triggered play in quick succession it wouldn’t actually play the sound if the previous play call was still executing. I haven’t worked much with the Audio API and probably just don’t know how to use it properly. In any case, Howler did exactly what we were looking for.

All the sounds are connected to UI actions. Therefore, I ended up placing them in the reducers, for example: circle-reducer.js#L30. This way the sound is played when the state is updated.

Creative Coding with React & SVG

Lastly I want to share the video for a talk I gave a talk at FITC Toronto 2016. In this talk I go through a basic tutorial of how to get started creating SVG images with React and adding animations to them. Also, I shared some examples and talked about the benefits of using this approach.

🎬 slides: winkervsbecks.github.io/creative-coding-with-react-svg

Questions, Comments or Suggestions? Open an Issue

Creative coding from a front-end developer's perspective

My goal with this blog is to go beyond the basics. Breakdown my sketches. And make animation math approachable. The newsletter is more of that.

Hear about what's got my attention—new tools and techniques I've picked up. Experiments I'm working on. And previews of upcoming posts.