Hang on there! We will do a proper launch soon and this is just for testing purposes. 🚀
kinetic-kit
beta

Animated Button

A versatile UI element that provides a flexible and customizable way to organize and display menu items within your application.

Usage

Apple style dock

Portrait 1
Portrait 2
Portrait 3
Portrait 4
Portrait 5
Portrait 6
Portrait 7

Code

'use client';

import { FC, useRef } from 'react';
import Image from 'next/image';
import {
  useScroll,
  useTransform,
  motion,
  UseScrollOptions,
} from 'framer-motion';
import { cn } from '@/lib/utils';

interface Picture {
  src: string;
  alt?: string;
  classes?: string;
  scaleAmount?: number;
}

interface Props {
  pictures: Picture[];
  classes?: string;
  height?: string;
  scaleRange?: {
    min: number;
    max: number;
  };
  scrollConfig?: {
    offset: UseScrollOptions['offset'];
  };
}

const GridSection: FC<Props> = ({
  pictures,
  classes,
  height = '200vh',
  scaleRange = { min: 1, max: 9 },
  scrollConfig = { offset: ['start start', 'end end'] },
}) => {
  const container = useRef(null);
  const { scrollYProgress } = useScroll({
    target: container,
    offset: scrollConfig.offset,
  });

  // Generate scale values dynamically based on the number of pictures
  const scales = pictures.map((_, index) => {
    const scaleAmount = pictures[index].scaleAmount || index + 1;
    const maxScale = Math.min(scaleRange.max, scaleAmount);
    return useTransform(scrollYProgress, [0, 1], [scaleRange.min, maxScale]);
  });

  return (
    <section
      ref={container}
      className={cn('relative w-full', classes)}
      style={{ height }}
    >
      <div className='sticky top-0 h-screen overflow-hidden'>
        {pictures.map(
          ({ src, alt = 'image', classes: pictureClasses }, index) => (
            <motion.div
              key={index}
              style={{ scale: scales[index] }}
              className='absolute top-0 flex h-full w-full items-center justify-center'
            >
              <div className={cn('relative', pictureClasses)}>
                <Image src={src} fill alt={alt} className='object-cover' />
              </div>
            </motion.div>
          )
        )}
      </div>
      <div className='leading-0 h-screen' />
    </section>
  );
};

export { GridSection };

Component API

GridSection

PropTypeDefaultDescription
childrenReactNodeThe content to display in the button.
asChildbooleanfalseReplaces the button tag with the provided child.
iconReactElement<ArrowBigRight size={28} />Colors for the waves that animate on hover.
prependIconbooleanfalseThe visual style of the button.
size'default' | 'large''default'The size of the button, with 'large' being bigger.
refRef<Button or Link Element>Provides a reference to the button or link DOM element, depending on asChild.