banjocode My Custom Svelte Boop Component

My Custom Svelte Boop Component

Creating a Boop component might be a bit tricky with the spring effect. This is my component I tend to re-use whenever I want to use it in a project.

8 min read

Since I first read about the boop, I have always been really fond of it. I try to use it throughout all of my applications (which often appears to be written in Svelte), so I thought I might share my custom boop component here so that I can reuse it without having to publish it to NPM.

I’ll publish the raw code and explain some of the use cases below.

<script lang="ts">
	import { spring } from "svelte/motion";

	interface IBoopParams {
		x?: number;
		y?: number;
		rotation?: number;
		scale?: number;
		opacity?: number;
	}

	const initialState: IBoopParams = {
		x: 0,
		y: 0,
		rotation: 0,
		scale: 1,
		opacity: 1,
	};

	export let boopParams: IBoopParams = initialState;
	$: boopParams = { ...initialState, ...boopParams };

	export let stiffness = 0.1;
	export let damping = 0.15;
	export let shouldBoop = false;
	export let timing = 150;
	export let isHovering: boolean | null = null;

	$: inHoverState = false;

	$: if (isHovering !== null) {
		inHoverState = isHovering;
	}

	let boopSpring = spring(initialState, { stiffness, damping });
	$: boopSpring.set(inHoverState ? boopParams : initialState);
	$: style = `transform:
        translateX(${$boopSpring.x}px)
        translateY(${$boopSpring.y}px)
        rotate(${$boopSpring.rotation}deg)
        translateZ(0)
        scale(${$boopSpring.scale});
        opacity: ${$boopSpring.opacity};`;

	const toggleSpring = () => (inHoverState = !inHoverState);

	$: if (inHoverState && shouldBoop) {
		window.setTimeout(() => {
			toggleSpring();
		}, timing);
	}
</script>

<div
	{style}
	class="flex"
	on:mouseenter={() => {
		if (!isHovering) toggleSpring();
	}}
	on:mouseleave={() => {
		if (!isHovering && !shouldBoop) toggleSpring();
	}}
>
	<slot />
</div>

Usage

So basically, I use this as a custom component. Pass whatever you want to animate as children and the component takes care of the rest. Everything occurs at hover, and for any effect you at least have to pass the boopParams to specify which animations.

<Boop boopParams={{ scale: 1.06 }}>
  <SomeComponent/>
</Boop>

By default, it will not actually “boop”. It will transition (with a spring effect) to the state defined in the boopParams, and transition back when you hover out from the specified component. The effect will basiscally work like an hover effect.

Spring settings

There are some basic spring configuration settings you can pass to the component as well, stiffness and damping. I won’t go into detail what they do here, but it’s easy to read up on here. This is more or less what created the “squigly” effect.

<Boop boopParams={{ scale: 1.06 }} stiffness={0.1} damping={0.15}>
  <SomeComponent/>
</Boop>

Using as a Boop

Usually, you probably want to have the boop effect when using this. Meaning, it will update to the specified state, and after a timeout, transition back to the original state. For this, the shouldBoop param is needed.

<Boop boopParams={{ scale: 1.06 }} shouldBoop={true}>
  <SomeComponent/>
</Boop>

This will basically remove the on/off hover effect, and run the boop animation whenever the element is hovered upon. You can also specify the timeout by passing the timing parameter.

Passing Your Own Hover State

Sometimes, you might want to add the boop on a part of a component. Basically, if you hover over the parent, you only want the child to Boop. This is allowed for by the isHovering parameter. If it is passed, it will only rely on it to trigger the boop.

<div
    class="parent"
    on:mouseenter={() => {isHovering = true}}
    on:mouseleave={() => {isHovering = false}}
  >
    <Boop boopParams={{ scale: 1.06 }} shouldBoop={true} isHovering={isHovering}>
        <SomeComponent/>
    </Boop>
</div>

This is not by any means a complete or highly maintained Svelte component, but it gets the job done and I tend to use it a lot.