Advertisement
nrzmalik

Articulate Storyline Video Scroll Animation JavaScript

Jan 2nd, 2025 (edited)
394
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
JavaScript 10.42 KB | Source Code | 0 0
  1. initVideoScrollControl("YourRectangleAccessibilityText", "YourVideoAccessibilityText");
  2.  
  3. /*
  4. VideoScrollTrigger v1.2.0
  5. Description: Allows scrubbing through videos using scroll, touch, or keyboard controls
  6. Usage: Replace "YourRectangleAccessibilityText" and "YourVideoAccessibilityText" with the
  7. accessibility text of the slide objects in Storyline. */
  8.  
  9. function initVideoScrollControl(rectangleId, videoId, options = {}) {
  10.     // Selectors
  11.     var videoContainer = document.querySelector(`[data-acc-text="${rectangleId}"] div`);
  12.     var video = document.querySelector(`[data-acc-text="${videoId}"] video`);
  13.    
  14.     // Error handling and fallbacks
  15.     if (!videoContainer) {
  16.         console.error(`VideoScrollTrigger: Container with data-acc-text="${rectangleId}" not found`);
  17.         return;
  18.     }
  19.    
  20.     if (!video) {
  21.         console.error(`VideoScrollTrigger: Video with data-acc-text="${videoId}" not found`);
  22.         return;
  23.     }
  24.    
  25.     // Merged config with user options
  26.     const config = {
  27.         scrollSensitivity: 0.3,
  28.         touchSensitivity: 0.2,
  29.         frameRate: 1000 / 60,
  30.         smoothingFactor: 0.8,
  31.         momentumScrolling: true,
  32.         momentumDecay: 0.95,
  33.         allowHorizontalScroll: true,
  34.         keyboardSensitivity: 5,
  35.         enableKeyboard: true,
  36.         ...options
  37.     };
  38.    
  39.     // State variables
  40.     let touchStartY = null;
  41.     let touchStartX = null;
  42.     let lastTouchY = null;
  43.     let lastTouchX = null;
  44.     let lastTime = video ? video.currentTime : 0;
  45.     let targetTime = lastTime;
  46.     let isAnimating = false;
  47.     let lastScrollTime = Date.now();
  48.     let scrollTimeout;
  49.     let momentum = 0;
  50.     let lastMomentumUpdate = Date.now();
  51.     let isMomentumActive = false;
  52.    
  53.     // Apply momentum decay
  54.     function applyMomentum() {
  55.         if (!config.momentumScrolling || Math.abs(momentum) < 0.001) {
  56.             isMomentumActive = false;
  57.             return;
  58.         }
  59.        
  60.         const now = Date.now();
  61.         const elapsed = now - lastMomentumUpdate;
  62.         lastMomentumUpdate = now;
  63.        
  64.         momentum *= Math.pow(config.momentumDecay, elapsed / 16);
  65.         updateVideoTime(targetTime + momentum);
  66.        
  67.         requestAnimationFrame(applyMomentum);
  68.     }
  69.    
  70.     function animateToTargetTime() {
  71.         if (!video || !isAnimating) return;
  72.         const diff = targetTime - lastTime;
  73.         if (Math.abs(diff) < 0.01) {
  74.             lastTime = targetTime;
  75.             isAnimating = false;
  76.             return;
  77.         }
  78.         lastTime += diff * config.smoothingFactor;
  79.         video.currentTime = lastTime;
  80.         requestAnimationFrame(animateToTargetTime);
  81.     }
  82.    
  83.     function updateVideoTime(newTargetTime) {
  84.         if (!video || isNaN(video.duration)) return;
  85.        
  86.         targetTime = Math.max(0, Math.min(video.duration, newTargetTime));
  87.         if (!isAnimating) {
  88.             isAnimating = true;
  89.             animateToTargetTime();
  90.         }
  91.     }
  92.    
  93.     function handleScroll(event) {
  94.         event.preventDefault();
  95.         if (!video || isNaN(video.duration)) return;
  96.        
  97.         const now = Date.now();
  98.         if (now - lastScrollTime < config.frameRate) return;
  99.         lastScrollTime = now;
  100.        
  101.         // Calculate scroll amount based on primary scroll direction
  102.         let scrollAmount;
  103.         if (Math.abs(event.deltaY) >= Math.abs(event.deltaX) || !config.allowHorizontalScroll) {
  104.             scrollAmount = event.deltaY * config.scrollSensitivity;
  105.         } else {
  106.             scrollAmount = event.deltaX * config.scrollSensitivity;
  107.         }
  108.        
  109.         // Update momentum
  110.         momentum = scrollAmount / 100;
  111.        
  112.         const newTime = targetTime + momentum;
  113.         updateVideoTime(newTime);
  114.        
  115.         // Start momentum scrolling
  116.         if (config.momentumScrolling && !isMomentumActive) {
  117.             isMomentumActive = true;
  118.             lastMomentumUpdate = now;
  119.             requestAnimationFrame(applyMomentum);
  120.         }
  121.        
  122.         clearTimeout(scrollTimeout);
  123.         scrollTimeout = setTimeout(() => {
  124.             isAnimating = false;
  125.         }, 150);
  126.     }
  127.    
  128.     function handleTouchStart(event) {
  129.         if (event.touches.length !== 1) return;
  130.        
  131.         touchStartY = event.touches[0].clientY;
  132.         touchStartX = event.touches[0].clientX;
  133.         lastTouchY = touchStartY;
  134.         lastTouchX = touchStartX;
  135.         lastTime = video.currentTime;
  136.         targetTime = lastTime;
  137.         momentum = 0;
  138.         isMomentumActive = false;
  139.        
  140.         event.preventDefault();
  141.     }
  142.    
  143.     function handleTouchMove(event) {
  144.         event.preventDefault();
  145.         if (!video || isNaN(video.duration) || event.touches.length !== 1) return;
  146.        
  147.         const currentY = event.touches[0].clientY;
  148.         const currentX = event.touches[0].clientX;
  149.        
  150.         // Determine primary direction of movement
  151.         const deltaY = lastTouchY - currentY;
  152.         const deltaX = lastTouchX - currentX;
  153.        
  154.         lastTouchY = currentY;
  155.         lastTouchX = currentX;
  156.        
  157.         let delta;
  158.         if (Math.abs(deltaY) >= Math.abs(deltaX) || !config.allowHorizontalScroll) {
  159.             delta = deltaY;
  160.         } else {
  161.             delta = deltaX;
  162.         }
  163.        
  164.         const direction = Math.sign(delta);
  165.         const magnitude = Math.abs(delta);
  166.         const scaledDelta = direction * Math.pow(magnitude, 1.5) * config.touchSensitivity;
  167.        
  168.         const timeAdjustment = scaledDelta / 100;
  169.         momentum = timeAdjustment;
  170.        
  171.         const newTime = video.currentTime + timeAdjustment;
  172.         updateVideoTime(newTime);
  173.     }
  174.    
  175.     function handleTouchEnd(event) {
  176.         event.preventDefault();
  177.        
  178.         // Start momentum effect
  179.         if (config.momentumScrolling && Math.abs(momentum) > 0.001) {
  180.             isMomentumActive = true;
  181.             lastMomentumUpdate = Date.now();
  182.             requestAnimationFrame(applyMomentum);
  183.         }
  184.        
  185.         touchStartY = null;
  186.         touchStartX = null;
  187.         lastTouchY = null;
  188.         lastTouchX = null;
  189.        
  190.         setTimeout(() => {
  191.             isAnimating = false;
  192.         }, 150);
  193.     }
  194.    
  195.     function handleKeyDown(event) {
  196.         if (!config.enableKeyboard || !video || isNaN(video.duration)) return;
  197.        
  198.         let timeAdjustment = 0;
  199.         const step = config.keyboardSensitivity / 100;
  200.        
  201.         switch (event.key) {
  202.             case 'ArrowUp':
  203.             case 'ArrowLeft':
  204.                 timeAdjustment = -step;
  205.                 event.preventDefault();
  206.                 break;
  207.             case 'ArrowDown':
  208.             case 'ArrowRight':
  209.                 timeAdjustment = step;
  210.                 event.preventDefault();
  211.                 break;
  212.             case 'Home':
  213.                 updateVideoTime(0);
  214.                 event.preventDefault();
  215.                 return;
  216.             case 'End':
  217.                 updateVideoTime(video.duration);
  218.                 event.preventDefault();
  219.                 return;
  220.             default:
  221.                 return;
  222.         }
  223.        
  224.         updateVideoTime(video.currentTime + timeAdjustment);
  225.     }
  226.    
  227.     // Clean up any existing event listeners before adding new ones
  228.     function setupEventListeners() {
  229.         // Remove any existing event listeners first
  230.         videoContainer.removeEventListener('wheel', handleScroll, { passive: false, capture: true });
  231.         videoContainer.removeEventListener('touchstart', handleTouchStart, { passive: false });
  232.         videoContainer.removeEventListener('touchmove', handleTouchMove, { passive: false });
  233.         videoContainer.removeEventListener('touchend', handleTouchEnd, { passive: false });
  234.         document.removeEventListener('keydown', handleKeyDown);
  235.        
  236.         // Add new event listeners
  237.         videoContainer.addEventListener('wheel', handleScroll, {
  238.             passive: false,
  239.             capture: true
  240.         });
  241.        
  242.         videoContainer.addEventListener('touchstart', handleTouchStart, {
  243.             passive: false
  244.         });
  245.        
  246.         videoContainer.addEventListener('touchmove', handleTouchMove, {
  247.             passive: false
  248.         });
  249.        
  250.         videoContainer.addEventListener('touchend', handleTouchEnd, {
  251.             passive: false
  252.         });
  253.        
  254.         if (config.enableKeyboard) {
  255.             document.addEventListener('keydown', handleKeyDown);
  256.         }
  257.     }
  258.    
  259.     // Initialize video
  260.     video.pause();
  261.     video.preload = 'auto';
  262.    
  263.     video.addEventListener('waiting', () => {
  264.         isAnimating = false;
  265.     });
  266.    
  267.     video.addEventListener('canplay', () => {
  268.         if (targetTime !== lastTime) {
  269.             isAnimating = true;
  270.             animateToTargetTime();
  271.         }
  272.     });
  273.    
  274.     // Set up all event listeners
  275.     setupEventListeners();
  276.    
  277.     // Return API for external control
  278.     return {
  279.         updateConfig: function(newOptions) {
  280.             Object.assign(config, newOptions);
  281.             setupEventListeners(); // Re-setup event listeners in case keyboard was enabled/disabled
  282.         },
  283.         seekTo: function(timePercentage) {
  284.             if (!video || isNaN(video.duration)) return;
  285.             const newTime = video.duration * Math.max(0, Math.min(1, timePercentage));
  286.             updateVideoTime(newTime);
  287.         }
  288.     };
  289. }
  290.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement