vvjs-1.0.1/js/slideshow-visibility.js

js/slideshow-visibility.js
/**
 * @file
 * Slideshow visibility management.
 *
 * Handles intersection observer, viewport visibility detection,
 * and automatic pause/resume based on visibility.
 */

((Drupal) => {
  'use strict';

  /**
   * Debounce utility function.
   */
  function debounce(func, delay) {
    let timer;
    return function(...args) {
      clearTimeout(timer);
      timer = setTimeout(() => func.apply(this, args), delay);
    };
  }

  /**
   * Visibility manager class.
   */
  class SlideshowVisibility {
    constructor(container, slideshowCore) {
      this.container = container;
      this.core = slideshowCore;

      // Visibility state
      this.isVisible = true;
      this.previousVisibility = false;
      this.intersectionObserver = null;

      // Configuration
      this.visibilityThreshold = 0.5; // How much of slideshow must be visible
      this.scrollDebounceDelay = 200;
      this.resizeDebounceDelay = 200;

      this.init();
    }

    init() {
      this.setupIntersectionObserver();
      this.setupEventListeners();
      this.handleInitialVisibility();
    }

    /**
     * Set up intersection observer for efficient visibility detection.
     */
    setupIntersectionObserver() {
      if ('IntersectionObserver' in window) {
        this.intersectionObserver = new IntersectionObserver((entries) => {
          const entry = entries[0];
          this.handleVisibilityChange(entry.isIntersecting);
        }, {
          threshold: this.visibilityThreshold,
          rootMargin: '50px' // Start/stop slightly before element enters/leaves viewport
        });

        this.intersectionObserver.observe(this.container);
      } else {
        // Fallback for browsers without IntersectionObserver
        this.setupScrollFallback();
      }
    }

    /**
     * Handle visibility changes detected by intersection observer.
     */
    handleVisibilityChange(isVisible) {
      this.isVisible = isVisible;
      this.core.setVisibility(isVisible);

      // Dispatch event for other modules
      this.container.dispatchEvent(new CustomEvent('vvjs:visibilityChanged', {
        detail: { isVisible: isVisible }
      }));
    }

    /**
     * Set up event listeners for visibility-related events.
     */
    setupEventListeners() {
      // Page visibility (tab switching)
      document.addEventListener('visibilitychange', () => {
        this.handlePageVisibilityChange();
      });

      // Window resize (debounced)
      window.addEventListener('resize', debounce(() => {
        this.handleResize();
      }, this.resizeDebounceDelay));

      // Scroll events (fallback and additional detection)
      if (!this.intersectionObserver) {
        document.addEventListener('scroll', debounce(() => {
          this.handleScroll();
        }, this.scrollDebounceDelay));
      }
    }

    /**
     * Handle page visibility changes (tab switching, minimizing).
     */
    handlePageVisibilityChange() {
      if (document.hidden) {
        this.core.stopAutoSlide();
      } else {
        // Check if slideshow is still visible when tab becomes active
        const isCurrentlyVisible = this.checkElementVisibility();
        if (isCurrentlyVisible && !this.core.isPaused) {
          this.core.startAutoSlide();
        }
      }
    }

    /**
     * Handle window resize events.
     */
    handleResize() {
      // Recalculate slideshow dimensions
      this.core.adjustHeight();

      // Check visibility after resize
      const isCurrentlyVisible = this.checkElementVisibility();
      this.handleVisibilityChange(isCurrentlyVisible);
    }

    /**
     * Handle scroll events (fallback for browsers without IntersectionObserver).
     */
    handleScroll() {
      const isCurrentlyVisible = this.checkElementVisibility();

      if (isCurrentlyVisible !== this.previousVisibility) {
        this.previousVisibility = isCurrentlyVisible;
        this.handleVisibilityChange(isCurrentlyVisible);
      }
    }

    /**
     * Check if slideshow element is visible in viewport.
     */
    checkElementVisibility() {
      const rect = this.container.getBoundingClientRect();
      const windowHeight = window.innerHeight || document.documentElement.clientHeight;
      const windowWidth = window.innerWidth || document.documentElement.clientWidth;

      // Calculate visible area
      const visibleHeight = Math.min(rect.bottom, windowHeight) - Math.max(rect.top, 0);
      const visibleWidth = Math.min(rect.right, windowWidth) - Math.max(rect.left, 0);

      // Element is considered visible if threshold percentage is visible
      const visibilityRatio = (visibleHeight * visibleWidth) / (rect.height * rect.width);

      return visibilityRatio >= this.visibilityThreshold;
    }

    /**
     * Handle initial visibility state.
     */
    handleInitialVisibility() {
      // Check initial visibility
      this.isVisible = this.checkElementVisibility();
      this.previousVisibility = this.isVisible;

      // Set initial core visibility state
      this.core.setVisibility(this.isVisible);
    }

    /**
     * Force visibility check (useful for dynamic content changes).
     */
    forceVisibilityCheck() {
      const isCurrentlyVisible = this.checkElementVisibility();
      this.handleVisibilityChange(isCurrentlyVisible);
    }

    /**
     * Set custom visibility threshold.
     */
    setVisibilityThreshold(threshold) {
      this.visibilityThreshold = Math.max(0, Math.min(1, threshold));

      // Update intersection observer if available
      if (this.intersectionObserver) {
        this.intersectionObserver.disconnect();
        this.setupIntersectionObserver();
      }
    }

    /**
     * Get current visibility state.
     */
    getVisibilityState() {
      return {
        isVisible: this.isVisible,
        isPageVisible: !document.hidden,
        elementVisibility: this.checkElementVisibility()
      };
    }

    /**
     * Manually set visibility state (for testing or special cases).
     */
    setVisibility(visible) {
      this.handleVisibilityChange(visible);
    }

    /**
     * Set up scroll-based visibility detection fallback.
     */
    setupScrollFallback() {
      // More frequent checking for older browsers
      this.scrollDebounceDelay = 100;

      document.addEventListener('scroll', debounce(() => {
        this.handleScroll();
      }, this.scrollDebounceDelay));

      // Also check on load
      window.addEventListener('load', () => {
        this.handleInitialVisibility();
      });
    }

    /**
     * Clean up visibility detection.
     */
    destroy() {
      if (this.intersectionObserver) {
        this.intersectionObserver.disconnect();
        this.intersectionObserver = null;
      }

      // Event listeners are automatically cleaned up when the element is removed
      // but we could add explicit cleanup here if needed
    }
  }

  // Export to global namespace
  Drupal.vvjs = Drupal.vvjs || {};
  Drupal.vvjs.SlideshowVisibility = SlideshowVisibility;

})(Drupal);

Главная | Обратная связь

drupal hosting | друпал хостинг | it patrol .inc