Framer For People Who Think Things Like Framer Are Weird And Hard

Sean Mateer
Framer
Published in
11 min readJul 30, 2015

--

A tutorial to help non-programmers get started with Framer.
Updated for Framer v72.

Thanks to Chris Coyier for the title inspiration and great read that made me stop thinking things like Grunt were weird and hard.

How to draw an owl

The first time I looked at an example project in Framer Studio, it felt like the “How to draw an owl” instructions. I played around with the demos and browsed the documentation, but quickly gave up, thinking the learning curve was too steep and I didn’t have the time to devote.

“A fun and creative guide for beginners.”

It was too weird and hard.

Since then, I had tried many other prototyping tools, but they either didn’t have the functionality I desired at the time, or also had fairly steep learning curves.

In an act of desperation and frustration, I gave Framer another try. Much to my surprise, it wasn’t long l before I’d created some semblance of a completed owl.

To Framer, or not to Framer?

If you’ve already got a favorite prototyping tool that works for you and your workflow, I won’t try to sway you into using Framer (also, what tool is it?). I was only compelled to share my experience with Framer after my perception of its weirdness and difficulty was turned upside down.

But CoffeeScript is scary

CoffeeScript is a language the compiles into JavaScript, just like Sass or Less into CSS.

Another layer of abstraction beyond JS? Are they trying to make this even harder?

You might think you need to be a JavaScript expert to use Framer, but in reality, Framer is doing a lot of the hard work by providing awesome documentation and familiar properties to interact with your design. CoffeeScript does you an additional favor and gets rid of all the icky JS syntax, so you can concentrate on making an amazing prototypes.

Many of the properties used in Framer are exactly the same, or extremely similar to CSS properties. If you know basic CSS, Framer shouldn’t be too scary.

For example, here’s how you can make a circle with CSS.

// A circle in CSS.circle {
width: 200px;
height: 200px;
border-radius: 100px;
background-color: red;
}

Here’s how you can make one in CoffeeScript with Framer.

# A circle in CoffeeScript with Framercircle = new Layer 
width: 200
height: 200
borderRadius: 100
backgroundColor: “red”

Suspiciously similar, isn’t it? Just for comparison, here’s what that CoffeeScript looks like when it’s compiled into JavaScript.

// Icky complied JS we don't care about(function () {
var circle;
circle = new Layer({
width: 200,
height: 200,
borderRadius: 200,
backgroundColor: ‘red’
});
}.call(this));

Gross. That just seems complicated and full of syntax. Lucky us, we will never need to look at the compiled JS.

“If I’m going to go to that much work, I might as well just build it.”

This is something I’ve heard quite a bit from friends and colleagues, and have been guilty of thinking myself. But ask yourself: are you going to actually build it (kudos), or are you going to end up trying to explain an intricate interaction to a developer and be upset when it isn’t built how you imagined?

If you want to accurately communicate high-fidelity interactions, a verbal description or napkin sketch isn’t going to cut it. After all, this is what you are an expert in — it’s worth learning how to use the tools that communicate your expertise.

Whether it’s Framer or another weird and hard prototyping tool, with a little bit of investment time you’ll be quickly slinging out prototypes and everyone will ❤ you, including your end users for giving them an intuitive and delightful experience.

Enough talk, let’s make the owl

As an intro to Framer, we’ll walk though making this draggable onboarding demo using elements from InVision’s Sketch to-do app ui kit. If you’re brand new to both Sketch and Framer, you can expect this tutorial to take roughly 30–45 minutes.

Our end results will look something like this.

If you haven’t yet, download Framer and the Sketch file we’ll be working with.

Import the Design and Choose a Device

Framer will automatically import your design from Sketch. You can make updates and re-import whenever you want. Let’s start by importing the file provided. Open a new file in Framer Studio, click import, and select ‘@1x’ from the dropdown before importing. (Sketch and the project you wish to import must be open).

In the latest versions of Framer, you can import Sketch files at increased resolutions. Since our Sketch artboard is already designed at 2x for an iPhone 6/7 (750 x 1334) we should import it at 1x so that it isn’t further scaled.

Make sure your Sketch file is open and saved so Framer can find it.

The newest version of Framer offers even more device types for displaying your prototypes in. Make sure that your device is set to an iPhone 6 or iPhone 7 so that the imported sketch design scales correctly.

sketch = Framer.Importer.load("imported/framerOnboard")

Cool. The bit of code Framer generated lets us know that our Sketch layers have been turned into an object that we will be able to reference. We’ll talk more about that soon. You’ll see the folder structure of the Sketch file directly to the right of the code. If you roll over items, you’ll see them highlighted in the device and useful information like their positions and sizes displayed.

