Framer practice: Looping Carousel 1
Review and practice your Framer prototyping skills with this pageComponent step-by-step.
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:
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.
- 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:
- Fonts are packaged correctly (don’t forget to add the CSS file to your prototype’s folder!);
- That the hints (or hotspots) won’t show up when using the prototype, and;
- 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 madescroll
’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.
- 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 oncard1
’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:
- If the carousel’s current page is called
cEnd
, ourpage
should snap to the realcard1
. - If the carousel’s current page is called
cStart
, ourpage
should snap to the realcard6
.
- These two conditions should run at the end of
page
’s tradition between cards. So we can use thepage.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
orcStart
, 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
✌️🌞