Archive » Animations (pre X22) » Toggle animation

Toggle animation

Warning: These pages are from before the launch of Framer X22 and are about the older animation API. These animations might still work in the current version of Framer but are officially deprecated. Learn about the current animation API here.

We want an animation that goes back and forth between two different states.

One of Framer’s example overrides (in Examples.tsx) actually does this.

It’s a set of two overrides. You attach FlipOutput to the frame you want to flip, and with the other one, FlipInput, you start the animation.

export const FlipOutput: Override = () => {
  return {
    rotationY: data.rotationY
  };
};

export const FlipInput: Override = () => {
  return {
    onTap() {
      const toggle = data.toggle;
      animate.spring(
        { rotationY: data.rotationY },
        {
          rotationY: toggle ? 360 : 0
        },
        { tension: 200, friction: 20 }
      );
      data.toggle = !toggle;
    }
  };
};

It contains an extra (Boolean) variable, data.toggle to keep track of the state of the frame.

The state being: Was the frame rotated (around the y-axis) to 360 degrees, or is it back at the initial 0 degrees?

If you’re wondering why it animates back from 360 to 0 (which actually looks the same). It’s because animating to 360 for a second time, with the frame already being in that position, would have no effect.

An ON/OFF button

We’ll also use a Boolean variable in which we save the ‘ON’ or ‘OFF’ state of our button.

Here I used toggle; initially it’s true, so that will stand for ‘ON’.

const data = Data({
  color: Animatable("green"),
  toggle: true
});

The Animatable is for the button’s "green" color.

In the override function, I create a variable, animateToColor, in which I can save the color to animate to. It’s a let because I’ll need to change it.

Then, with an if…else statement, I check whether data.toggle is true.

When it is, I set animateToColor to "red", and when not, I set it back to "green".

export const Button: Override = () => {
  return {
    background: data.color,
    onTap() {
      let animateToColor = "";
      if (data.toggle) {
        animateToColor = "red";
      } else {
        animateToColor = "green";
      }
      animate(data.color, animateToColor);
      data.toggle = !data.toggle;
    }
  };
};

I then animate to the correct color.

And I set data.toggle to its inverse value by typing a ! before it. (If it was true then it will be false, and also the other way round.)

But actually, this can be shorter, like this:

export const Button: Override = () => {
  return {
    background: data.color,
    onTap() {
      const animateToColor = data.toggle ? "red" : "green";
      animate(data.color, animateToColor);
      data.toggle = !data.toggle;
    }
  };
};

It now uses the JavaScript ternary operator, a shorter way of writing an if…else statement.

If data.toggle is true, then the first value (after ?) will be used, and if not, the second value (after :). Just one line instead of the above six lines.

Text on the button

We can add a button label as well. You can’t animate text, so it shouldn’t be an Animatable but a common string variable.

const data = Data({
  color: Animatable("green"),
  label: "ON",
  toggle: true
});

And I made a separate override that I attached to the text block.

export const ButtonText: Override = () => {
  return {
    text: data.label
  };
};

When the button is tapped, I change the data.label to either "OFF" or "ON", also in a one-liner.

export const Button: Override = () => {
  return {
    background: data.color,
    onTap() {
      const animateToColor = data.toggle ? "red" : "green";
      animate(data.color, animateToColor);
      data.label = data.toggle ? "OFF" : "ON";
      data.toggle = !data.toggle;
    }
  };
};

All together it looks like this:

import { Data, animate, Override, Animatable } from "framer";

const data = Data({
  color: Animatable("green"),
  label: "ON",
  toggle: true
});

export const ButtonText: Override = () => {
  return {
    text: data.label
  };
};

export const Button: Override = () => {
  return {
    background: data.color,
    onTap() {
      const animateToColor = data.toggle ? "red" : "green";
      animate(data.color, animateToColor);
      data.label = data.toggle ? "OFF" : "ON";
      data.toggle = !data.toggle;
    }
  };
};
download this project