You’ll also likely see a comment above the import code that Framer generated:

# Import file "framerOnboard"

The ‘#’ designates that the text following it is a comment. You can create your own comments in Framer by typing ‘#’. You can also highlight existing text and comment it out by pressing cmd and / (forward slash). The import comment isn’t necessary, so you can delete it if you’d like.

To make it easier to reference the Sketch layers we want in Framer with code, you can change the name of the variable referencing the Sketch layers. I like to use a dollar sign because it is unique in the Framer environment, much quicker to type than ‘sketch’, and because $$$.

$ = Framer.Importer.load “imported/framerOnboard@1x”

Set up initial states

Our first card is right where we want it, but the second and third cards are directly behind it. Let’s set up an initial state for our second card. Since we made a reference to all of the layers with $ (or whatever variable you used), we can access any specific layer name through dot notation. Adding another dot and a property name lets us change the value of that property.

In our Sketch file, the layer placeholder2 has a position x = 700 and y = 300. Let’s set card2 to the same position in Framer by typing the following code:

$.card2.x = 700
$.card2.y = 300

You should see Framer update in real time. Our card2 is now 700 px from the left, and 300 px from the top.

Next, hide the placeholder cards using the opacity property. The placeholder cards are ultimately unnecessary to the design, but served as a reference in the Sketch layout for where we want to dynamically place our inactive cards. Additionally, set the opacity of card3 to zero, we’ll deal with that later.

$.placeholder1.opacity = 0
$.placeholder2.opacity = 0
$.card3.opacity = 0

Now let’s scale our inactive card2 down like it is in the Sketch layout. The default scale property is set to 1, so let’s size it down just a bit to 0.85.

$.card2.scale = 0.85

Bummer, looks like card2 may have disappeared off the screen. This is because by default, all objects scale from the center — just like when you hold shift + opt to resize something in Photoshop or Sketch. Let’s have our card scale with its origin at the left and top instead.

$.card2.scale = 0.85
$.card2.originX = 0
$.card2.originY = 0

The originX and originY properties tell us which side of the object we want to scale, rotate and skew from, just like transform-origin in CSS. originX and originY can have a value between 0 and 1 and have a default value of 0.5. Below is an example of how you can think about how origins effect the direction an object scales in relation to how you might resize something in Sketch or Photoshop.

Here’s how you can think about object origins in relation to scaling objects in Sketch or Photoshop.

Getting some motion on

Let’s set the position of where we want card1 to move when it is interacted with. We’ll add a state to card1 and a click event handler right away, and then talk about what they’re doing specifically.

Note: CoffeeScript forgoes the curly brackets and semicolons used by JavaScript for whitespace, so indention is crucial or your code will not work. Be sure to be consistent in your indention length, whether using spaces, or a tab. (If you are copying from Medium, you’ll get four spaces, which equals the width of a tab in the Framer IDE).

# Add a state to card1
$.card1.states =
two: x: -510, y: 300, scale: 0.85, originX: 1, originY: 0
# Add a click event listener
$.card1.onClick ->
$.card1.animate('two')
Bye card. We’ll make you come back in a few more steps.

First, we added a new state to our card1 layer. I called the state two, but you can call it whatever you want. The state two, when active, applies new x and y coordinates to card1. It also scales the card, just like how card2 is scaled on its initial state.

Second, we added an onClick event that listens for card1 to be clicked, so it can then do anything we’ve designated. In this case, we instruct card1 to animate to state two, but only onClick.

Now we can click card1 to apply the state two. Oooh motion! Framer automatically applied default animation speed and timing and also tweened between the properties. Let’s override the default animation with a custom one. Hit cmd + R to refresh the prototype if you haven’t already.

Add this animation option right below where you declared the state for card1:

$.card1.states.animationOptions = curve: 'spring(200, 20, 0)'

Framer has a lot of animation timing options, including bezier-curves, which are more common with CSS animations. You can explore the animation documentation, and tweak the type and timing attributes to your liking.

Next, let’s add a second state for card2, just like we did for card1. Then, add an onClick event for card2 that returns the cards to their default state. Default is a state created for us by Framer that refers to the layer’s original properties before any other states were applied. We specify default as the state to animate for the onClick event of card2 in order to return the cards to their original coordinates.

$.card2.states =
two: scale: 1, x: 94, y: 268, originX: 0, originY: 0
$.card2.states.animationOptions = curve: 'spring(200, 20, 0)'
$.card1.onClick ->
$.card1.animate('two')
$.card2.animate('two')
# Return cards to their default state when card2 is clicked
$.card2.onClick ->
$.card1.animate('default')
$.card2.animate('default')

Wahoo. We should now be able to click both cards to have them swap positions.

