Skip to content

How It Works

The router plugin operates by integrating with Vue Router and using middleware to control when flip animations should occur based on route conditions.

Technical Implementation

1. Route Integration

The plugin uses Vue Router's useRouter composable and the beforeResolve hook to track route changes:

typescript
const router = useRouter()
const nextRoute = shallowRef<RouteLocationNormalizedGeneric>()
const prevRoute = shallowRef<RouteLocationNormalizedGeneric>()

router.beforeResolve((to, from) => {
  nextRoute.value = to
  prevRoute.value = from
})

This provides access to both the current and previous route states, allowing the plugin to make decisions based on route transitions.

2. Route Evaluation Function

The core logic is in the needFlip function that determines whether an animation should occur:

typescript
function needFlip (config: FlipElementConfig) {
  // no route configured, continue
  if (!config.routes) return true
  // no route to flip, stop execution
  if (!nextRoute.value) return false
  const configMatch = config.routes.find(route => isAllowedRoute(route, nextRoute.value!))
  // routes are configured but no route match, stop execution
  if (!configMatch) return false

  if (configMatch.from) {
    const prevRouteMatch = configMatch.from.find(route => isAllowedRoute(route, prevRoute.value!))
    if (!prevRouteMatch) return false
  }
  return true
}

This function:

  • Returns true if no routes are configured (default behavior)
  • Returns false if no route is available to flip to
  • Finds a matching route configuration for the current route
  • Validates the "from" route if specified
  • Returns true only when all conditions are met

3. Route Permission Checking

The isAllowedRoute function handles complex route matching:

typescript
function isAllowedRoute (permission: RouteConfig, route: RouteLocationNormalizedGeneric) {
  const isSameRouteName = permission.name instanceof RegExp
    ? permission.name.test(route.name as string)
    : permission.name === route.name

  if (!isSameRouteName) return false
  return !permission.params || (route.params && Object.entries(permission.params).every(([key, configParam]) => {
    const routeParam = route.params[key]
    if (Array.isArray(routeParam) && Array.isArray(configParam)) {
      return configParam.every(param => routeParam.includes(param))
    } else if (Array.isArray(routeParam)) {
      return routeParam.includes(configParam as string)
    } else if (Array.isArray(configParam)) {
      // configuration define an array of params, but the route doesn't have an array
      return false
    } else {
      return routeParam === configParam
    }
  }))
}

This function supports:

  • String and RegExp route names: Route names can be exact strings or RegExp patterns
  • Complex parameter matching: Parameters can be strings or arrays with sophisticated matching logic
  • Array parameter support: Route parameters can be arrays for flexible matching

4. Middleware Integration

The plugin registers a single middleware function that intercepts detach animation requests:

typescript
function detachMiddleware (_id: string, _el: Element, config: FlipElementConfig) {
  return needFlip(config)
}

flipManager.addDetachMiddleware(detachMiddleware)

This middleware:

  • Intercepts detach animation requests
  • Uses the needFlip function to determine if animation should proceed
  • Returns true to allow animation when conditions are satisfied
  • Returns false to prevent animation when conditions aren't met

5. Route Change Watching

The plugin watches for route changes and cleans up orphaned elements:

typescript
const stopWatcher = watch(() => router.currentRoute.value, () => {
  for (const [id, data] of flipManager.store.entries()) {
    if (needFlip(data.config)) continue
    if (data.clone) document.body.removeChild(data.clone)
    flipManager.store.delete(id)
  }
})

This ensures that:

  • Elements are cleaned up when routes change
  • Orphaned clones are removed from the DOM
  • The flip manager store is kept clean

Configuration Logic

The plugin adds a routes property to the FlipElementConfig:

typescript
interface RouteConfig {
  name: string | RegExp
  params: RouteLocationNormalizedGeneric['params']
}

interface RouterFlipElementConfig {
  routes?: (RouteConfig & { from?: RouteConfig[] })[]
}

Route Configuration

PropertyTypeDescription
routes(RouteConfig & { from?: RouteConfig[] })[]Array of route configurations
namestring | RegExpRoute name (exact string or RegExp pattern)
paramsRouteLocationNormalizedGeneric['params']Route parameters for matching
fromRouteConfig[]Array of routes that can animate to this route

Animation Logic

The animation logic works as follows:

  1. No Routes Configured: Animation always occurs (default behavior)
  2. Route Name Match: Animation occurs if current route name matches a configured route (string or RegExp)
  3. Parameter Matching: Animation occurs if route parameters match the configured parameters
  4. From Routes: Animation occurs if navigating from a route in the from array
  5. Complex Matching: Supports array parameters and RegExp patterns for flexible matching

Type Augmentation

The plugin automatically extends TypeScript types through module augmentation:

typescript
declare module '@vue-gsap-flip/core' {
  interface FlipElementConfigAugmentation extends RouterFlipElementConfig {}
}

This ensures that the routes property is available in TypeScript when using the plugin.

Cleanup

The plugin returns a cleanup function when installed:

typescript
return () => {
  flipManager.addDetachMiddleware(detachMiddleware)
  stopWatcher()
}

This allows the plugin to be properly uninstalled, removing middleware functions and stopping route watchers.

Performance Considerations

  • Route evaluation is lightweight and only occurs during animation requests
  • Route watching uses Vue's reactive system for efficient updates
  • Cleanup is automatic and prevents memory leaks
  • Middleware functions are simple boolean checks with minimal performance impact
  • RegExp patterns are compiled once and reused for efficient matching

Released under the MIT License.