Building a Swiper with Animated Background Color in React Native

This will be our render method 😉

Recently while I was browsing r/DnD I found this awesome thread that provided the Dungeon & Dragons class icons as SVGs (you can find them here). I thought this would be a cool opportunity to build something!

The result of this journey

For those who a curious on how to record a video with the iOS simulator:

xcrun simctl io booted recordVideo output.mov

The demo was built using the react-native-cli, but I am going to use expo for sharing the building process with snack embeds.


First of all we have to convert all those SVGs to React Components. I love building applications with SVG content and there are two tools I would highly recommend to any developer out there:

  • SVGOMG: Optimize SVG code (merging paths, remove unnecessary attributes and comments)
  • SVGR: Convert SVG code to React/React Native components

Unfortunately SVGR does not allow generating code for the Expo SDK, thats why we have to put in some extra search and replace work. 😅

After we converted all those SVGs into beautiful React Native components we dump them all into a file and make sure we also export them.

The code for setting up the slider is straight forward and you can find the work in progress below. 👇

D&D Class Icon Slider WIP (no background color yet!)

Next up we have to do some animation shinanigans! I am not good at picking colors, but I heared it is very hard to get “bad” colors by using hsla instead of rgba , so I searched for a cool function that generates those colors.

Thank you Mika for this awesome function (check out his blog post):

function generateHslaColors (saturation, lightness, alpha, amount) {
const colors = [];
const hueDelta = Math.trunc(360 / amount);

for (let i = 0; i < amount; i++) {
let hue = i * hueDelta
colors.push(
`hsla(${hue}, ${saturation}%, ${lightness}%, ${alpha})`
);
}

return colors;
}

Those who already used the the Animated API know that Animated.Value can only be used to animate numbers. Since we want animate colors this is a bit of a problem.

Thankfully we can interpolate our numeric value. That means that we can map a numeric value to a color.

// generate an array that includes the numbers 0 - n
const inputRange = Object.entries(ClassIcons).map((_, i) => i);
console.log(inputRange);
// [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
// generate colors that map to our inputRange
const outputRange = generateHslaColors(50, 50, 1.0, inputRange.length);
inputRange.length === outputRange.length; // true
const animatedColorValue = new Animated.Value(0);
const backgroundColor = animatedColorValue.interpolate({      
inputRange,
outputRange
});

The index of the slide items do represent the indices of the slides. That means we have to animate our value when the index of the active slide item changes.

The Swiper component has a onIndexChanged callback property, which suits our needs! 🎉 Just some basic Animated.timing on the animated value and we are good. 👇

WARNING: Do not try this demo if you have epilepsy

D&D Class Icon Slider WIP (With animated background color)

WE HAVE FINISHED! 💯

No, as the warning indicated there is still an issue! Try swiping from the last item to the first item or vice versa. Do you see the party that is going on?

Because the value is animated from 0 to n (and vice versa) all the inputRanges between those values will be covered. Which basically means all outputRanges colors will show up during this animation.

I actually had to think a few minutes about how to tackle this issue. Thanks to IDE completion I have seen the method setValue on an Animated.Value instance before. I assumed that with this method I can set the new value of an animated value without enforcing an animation.

However we still want the animation between the first and last slide.

I came up with the idea that if the first value in outputRanges would be the same as the last value in outputRanges this might still be possible.

// append last item
outputRange.unshift(outputRange[outputRange.length - 1]);
// inputRanges needs the same length as 
inputRange.unshift(-1);

Now, if know that the index we are on is e.g. 12 and onIndexChanged is called with 0 we can set the value of our animated value to -1 before animating. We actually have to do the same if we want to animate from 0 to 12.

What the heck ?! 🤯

Lets try to visualize what array structure we want to build

// outputRanges
["red", "yellow", "blue", "purple", "red", "yellow"]
// inputRanges
[-1, 0, 1, 2, 3, 4]

In this example the color for our first slide would be "yellow" and the color for the last one would be "red" .

Of we animate from the first slide (inputRange = 0) to the last slide (inputRange = 3) a.k.a "yellow" to "red", we first set the animated value to 4 and animate it to 3 afterwards.

Actually pretty simple huh 😅

Now we can finally resolve this issue 👇

D&D Class Icon Slider (The Final Version)

Thank you for reading this story 😊

This is my first blog post on coding so I would love to hear your feedback. Should I do more small on hands tutorials like this? Would you love some more complex topics covered?

I appreciate every comment, clap and share on twitter!