At this point, your code should be looking something like this. Order may or may not effect your outcome, but organization is useful as your project becomes more complex. Following a pattern of defining Layer Properties, States, and then Events in order will generally prevent any errors from occurring.

Make it Draggable

If you haven’t already, download the Framer App to try the prototype on your phone. Clicking isn’t necessarily what you’d expect as the user input here, especially on a phone, so let’s make our cards draggable instead.

Enable dragging on card1 and card2:

$.card1.draggable.enabled = true
$.card2.draggable.enabled = true

Uh oh! If you swipe hard enough and don’t register a “click” our cards will fling off the screen. We’ll fix this in the next few steps.

We really only want cards to be dragged horizontally here, so let’s disable the ability for a user to drag them vertically. While were at it, let’s change our event handlers from listening for a Click to a drag. Specifically, when a user stops dragging the object with onDragEnd.

$.card1.draggable.vertical = false
$.card2.draggable.vertical = false
# Change our event handlers from 'Click' to 'DragEnd'
$.card1.onDragEnd ->
$.card1.animate('two')
$.card2.animate('two')
$.card2.onDragEnd ->
$.card1.animate('default')
$.card2.animate('default')

UPDATED: We should also disable the momentum on our draggable cards. This is an option enabled by default for draggable layers. However, when animating with states it can cause some unintended glitchiness, so it should be set to false.

$.card1.draggable.momentum = false
$.card2.draggable.momentum = false

Okay, we made the cards draggable and changed our events to listen for when a user stops dragging, but right now the cards swap even if you only drag a tiny bit. Let’s use an if statement to check how far a user drags. We only want to move to the next state if it looks like they made conscious effort to swipe.

Let’s look at the logic we want to write in pseudo-code:

When the user finishes dragging
Check if card1 is less than 15 px from the edge of the device
If so, switch to state two for card1 and card2
Otherwise (else)
Leave the cards in their default position

And below is what it looks implemented. Add these lines and indentation to the card1.onDragEnd event.

$.card1.onDragEnd ->
if $.card1.x < 15
$.card1.animate('two')
$.card2.animate('two')
else
$.card1.animate('default')
$.card2.animate('default')

With this code implemented, state two no longer activates if the user swipes to the right. If you only slightly swipe card1 to the left, it will also not trigger. You can adjust the 15px to dial in the level of tolerance you like.

Now implement a similar setup on card2:

$.card2.onDragEnd ->
if $.card2.x > 160
$.card1.animate('default')
$.card2.animate('default')
else
$.card1.animate('two')
$.card2.animate('two')

This time we look for when the left edge of card 2 is greater than 160px from the left edge of the screen.

Finishing touches

Currently, card2 goes under our card1 when dragged.

We have some z-indexing problems going on. First, use the z property to set the z index of our cards to 1 near the top of our file. This will ensure the cards are set to a z-index of 1 when they are in their default states:

$.card1.z = 1
$.card2.z = 1

Next, add z: 2 t0 the end of two states, for both card1 and card2 so they will overlay each other when active.

two: scale: 1, x: 94, y: 268, originX: 0, originY: 0, z: 2

Last but not least, let’s add the active state on the navigation. Since we didn’t make an active layer in Sketch, we’ll make it in Framer. Create a new layer called activeDot and give it some initial properties:

activeDot = new Layer
width: 20
height: 20
x: 314
y: 1248
borderRadius: 20
backgroundColor: '#6563A4'

Add a state t0 activeDot and set animation timing:

activeDot.states =
two: x: 367
activeDot.states.animationOptions = curve: 'spring(400, 20, 0)'

Then we just need to add the activeDot states to our events.

$.card1.onDragEnd ->
if $.card1.x < 15
$.card1.animate('two')
$.card2.animate('two')
activeDot.animate('two')
else
$.card1.animate('default')
$.card2.animate('default')
$.card2.onDragEnd ->
if $.card2.x > 160
$.card1.animate('default')
$.card2.animate('default')
activeDot.animate('default')
else
$.card1.animate('two')
$.card2.animate('two')

Voila

If you’re having trouble or want to compare, you can download the prototype with everything we’ve covered so far by clicking ‘open’ or the download button here: http://share.framerjs.com/gfsk63r3drg7/

Adding more cards and refactoring

I won’t go through the details of adding the third card and doing a bit of refactoring of our code in this tutorial, as it’s a lot of repetition. For now, you can view an example with the third card added or feel free to ask me if you have questions.

Was it weird and hard?

What do you think of using Framer as a prototyping tool? Do you have another prototyping tool that you prefer? Let me know if you think any aspects of this walkthrough or example can be improved — I’m still learning too!

--

--