Framer practice: Looping Carousel 1

Review and practice your Framer prototyping skills with this pageComponent step-by-step.

Sophie Rahier
6 min readJul 12, 2018

This is part 1 of 2, and covers basics of the pageComponent as well as the carousel effect.

Let’s have another round of Framer practice and make a looping (or circular?) carousel! I’ll cover desktop here, however a mobile prototype would work essentially the same way.

To start off with, here is what we’re going for:

<- A carousel where navigation works both ways ->

View on desktop or open in Framer

So here’s the design-only file if you’re tagging along for the ride!

If you already have or know how to set-up your desktop prototype, a scroll, and page component, go ahead and skip to the Creating the loop section!

Design Mode stuff

Let’s have a quick look a design mode to understand the file’s set-up:

In here, I’ve put a desktop frame, which contains a responsive sticky header and fixed width container. I’ve set the container to clip with a quick right-click on the layer name, so feel free to unclip it if you’d prefer a full-bleed carousel.

  • I’ve also manually added 6 cards, each prepared with padding on either side.
Padding between pages is added manually in design mode
  • Lastly: the carousel’s title. I tend to prefer positioning titles using code rather than design mode, as I find it makes it easier to put things above or below the whole module later.

Desktop settings

I re-use these settings a lot when working on desktop prototypes:

# Desktop specific settings 🖥# Insert fonts from CSS
Utils.insertCSS(‘@import url(css/fonts.css)’)
# Hide Hints
Framer.Extras.Hints.disable()
# Use desktop cursor
document.body.style.cursor = “auto”

These lines make sure that:

  1. Fonts are packaged correctly (don’t forget to add the CSS file to your prototype’s folder!);
  2. That the hints (or hotspots) won’t show up when using the prototype, and;
  3. That the cursor switches back to the default arrow rather than the thumb-hover-like grey circle.

Scroll component

Next, and this is still a bit of set-up, let’s add a scroll component:

# Scroll component 🐁scroll = ScrollComponent.wrap(container)scroll.width = Screen.widthscroll.scrollHorizontal = false
scroll.mouseWheelEnabled = true
scroll.directionLock = true
  • We first create the scroll component, and indicate which layer we want it to affect. I’ve told it to affect (or wrap) container, as that will mean my header stays out of it and will have a sticky behaviour.
  • To make sure users can still scroll even if their mouse happens to be positioned outside the container, I’ve made scroll’s width the width of the Screen.
  • I’ve disabled the horizontal scroll.
  • I’ve enabled scrolling using the mouse.
  • And I’ve ensured that the scroll can only go one direction (horizontal or vertical) at once. This is important because we’ll soon add a horizontally scrolling page component.

The PageComponent

Right! Now we have some solid foundations, let’s start on this carousel! As mentioned, we’ll do this with a page component. Go ahead and use page = new PageComponent to add one in there.

Tip: Once I’ve added the page component, I usually roughly adjust its size and position using the visual editor found in code mode. This saves me writing up the first few lines of code, and I then just have to adjust the values.

The visual code editor gives you a bit of a head start
  • In this case, I’ll adjust the Y position to take the title into consideration (which we’ll add in later), and make the X position more responsive.
  • I also adjust width and height to match those of the cards defined in design mode.
# PageComponent set-up 📄page = new PageComponent
y: title.height + 120
x: Align.center
width: 600
height: 350

Let’s now refine our page component with a few more settings:

# PageComponent set-up 📄page = new PageComponent
y: title.height + 120
x: Align.center
width: 600
height: 350

parent: container
scrollVertical: false
clip: false
# Define card transition animation
page.animationOptions =
curve: “ease”
time: 0.6
  • I make it a child of the container, to make sure it follows the scrolling behaviour.
  • I limit its scrolling direction.
  • I set its clip option to false, so that it doesn’t mask the adjacent cards.
  • Lastly, I add a default transition animation! These are super fun to play around with, I recommend you do!

Creating the loop

In an attempt to avoid any confusion: This section does not yet touch on loops as in the coffeeScript concept, it’s about the carousel’s navigation that loops around!

By using copies of the first and last card and removing some animation settings, we’ll make the transition between the start and the end of the carousel look almost seamless! Let’s get around to it… 🥁

  • We’ll start by creating a copy of the first card, card1, and assigning it a name. Let’s then do the same for the last card, card6:
# Creating the loop 🎠# Creating “fake” cards to give impression of loop
cardEnd = card1.copy()
cardEnd.name = “cEnd”
cardStart = card6.copy()
cardStart.name = “cStart”

Then, let’s add these to our pageComponent, along with all of our real cards. Make sure they’re in order, as it determines the order in which they’ll appear in the carousel.

# Creating the loop 🎠# Creating “fake” cards to give impression of loop
cardEnd = card1.copy()
cardEnd.name = “cEnd”
cardStart = card6.copy()
cardStart.name = “cStart”
# Adding cards to component
page.addPage(cardStart)
page.addPage(card1)
page.addPage(card2)
page.addPage(card3)
page.addPage(card4)
page.addPage(card5)
page.addPage(card6)
page.addPage(cardEnd)

Right now, our carousel should work like any page component, in a linear manner:

Here’s the looping magic:

  • Firstly, set the carousel to start on its second page (because of the copied card, the second card is card1) without animating, by default. This will make it look like the last card is peeking out on card1’s left side.
  • We’re trying to make the carousel jump the original card after it lands on the fake/copied one. As a drawing, that’d look like this:
  • To do this, we need to add conditions:
  1. If the carousel’s current page is called cEnd, our page should snap to the real card1.
  2. If the carousel’s current page is called cStart, our page should snap to the real card6.
  • These two conditions should run at the end of page’s tradition between cards. So we can use the page.content onAnimationEnd -> event to trigger them.
  • Now, every time the carousel is done animating from one page to the next, it will check if the page is cEnd or cStart, and proceed accordingly.

The naming thing is a bit confusing, but it’s necessary so that we can use page.currentPageName is “” which will only recognise an assigned name. Look out for this in the follow-up article!

Here’s what this all looks like in code:

# Creating the loop 🎠# Creating “fake” cards to give impression of loop
cardEnd = card1.copy()
cardEnd.name = “cEnd”
cardStart = card6.copy()
cardStart.name = “cStart”
# Adding cards to component
page.addPage(cardStart)
page.addPage(card1)
page.addPage(card2)
page.addPage(card3)
page.addPage(card4)
page.addPage(card5)
page.addPage(card6)
page.addPage(cardEnd)
# Make PageComponent start on first real card
page.snapToNextPage(“right”, false)
# Jump to real card when landing on fake card
page.content.onAnimationEnd ->
if page.currentPage.name is “cEnd”
page.snapToPage(card1, false)
if page.currentPage.name is “cStart”
page.snapToPage(card6, false)

One last detail

For good measure, let’s also add the module’s title:

# Title set-up 🗣title.midX = container.midX
title.maxY = page.minY — 10
title.parent = container

Don’t forget to set its parent to container to make sure it mimics its scrolling behaviour.

Basics are all done! What’s next?

Awesome, you’ll now have a looping carousel!

In my next article, I cover how to make this when using a loop to generate the cards, rather than making them in design mode.

If you want a few more tips on this, let me know. I’m also always happy to write up an extra example, and if you happen to use or remix this in one of your prototypes, I’d love to see it!

Find me on my very active Twitter: @a_soapy

✌️🌞

--

--

Sophie Rahier

Product designer at Planes, otherwise mostly outdoors. I love cycling, prototyping, and wonky pottery.