Skip to main content
Glama

Motion.dev MCP Server

patterns.ts18.3 kB
/** * Motion.dev Animation Patterns and Templates * Common animation patterns that can be applied across frameworks */ import { Framework } from '../../types/motion.js'; export interface AnimationPattern { id: string; name: string; description: string; category: 'entrance' | 'exit' | 'gesture' | 'layout' | 'scroll' | 'stagger' | 'complex'; complexity: 'basic' | 'intermediate' | 'advanced'; frameworks: Framework[]; tags: string[]; config: { initial?: Record<string, any>; animate?: Record<string, any>; exit?: Record<string, any>; transition?: Record<string, any>; variants?: Record<string, Record<string, any>>; gestures?: Record<string, Record<string, any>>; }; usage: { props: string[]; dependencies: string[]; notes?: string[]; }; } export class AnimationPatterns { private patterns: Map<string, AnimationPattern> = new Map(); constructor() { this.initializePatterns(); } private initializePatterns(): void { // Entrance Animations this.addPattern({ id: 'fade-in', name: 'Fade In', description: 'Smooth opacity transition from invisible to visible', category: 'entrance', complexity: 'basic', frameworks: ['react', 'js', 'vue'], tags: ['fade', 'opacity', 'entrance'], config: { initial: { opacity: 0 }, animate: { opacity: 1 }, transition: { duration: 0.3 } }, usage: { props: ['initial', 'animate', 'transition'], dependencies: [] } }); this.addPattern({ id: 'slide-up', name: 'Slide Up', description: 'Element slides up from below while fading in', category: 'entrance', complexity: 'basic', frameworks: ['react', 'js', 'vue'], tags: ['slide', 'translate', 'entrance'], config: { initial: { opacity: 0, y: 20 }, animate: { opacity: 1, y: 0 }, transition: { duration: 0.4, ease: 'easeOut' } }, usage: { props: ['initial', 'animate', 'transition'], dependencies: [] } }); this.addPattern({ id: 'scale-bounce', name: 'Scale Bounce', description: 'Element scales in with a bouncy spring animation', category: 'entrance', complexity: 'intermediate', frameworks: ['react', 'js', 'vue'], tags: ['scale', 'spring', 'bounce'], config: { initial: { scale: 0, opacity: 0 }, animate: { scale: 1, opacity: 1 }, transition: { type: 'spring', stiffness: 260, damping: 20 } }, usage: { props: ['initial', 'animate', 'transition'], dependencies: [] } }); this.addPattern({ id: 'scale', name: 'Scale', description: 'Simple scale animation for elements', category: 'entrance', complexity: 'basic', frameworks: ['react', 'js', 'vue'], tags: ['scale', 'transform', 'basic'], config: { initial: { scale: 0.8 }, animate: { scale: 1 }, transition: { duration: 0.3, ease: 'easeOut' } }, usage: { props: ['initial', 'animate', 'transition'], dependencies: [] } }); this.addPattern({ id: 'rotate', name: 'Rotate', description: 'Simple rotation animation for elements', category: 'entrance', complexity: 'basic', frameworks: ['react', 'js', 'vue'], tags: ['rotate', 'transform', 'basic'], config: { initial: { rotate: -10 }, animate: { rotate: 0 }, transition: { duration: 0.4, ease: 'easeOut' } }, usage: { props: ['initial', 'animate', 'transition'], dependencies: [] } }); // Exit Animations this.addPattern({ id: 'fade-out', name: 'Fade Out', description: 'Smooth opacity transition from visible to invisible', category: 'exit', complexity: 'basic', frameworks: ['react', 'js', 'vue'], tags: ['fade', 'opacity', 'exit'], config: { exit: { opacity: 0 }, transition: { duration: 0.2 } }, usage: { props: ['exit', 'transition'], dependencies: ['AnimatePresence'] } }); this.addPattern({ id: 'slide-down-exit', name: 'Slide Down Exit', description: 'Element slides down while fading out', category: 'exit', complexity: 'basic', frameworks: ['react', 'js', 'vue'], tags: ['slide', 'translate', 'exit'], config: { exit: { opacity: 0, y: 20 }, transition: { duration: 0.3, ease: 'easeIn' } }, usage: { props: ['exit', 'transition'], dependencies: ['AnimatePresence'] } }); // Gesture Animations this.addPattern({ id: 'hover-scale', name: 'Hover Scale', description: 'Scale element on hover with smooth transition', category: 'gesture', complexity: 'basic', frameworks: ['react', 'js', 'vue'], tags: ['hover', 'scale', 'interaction'], config: { gestures: { whileHover: { scale: 1.05 }, whileTap: { scale: 0.95 } }, transition: { type: 'spring', stiffness: 400, damping: 17 } }, usage: { props: ['whileHover', 'whileTap', 'transition'], dependencies: [] } }); this.addPattern({ id: 'drag-elastic', name: 'Elastic Drag', description: 'Draggable element with elastic constraints', category: 'gesture', complexity: 'advanced', frameworks: ['react', 'js', 'vue'], tags: ['drag', 'elastic', 'constraints'], config: { gestures: { drag: { enabled: true, constraints: { left: -100, right: 100, top: -100, bottom: 100 }, elastic: 0.2 } }, transition: { type: 'spring', stiffness: 300, damping: 30 } }, usage: { props: ['drag', 'dragConstraints', 'dragElastic', 'transition'], dependencies: [] } }); // Layout Animations this.addPattern({ id: 'shared-layout', name: 'Shared Layout', description: 'Smooth layout transitions between different states', category: 'layout', complexity: 'advanced', frameworks: ['react'], tags: ['layout', 'transition', 'morph'], config: { variants: { collapsed: { width: 200, height: 100 }, expanded: { width: 400, height: 300 } }, transition: { layout: { duration: 0.3 } } }, usage: { props: ['layout', 'layoutId', 'variants', 'transition'], dependencies: [], notes: ['Requires layoutId for shared layout animations'] } }); // Scroll Animations this.addPattern({ id: 'scroll-reveal', name: 'Scroll Reveal', description: 'Element animates in when it enters the viewport', category: 'scroll', complexity: 'intermediate', frameworks: ['react', 'js', 'vue'], tags: ['scroll', 'viewport', 'reveal'], config: { initial: { opacity: 0, y: 50 }, gestures: { whileInView: { opacity: 1, y: 0 }, viewport: { once: true, margin: '-100px' } }, transition: { duration: 0.6, ease: 'easeOut' } }, usage: { props: ['initial', 'whileInView', 'viewport', 'transition'], dependencies: [] } }); this.addPattern({ id: 'parallax-scroll', name: 'Parallax Scroll', description: 'Element moves at different speed than scroll for depth effect', category: 'scroll', complexity: 'advanced', frameworks: ['react', 'js'], tags: ['parallax', 'scroll', 'depth'], config: { variants: { offscreen: { y: 100 }, onscreen: { y: -100, transition: { type: 'spring', bounce: 0.4 } } } }, usage: { props: ['variants', 'initial', 'whileInView'], dependencies: ['useScroll', 'useTransform'], notes: ['Requires scroll progress tracking'] } }); // Stagger Animations this.addPattern({ id: 'stagger-children', name: 'Stagger Children', description: 'Children elements animate in sequence with delay', category: 'stagger', complexity: 'intermediate', frameworks: ['react', 'js', 'vue'], tags: ['stagger', 'children', 'sequence'], config: { variants: { hidden: { opacity: 0 }, visible: { opacity: 1, transition: { delayChildren: 0.1, staggerChildren: 0.1 } } } }, usage: { props: ['variants', 'initial', 'animate'], dependencies: [], notes: ['Parent container needs staggerChildren, children need individual variants'] } }); // Complex Animations this.addPattern({ id: 'morphing-button', name: 'Morphing Button', description: 'Button that morphs between different states', category: 'complex', complexity: 'advanced', frameworks: ['react'], tags: ['button', 'morph', 'state', 'loading'], config: { variants: { idle: { width: 120, borderRadius: 8, backgroundColor: '#3B82F6' }, loading: { width: 40, borderRadius: 20, backgroundColor: '#10B981' }, success: { width: 140, borderRadius: 8, backgroundColor: '#059669' } }, transition: { type: 'spring', stiffness: 300, damping: 30 } }, usage: { props: ['variants', 'animate', 'transition', 'layout'], dependencies: [], notes: ['Requires state management for different button states'] } }); this.addPattern({ id: 'card-flip', name: 'Card Flip', description: '3D card flip animation with front and back sides', category: 'complex', complexity: 'advanced', frameworks: ['react', 'js'], tags: ['3d', 'flip', 'card', 'perspective'], config: { variants: { front: { rotateY: 0 }, back: { rotateY: 180 } }, transition: { duration: 0.6, ease: 'easeInOut' }, initial: { transformPerspective: 1000 } }, usage: { props: ['variants', 'animate', 'transition', 'style'], dependencies: [], notes: ['Requires CSS perspective and backface-visibility settings'] } }); } private addPattern(pattern: AnimationPattern): void { this.patterns.set(pattern.id, pattern); } // Public API methods getPattern(id: string): AnimationPattern | undefined { return this.patterns.get(id); } getAllPatterns(): AnimationPattern[] { return Array.from(this.patterns.values()); } getPatternsByCategory(category: AnimationPattern['category']): AnimationPattern[] { return this.getAllPatterns().filter(pattern => pattern.category === category); } getPatternsByFramework(framework: Framework): AnimationPattern[] { return this.getAllPatterns().filter(pattern => pattern.frameworks.includes(framework) ); } getPatternsByComplexity(complexity: AnimationPattern['complexity']): AnimationPattern[] { return this.getAllPatterns().filter(pattern => pattern.complexity === complexity); } searchPatterns(query: string): AnimationPattern[] { const searchTerm = query.toLowerCase(); return this.getAllPatterns().filter(pattern => pattern.name.toLowerCase().includes(searchTerm) || pattern.description.toLowerCase().includes(searchTerm) || pattern.tags.some(tag => tag.toLowerCase().includes(searchTerm)) ); } getPatternCode( patternId: string, framework: Framework, componentName: string = 'AnimatedComponent' ): string { const pattern = this.getPattern(patternId); if (!pattern) { throw new Error(`Pattern '${patternId}' not found`); } if (!pattern.frameworks.includes(framework)) { throw new Error(`Pattern '${patternId}' not supported for ${framework}`); } switch (framework) { case 'react': return this.generateReactCode(pattern, componentName); case 'vue': return this.generateVueCode(pattern, componentName); case 'js': return this.generateJavaScriptCode(pattern, componentName); default: throw new Error(`Framework '${framework}' not supported`); } } private generateReactCode(pattern: AnimationPattern, componentName: string): string { const imports = this.getRequiredImports(pattern, 'react'); const props = this.generateReactProps(pattern); return `import React from 'react'; ${imports} const ${componentName}: React.FC = () => { return ( <motion.div ${props} > {/* Your content here */} </motion.div> ); }; export default ${componentName};`; } private generateVueCode(pattern: AnimationPattern, _componentName: string): string { const directives = this.generateVueDirectives(pattern); return `<template> <div ${directives}> <!-- Your content here --> </div> </template> <script setup lang="ts"> import { ref } from 'vue'; import { MotionPlugin } from '@vueuse/motion'; // Component logic here </script>`; } private generateJavaScriptCode(pattern: AnimationPattern, functionName: string): string { const animationCode = this.generateJavaScriptAnimation(pattern); return `import { animate } from 'motion'; function ${functionName}() { const element = document.querySelector('.animated-element'); if (element) { ${animationCode} } } // Call the function ${functionName}();`; } private getRequiredImports(pattern: AnimationPattern, _framework: Framework): string { const imports = new Set(['motion']); if (pattern.usage.dependencies.includes('AnimatePresence')) { imports.add('AnimatePresence'); } if (pattern.usage.dependencies.includes('useScroll')) { imports.add('useScroll'); } if (pattern.usage.dependencies.includes('useTransform')) { imports.add('useTransform'); } return `import { ${Array.from(imports).join(', ')} } from 'framer-motion';`; } private generateReactProps(pattern: AnimationPattern): string { const props: string[] = []; if (pattern.config.initial) { props.push(` initial={${JSON.stringify(pattern.config.initial, null, 6)}}`); } if (pattern.config.animate) { props.push(` animate={${JSON.stringify(pattern.config.animate, null, 6)}}`); } if (pattern.config.exit) { props.push(` exit={${JSON.stringify(pattern.config.exit, null, 6)}}`); } if (pattern.config.variants) { props.push(` variants={${JSON.stringify(pattern.config.variants, null, 6)}}`); } if (pattern.config.transition) { props.push(` transition={${JSON.stringify(pattern.config.transition, null, 6)}}`); } if (pattern.config.gestures) { Object.entries(pattern.config.gestures).forEach(([key, value]) => { props.push(` ${key}={${JSON.stringify(value, null, 6)}}`); }); } return props.join('\n'); } private generateVueDirectives(pattern: AnimationPattern): string { const directives: string[] = []; if (pattern.config.initial) { directives.push(`v-motion-initial="${JSON.stringify(pattern.config.initial)}"`); } if (pattern.config.animate) { directives.push(`v-motion-enter="${JSON.stringify(pattern.config.animate)}"`); } return directives.join(' '); } private generateJavaScriptAnimation(pattern: AnimationPattern): string { if (pattern.config.animate) { return ` animate(element, ${JSON.stringify(pattern.config.animate, null, 4)}, { duration: ${pattern.config.transition?.duration || 0.3} });`; } return ' // Animation code here'; } // Pattern analysis and suggestions getSimilarPatterns(patternId: string, limit: number = 3): AnimationPattern[] { const pattern = this.getPattern(patternId); if (!pattern) return []; return this.getAllPatterns() .filter(p => p.id !== patternId) .filter(p => p.category === pattern.category || p.tags.some(tag => pattern.tags.includes(tag)) ) .slice(0, limit); } getPatternCombinations(_patternIds: string[]): { compatible: boolean; conflicts: string[]; suggestions: string[]; } { // const patterns = patternIds.map(id => this.getPattern(id)).filter(Boolean) as AnimationPattern[]; return { compatible: true, conflicts: [], suggestions: [] }; } getPatternPerformanceScore(patternId: string): { score: number; factors: { transforms: number; layout: number; complexity: number; }; recommendations: string[]; } { const pattern = this.getPattern(patternId); if (!pattern) { return { score: 0, factors: { transforms: 0, layout: 0, complexity: 0 }, recommendations: [] }; } // Simplified performance scoring let score = 100; const factors = { transforms: 0, layout: 0, complexity: 0 }; const recommendations: string[] = []; // Check for transform properties (good for performance) const config = JSON.stringify(pattern.config); if (config.includes('"x":') || config.includes('"y":') || config.includes('"scale":')) { factors.transforms = 20; } // Check for layout properties (bad for performance) if (config.includes('"width":') || config.includes('"height":')) { factors.layout = -30; score -= 30; recommendations.push('Consider using transform properties instead of width/height'); } // Complexity penalty if (pattern.complexity === 'advanced') { factors.complexity = -10; score -= 10; } return { score: Math.max(0, Math.min(100, score)), factors, recommendations }; } }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Abhishekrajpurohit/motion-dev-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server