Docs
Music Stack Interaction
widget for Music stack and unstacking
Installation
Install dependencies
npm install lucide-react framer-motion
Run the following command
It will create a new file music-stack-interaction.tsx
inside the components/animata/widget
directory.
mkdir -p components/animata/widget && touch components/animata/widget/music-stack-interaction.tsx
Paste the code
Open the newly created file and paste the following code:
import React, { useState } from "react";
import { motion } from "framer-motion";
import { Layers, LayoutGrid } from "lucide-react";
import { cn } from "@/lib/utils";
const carouselStyles = {
perspective: "1000px",
overflow: "hidden",
};
const carouselInnerStyles: React.CSSProperties = {
display: "flex",
transformStyle: "preserve-3d",
transition: "transform 2s",
justifyContent: "center",
alignItems: "center",
height: "100%",
};
const carouselItemStyles: React.CSSProperties = {
minWidth: "200px",
marginLeft: "-180px",
transform: "rotateY(15deg) translateZ(300px) translateX(-50px)",
backfaceVisibility: "hidden",
boxShadow: "0 4px 8px rgba(0, 0, 0, 0.2)",
transition: "transform 2s, box-shadow 2s",
};
const carouselItemFirstChildStyles: React.CSSProperties = {
minWidth: "200px",
marginLeft: "20px",
transform: "rotateY(5deg) translateZ(300px) translateX(0)",
backfaceVisibility: "hidden",
boxShadow: "0 4px 8px rgba(0, 0, 0, 0.2)",
transition: "transform 2s, box-shadow 2s",
};
interface albumsProps {
/*
* Array of album objects
*/
albums: {
id: number;
title: string;
artist: string;
cover: string;
}[];
}
export default function MusicStackInteraction({ albums }: albumsProps) {
const [isGridView, setIsGridView] = useState(true);
const handleToggleView = () => {
setIsGridView(!isGridView);
};
return (
<div className="relative mx-auto h-[33rem] w-80 rounded bg-gray-900 p-2 text-white">
<motion.div
className={cn("h-96 w-full", { "mt-24": !isGridView })}
style={isGridView ? undefined : carouselStyles}
layout
>
<motion.div
className={cn("", { "mb-4 grid grid-cols-2 gap-6": isGridView })}
style={isGridView ? undefined : carouselInnerStyles}
layout
>
{albums.map((album, index) => (
<div
key={album.id}
className={cn("relative w-full shadow-lg")}
style={
isGridView
? undefined
: index === 0
? carouselItemFirstChildStyles
: carouselItemStyles
}
>
<motion.img
layout
src={album.cover + "?w=200&h=200"}
alt={album.title}
className="h-auto rounded-xl shadow-md"
/>
<motion.div
layout
className="absolute bottom-0 left-0 w-full bg-opacity-50 bg-gradient-to-b from-transparent to-gray-800 px-4 py-2 text-white"
>
<motion.h3 layout className="font-semibold leading-tight">
{album.title}
</motion.h3>
<motion.p layout className="text-sm leading-snug">
{album.artist}
</motion.p>
</motion.div>
</div>
))}
</motion.div>
</motion.div>
<motion.div className="duration-2000 absolute bottom-4 left-0 right-0 -mb-4 flex w-auto items-center justify-center rounded-xl bg-gray-800 p-4 text-white shadow-2xl transition-all">
<div className="flex w-32 items-center space-x-2 rounded-full bg-gray-900 p-2">
<div
className={cn("flex h-8 w-16 cursor-pointer items-center justify-center rounded-full", {
"bg-gray-700": isGridView,
})}
onClick={handleToggleView}
>
<LayoutGrid />
</div>
<div
className={cn("flex h-8 w-16 cursor-pointer items-center justify-center rounded-full", {
"bg-gray-700": !isGridView,
})}
onClick={handleToggleView}
>
<Layers />
</div>
</div>
</motion.div>
</div>
);
}
Credits
Built by Mohit Ahlawat