cartflow
Version:
A lightweight JavaScript library for creating smooth 'Add to Cart' animations with customizable options, sound effects, and cart shake effects.
1 lines • 10.5 kB
Source Map (JSON)
{"version":3,"sources":["../src/CartFlow.js"],"sourcesContent":["/**\n * Copyright (c) 2025 A.S Nassiry\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n *\n * @see https://github.com/nassiry/cartflow\n */\n(function (global, factory) {\n if (typeof module === 'object' && typeof module.exports === 'object') {\n module.exports = factory();\n /* global define */\n } else if (typeof define === 'function' && define.amd) {\n define(factory);\n } else {\n global.CartFlow = factory();\n }\n})(typeof window !== 'undefined' ? window : this, function () {\n 'use strict';\n\n class CartFlow {\n /**\n * Initialize the CartFlow library\n * @param {Object} options - Configuration options\n */\n constructor(options = {}) {\n this.DEFAULTS = {\n cartSelector: '.shopping-cart',\n buttonSelector: '.add-to-cart',\n itemSelector: '.item',\n imageSelector: 'img',\n animationDuration: 1000,\n easing: 'ease-in-out',\n shakeEffect: true,\n soundEffect: null,\n onComplete: null,\n onCartShake: null,\n };\n\n this.settings = { ...this.DEFAULTS, ...options };\n this.cartElement = document.querySelector(this.settings.cartSelector);\n\n if (!this.cartElement) {\n throw new Error(\n `Cart element not found: ${this.settings.cartSelector}`,\n );\n }\n\n this.animationQueue = [];\n this.isAnimating = false;\n\n this.initEventListeners();\n }\n\n /**\n * Set up event listeners for cart button clicks\n */\n initEventListeners() {\n document.body.addEventListener('click', (event) => {\n const button = event.target.closest(this.settings.buttonSelector);\n if (button) {\n this._handleButtonClick(button);\n }\n });\n }\n\n /**\n * Handle \"Add to Cart\" button click\n * @param {HTMLElement} button - The clicked button\n */\n _handleButtonClick(button) {\n const item = button.closest(this.settings.itemSelector);\n if (!item) {\n return;\n }\n\n const image = item.querySelector(this.settings.imageSelector);\n if (!image) {\n return;\n }\n\n if (this.settings.soundEffect) {\n if (\n typeof this.settings.soundEffect === 'string' ||\n this.settings.soundEffect instanceof HTMLAudioElement\n ) {\n this._playSound(this.settings.soundEffect);\n } else {\n console.warn(\n 'Invalid soundEffect type. Must be a string or an HTMLAudioElement.',\n );\n }\n }\n\n this.animationQueue.push(() => this._animateImageToCart(image, item));\n if (!this.isAnimating) this._processQueue();\n }\n\n /**\n * Play a sound effect\n * @param {string|HTMLAudioElement} soundEffect - Sound file URL or Audio object\n */\n _playSound(soundEffect) {\n try {\n const audio =\n typeof soundEffect === 'string'\n ? new Audio(soundEffect)\n : soundEffect;\n audio\n ?.play()\n .catch((err) => console.error('Failed to play sound:', err));\n } catch (error) {\n console.error('Error playing sound:', error);\n }\n }\n\n /**\n * Process the animation queue\n */\n _processQueue() {\n if (this.animationQueue.length > 0) {\n const nextAnimation = this.animationQueue.shift();\n nextAnimation();\n }\n }\n\n /**\n * Animate the item image moving to the cart\n * @param {HTMLElement} image - The image element to animate\n * @param {HTMLElement} item - The item container element\n */\n _animateImageToCart(image, item) {\n this.isAnimating = true;\n\n const clone = image.cloneNode(true);\n const imageRect = image.getBoundingClientRect();\n const cartRect = this.cartElement.getBoundingClientRect();\n\n const computedStyles = window.getComputedStyle(image);\n Object.assign(clone.style, {\n position: 'absolute',\n top: `${imageRect.top + window.scrollY}px`,\n left: `${imageRect.left + window.scrollX}px`,\n width: `${imageRect.width}px`,\n height: `${imageRect.height}px`,\n border: computedStyles.border,\n boxShadow: computedStyles.boxShadow,\n filter: computedStyles.filter,\n opacity: 0.7,\n zIndex: 1000,\n pointerEvents: 'none',\n });\n\n document.body.appendChild(clone);\n /* global gsap */\n if (typeof gsap !== 'undefined') {\n // Use GSAP for animation if available\n gsap.to(clone, {\n duration: this.settings.animationDuration / 1000,\n x: cartRect.left - imageRect.left + 10,\n y: cartRect.top - imageRect.top + 10,\n width: 50,\n height: 50,\n opacity: 0,\n ease: this.settings.easing,\n onComplete: () => {\n clone.remove();\n this._finalizeAnimation(item);\n },\n });\n } else {\n // Fallback animation\n Object.assign(clone.style, {\n transition: `all ${this.settings.animationDuration}ms ${this.settings.easing}`,\n });\n\n requestAnimationFrame(() => {\n Object.assign(clone.style, {\n top: `${cartRect.top + window.scrollY + 10}px`,\n left: `${cartRect.left + window.scrollX + 10}px`,\n width: '50px',\n height: '50px',\n opacity: 0,\n });\n });\n\n setTimeout(() => {\n clone.remove();\n this._finalizeAnimation(item);\n }, this.settings.animationDuration);\n }\n }\n\n /**\n * Finalize the animation and process the queue\n * @param {HTMLElement} item - The animated item\n */\n _finalizeAnimation(item) {\n this.settings.onComplete?.(item);\n\n if (this.settings.shakeEffect) {\n this._shakeCart();\n }\n\n this.isAnimating = false;\n this._processQueue();\n }\n\n /**\n * Add a shake effect to the cart\n */\n _shakeCart() {\n if (typeof gsap !== 'undefined') {\n // Use GSAP for shake animation\n gsap.fromTo(\n this.cartElement,\n { x: 0 },\n {\n x: -10,\n duration: 0.1,\n repeat: 3,\n yoyo: true,\n onComplete: () => this.settings.onCartShake?.(this.cartElement),\n },\n );\n } else {\n // Fallback CSS animation\n const keyframes = [\n { transform: 'translateX(0)' },\n { transform: 'translateX(-10px)' },\n { transform: 'translateX(10px)' },\n { transform: 'translateX(0)' },\n ];\n\n this.cartElement.animate(keyframes, {\n duration: 300,\n iterations: 1,\n });\n\n this.settings.onCartShake?.(this.cartElement);\n }\n }\n }\n\n return CartFlow;\n});\n"],"mappings":"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","names":["require_CartFlow","__commonJSMin","exports","module","global","factory","CartFlow","options","event","button","item","image","soundEffect","audio","err","error","clone","imageRect","cartRect","computedStyles","_a","_b","keyframes"]}