UNPKG

html-trajectory

Version:

Animate HTML elements along a curved path from their current position to a target element. Eg. a product photo flying into the cart icon.

108 lines 5.32 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = projectile; function projectile(flyingId, targetId, options) { var _a, _b, _c, _d, _e, _f; const flying = document.getElementById(flyingId); const target = document.getElementById(targetId); // Try to get the field element first, fall back to document.body if not found const field = document.getElementById('field'); const containerElement = field || document.body; if (!flying || !target) { throw 'Invalid IDs provided to projectile function'; } const clone = flying.cloneNode(true); clone.removeAttribute('id'); clone.style.position = 'absolute'; clone.style.pointerEvents = 'none'; clone.style.margin = '0'; // Reset transformation if option is enabled if (options === null || options === void 0 ? void 0 : options.resetTransformation) { clone.style.transform = 'none'; clone.style.transformOrigin = 'center center'; } // Add html-trajectory-cloned class clone.classList.add("html-trajectory-cloned"); containerElement.appendChild(clone); const computedStyle = window.getComputedStyle(flying); const transform = computedStyle.transform; let baseRotation = 0; if (transform && transform !== 'none') { const matrixMatch = transform.match(/^matrix\(([-\d.,\s]+)\)$/); if (matrixMatch) { const values = matrixMatch[1].split(',').map(v => parseFloat(v.trim())); if (values.length >= 6) { const [a, b, c, d] = values; baseRotation = Math.atan2(b, a) * (180 / Math.PI); } } } clone.getBoundingClientRect(); const flyingRect = flying.getBoundingClientRect(); const containerRect = containerElement.getBoundingClientRect(); const targetRect = target.getBoundingClientRect(); const startX = flyingRect.left - containerRect.left; const startY = flyingRect.top - containerRect.top; const endX = targetRect.left - containerRect.left + (targetRect.width - clone.offsetWidth) / 2; const endY = targetRect.top - containerRect.top + (targetRect.height - clone.offsetHeight) / 2; clone.style.left = `${startX}px`; clone.style.top = `${startY}px`; const duration = ((_a = options === null || options === void 0 ? void 0 : options.duration) !== null && _a !== void 0 ? _a : 1) * 1000; const userAcceleration = (_b = options === null || options === void 0 ? void 0 : options.acceleration) !== null && _b !== void 0 ? _b : 4; const acceleration = userAcceleration * 100; const moveX = (_c = options === null || options === void 0 ? void 0 : options.moveX) !== null && _c !== void 0 ? _c : true; const moveY = (_d = options === null || options === void 0 ? void 0 : options.moveY) !== null && _d !== void 0 ? _d : true; const targetScale = (_e = options === null || options === void 0 ? void 0 : options.scale) !== null && _e !== void 0 ? _e : 1; const removeOriginal = (_f = options === null || options === void 0 ? void 0 : options.removeOriginal) !== null && _f !== void 0 ? _f : true; if (removeOriginal) { flying.remove(); } const distanceX = endX - startX; const distanceY = endY - startY; const velocityX = distanceX / (duration / 1000); const velocityYInitial = (distanceY - 0.5 * acceleration * Math.pow(duration / 1000, 2)) / (duration / 1000); let startTime; function animate(currentTime) { if (startTime === undefined) { startTime = currentTime; } const elapsed = (currentTime - startTime) / 1000; // in seconds const t = Math.min(elapsed, duration / 1000); const x = moveX ? startX + velocityX * t : startX; const y = moveY ? startY + velocityYInitial * t + 0.5 * acceleration * t * t : startY; const deltaT = 0.001; const nextT = Math.min(t + deltaT, duration / 1000); const nextX = moveX ? startX + velocityX * nextT : startX; const nextY = moveY ? startY + velocityYInitial * nextT + 0.5 * acceleration * nextT * nextT : startY; const dx = nextX - x; const dy = nextY - y; const angleRad = Math.atan2(dy, dx); const angleDeg = angleRad * (180 / Math.PI); const dynamicRotation = angleDeg + 90; const finalRotation = baseRotation + dynamicRotation; let scaleFactor = 1; if (t > (duration / 1000) * 0.66) { const progress = (t - (duration / 1000) * 0.66) / ((duration / 1000) * 0.34); scaleFactor = 1 + (targetScale - 1) * progress; } clone.style.transform = `rotate(${finalRotation}deg) scale(${scaleFactor})`; clone.style.left = `${x}px`; clone.style.top = `${y}px`; if (t < duration / 1000) { requestAnimationFrame(animate); } else { try { if (options === null || options === void 0 ? void 0 : options.onTransitionEnd) { options.onTransitionEnd(); } } catch (e) { console.error("Error in onTransitionEnd callback", e); } clone.remove(); } } requestAnimationFrame(animate); } //# sourceMappingURL=projectile.js.map