UNPKG

clarity-js

Version:

An analytics library that uses web page interactions to generate aggregated insights

128 lines (117 loc) 4.6 kB
import { Event } from "@clarity-types/data"; import { AnimationOperation, AnimationState } from "@clarity-types/layout"; import { time } from "@src/core/time"; import { shortid } from "@src/data/metadata"; import encode from "@src/layout/encode"; import { getId } from "@src/layout/dom"; import * as core from "@src/core"; export let state: AnimationState[] = []; let elementAnimate: (keyframes: Keyframe[] | PropertyIndexedKeyframes, options?: number | KeyframeAnimationOptions) => Animation = null; let overridden = false; const animationId = '__clrAId'; const operationCount = '__clrOCnt'; const maxOperations = 20; export function start(): void { if ( window["Animation"] && window["Animation"].prototype && window["KeyframeEffect"] && window["KeyframeEffect"].prototype && window["KeyframeEffect"].prototype.getKeyframes && window["KeyframeEffect"].prototype.getTiming ) { reset(); if (!overridden) { overridden = true; ["play", "pause", "commitStyles", "cancel", "finish"].forEach(overrideAnimationHelper); } if (elementAnimate === null) { elementAnimate = Element.prototype.animate; Element.prototype.animate = function(): Animation { var createdAnimation = elementAnimate.apply(this, arguments); trackAnimationOperation(createdAnimation, "play"); return createdAnimation; } } if (document.getAnimations) { for (var animation of document.getAnimations()) { if (animation.playState === "finished") { trackAnimationOperation(animation, "finish"); } else if (animation.playState === "paused" || animation.playState === "idle") { trackAnimationOperation(animation, "pause"); } else if (animation.playState === "running") { trackAnimationOperation(animation, "play"); } } } } } export function reset(): void { state = []; } function track(time: number, id: string, operation: AnimationOperation, keyFrames?: string, timing?: string, targetId?: number, timeline?: string): void { state.push({ time, event: Event.Animation, data: { id, operation, keyFrames, timing, targetId, timeline } }); encode(Event.Animation); } export function stop(): void { reset(); } function overrideAnimationHelper(name: string) { let original = Animation.prototype[name]; if (typeof original !== "function") { return; } Animation.prototype[name] = function(): void { trackAnimationOperation(this, name); return original.apply(this, arguments); } } function trackAnimationOperation(animation: Animation, name: string) { if (core.active()) { let effect = <KeyframeEffect>animation.effect; let target = effect?.target ? getId(effect.target) : null; if (target !== null && effect.getKeyframes && effect.getTiming) { if (!animation[animationId]) { animation[animationId] = shortid(); animation[operationCount] = 0; let keyframes = effect.getKeyframes(); let timing = effect.getTiming(); track(time(), animation[animationId], AnimationOperation.Create, JSON.stringify(keyframes), JSON.stringify(timing), target); } if (animation[operationCount]++ < maxOperations) { let operation: AnimationOperation = null; switch (name) { case "play": operation = AnimationOperation.Play; break; case "pause": operation = AnimationOperation.Pause; break; case "cancel": operation = AnimationOperation.Cancel; break; case "finish": operation = AnimationOperation.Finish; break; case "commitStyles": operation = AnimationOperation.CommitStyles; break; } if (operation) { track(time(), animation[animationId], operation); } } } } }