<section class="supt-section-products-innovation">
    <div class="supt-section-products-innovation__inner">
        <div class="container supt-section-products-innovation__products">
        </div>
    </div>
</section>

No notes defined.

<section class="supt-section-products-innovation" {{ attrs|attrs }}>
	<div class="supt-section-products-innovation__inner">
		<div class="container supt-section-products-innovation__products">
			{% for product in products %}
				<div class="supt-section-products-innovation__product{{ product.isWifiVision ? ' -is-wifi-vision' : '' }}">
					<div class="row justify-content-center align-items-center supt-section-products-innovation__product__inner">
						<div class="col-12 col-md-4 col-lg-3">
							<h2 class="supt-section-products-innovation__title">
								{{ product.title }}
							</h2>
						</div>
						<div class="col-12 col-md-4 col-lg-3 order-1 order-md-0">
							<div class="supt-section-products-innovation__image-wrapper">
								{% if product.isWifiVision %}
									{# Wifi Vision animation #}
									<span class="supt-section-products-innovation__image__zoom">
										{% for i in 1..4 %}
											<svg width="142" height="483" viewbox="0 0 142 483" fill="none" xmlns="http://www.w3.org/2000/svg" class="supt-section-products-innovation__image__zoom__radar">
												<path d="M130 75.5C130 108.361 103.361 135 70.5 135C37.6391 135 12 108.361 12 75.5C12 44.6391 38.6391 16 70.5 16C103.361 16 130 42.6391 130 75.5Z" stroke="#ED002F" stroke-width="0.5" vector-effect="non-scaling-stroke"/>
											</svg>
										{% endfor %}
										<img src="/sites/gv/files/flmngr/superhuit/innovation/overview/WifiVision-circle.png" alt="" class="supt-section-products-innovation__image__zoom__default">
									</span>
								{% endif %}
								{% if product.image %}
									<img src="{{ product.image.src }}" alt="{{ product.title }}" class="supt-section-products-innovation__image" style="--supt-innovation-product-desktop-width: {{ product.image.width.desktop }}px; --supt-innovation-product-tablet-width: {{ product.image.width.tablet }}px; --supt-innovation-product-mobile-width: {{ product.image.width.mobile }}px;">
								{% endif %}
							</div>
						</div>
						<div class="col-12 col-md-4 col-lg-3">
							<p class="supt-section-products-innovation__description">
								{{ product.description }}
							</p>
						</div>
					</div>
				</div>
			{% endfor %}
		</div>
	</div>
</section>
/* No context defined. */
  • Content:
    import { animate, createTimeline, onScroll, stagger } from 'animejs';
    import { Sizes } from '@/js/Sizes';
    
    export class SectionProductsInnovation {
    	$element: HTMLElement;
    	$inner: HTMLElement;
    	products: Product[];
    	sizes: Sizes;
    
    	constructor($element: HTMLElement) {
    		this.$element = $element;
    		this.$inner = $element.querySelector('.supt-section-products-innovation__inner') as HTMLElement;
    
    		const $products = $element.querySelectorAll('.supt-section-products-innovation__product');
    		this.products = Array.from($products).map($product => new Product($product as HTMLElement));
    
    		this.sizes = new Sizes();
    
    		this.handleWindowResize(); // Init
    
    		this.handleScrollAnimation();
    
    		window.addEventListener('resize', this.handleWindowResize.bind(this));
    	}
    
    	getDeltaBetween3rdAnd4thProductImage() {
    		const $3rdProductImage = this.products[2].$imageWrapper;
    		const $4thProductImage = this.products[3].$imageWrapper;
    		const delta =
    			$4thProductImage.parentElement!.getBoundingClientRect().top -
    			$3rdProductImage.parentElement!.getBoundingClientRect().top;
    
    		this.products[3].$imageWrapper.style.top = `${-1 * delta}px`;
    	}
    
    	handleScrollAnimation() {
    		const DISPLAY_IMAGE_TRANSLATION: Record<string, string[]> = this.sizes.isDesktop
    			? { y: ['75vh', '0vh'] }
    			: { x: ['75vw', '0vw'] };
    		const HIDE_IMAGE_TRANSLATION: Record<string, string[]> = this.sizes.isDesktop
    			? { y: ['0vh', '-75vh'] }
    			: { x: ['0vw', '-75vw'] };
    
    		/**
    		 * 1st product animation
    		 */
    		// Display 1st product image
    		animate(this.products[0].$image, {
    			scale: [0.5, 1],
    			autoplay: onScroll({
    				container: document.body,
    				target: this.$element,
    				enter: 'center top',
    				leave: 'bottom top+=100vh',
    				sync: true,
    			}),
    		});
    
    		// Display 1st product text
    		animate([this.products[0].$title, this.products[0].$description], {
    			opacity: [0, 1],
    			delay: stagger(50),
    			autoplay: onScroll({
    				container: document.body,
    				target: this.$element,
    				enter: 'bottom top+=100vh',
    				leave: 'bottom top+=120vh',
    				sync: true,
    			}),
    		});
    
    		/**
    		 * 2nd product animation
    		 */
    		// Display 2nd product image (+ hide 1st product image)
    		createTimeline({
    			autoplay: onScroll({
    				container: document.body,
    				target: this.$element,
    				enter: 'bottom top+=120vh',
    				leave: 'bottom top+=175vh',
    				sync: true,
    			}),
    		})
    			// Hide image of 1st product (needs to be the wrapper as it's not possible to re-animate the image because of animejs being dummy....!!!)
    			.add(this.products[0].$imageWrapper, {
    				...HIDE_IMAGE_TRANSLATION,
    				scale: [1, 0.5],
    			})
    			// Display image of 2nd product
    			.add(
    				this.products[1].$image,
    				{
    					...DISPLAY_IMAGE_TRANSLATION,
    					scale: [0.5, 1],
    				},
    				'<<'
    			);
    
    		// Display 2nd product text (+ hide 1st product text)
    		createTimeline({
    			autoplay: onScroll({
    				container: document.body,
    				target: this.$element,
    				enter: 'bottom top+=130vh',
    				leave: 'bottom top+=160vh',
    				sync: true,
    			}),
    		})
    			// Hide inner of first product
    			.add(this.products[0].$inner, {
    				opacity: [1, 0],
    			})
    			// Display text of second product
    			.add([this.products[1].$title, this.products[1].$description], {
    				opacity: [0, 1],
    				delay: stagger(50),
    			});
    
    		/**
    		 * 3rd product animation
    		 */
    		// Display 3rd product image (+ hide 2nd product image)
    		createTimeline({
    			autoplay: onScroll({
    				container: document.body,
    				target: this.$element,
    				enter: 'bottom top+=190vh',
    				leave: 'bottom top+=255vh',
    				sync: true,
    			}),
    		})
    			// Hide image of 2nd product (needs to be the wrapper as it's not possible to re-animate the image because of animejs being dummy....!!!)
    			.add(this.products[1].$imageWrapper, {
    				...HIDE_IMAGE_TRANSLATION,
    				scale: [1, 0.5],
    			})
    			// Display image of 3rd product
    			.add(
    				this.products[2].$image,
    				{
    					...DISPLAY_IMAGE_TRANSLATION,
    					scale: [0.5, 1],
    				},
    				'<<'
    			);
    
    		// Display 3rd product text (+ hide 2nd product text)
    		createTimeline({
    			autoplay: onScroll({
    				container: document.body,
    				target: this.$element,
    				enter: 'bottom top+=210vh',
    				leave: 'bottom top+=240vh',
    				sync: true,
    			}),
    		})
    			// Hide inner of first product
    			.add(this.products[1].$inner, {
    				opacity: [1, 0],
    			})
    			// Display text of second product
    			.add([this.products[2].$title, this.products[2].$description], {
    				opacity: [0, 1],
    				delay: stagger(50),
    			});
    
    		/**
    		 * 4th product animation
    		 */
    		// Display 4th product image (+ hide 3rd product image)
    		createTimeline({
    			autoplay: onScroll({
    				container: document.body,
    				target: this.$element,
    				enter: 'bottom top+=300vh',
    				leave: 'bottom bottom',
    				sync: true,
    			}),
    		})
    			// Display image of 3rd product
    			.add(this.products[3].$imageWrapper, {
    				opacity: [0, 1],
    				duration: 500,
    			})
    			// Hide image of 2nd product (needs to be the wrapper as it's not possible to re-animate the image because of animejs being dummy....!!!)
    			.add(
    				this.products[2].$imageWrapper,
    				{
    					opacity: [1, 0],
    				},
    				'+=300'
    			)
    			// Move the image of the fourth product to the middle of the screen
    			.add(
    				this.products[3].$imageWrapper,
    				{
    					transform: 'translate(0%, 35%)',
    				},
    				'<'
    			);
    
    		// Display 4th product text (+ hide 3rd product text)
    		createTimeline({
    			autoplay: onScroll({
    				container: document.body,
    				target: this.$element,
    				enter: 'bottom top+=330vh',
    				leave: 'bottom top+=360vh',
    				sync: true,
    			}),
    		})
    			// Hide inner of first product
    			.add(this.products[2].$inner, {
    				opacity: [1, 0],
    			})
    			// Display text of second product
    			.add([this.products[3].$title, this.products[3].$description], {
    				opacity: [0, 1],
    				delay: stagger(50),
    			});
    
    		// Animate the wifi vision animation
    		animate(this.products[3].$zoomRadars!, {
    			opacity: [1, 0],
    			scale: [1, 6],
    			duration: 2000,
    			loop: true,
    			ease: 'easeInOutSine',
    			delay: stagger(400, { ease: 'inQuad' }),
    			autoplay: onScroll({
    				container: document.body,
    				target: this.$element,
    				enter: 'bottom bottom-=100vh',
    				sync: 'play play pause pause',
    			}),
    		});
    
    		// Hide section
    		animate(this.$inner, {
    			opacity: [1, 0],
    			autoplay: onScroll({
    				container: document.body,
    				target: this.$element,
    				enter: 'bottom bottom',
    				leave: 'center bottom',
    				sync: true,
    			}),
    		});
    	}
    
    	handleWindowResize() {
    		this.getDeltaBetween3rdAnd4thProductImage();
    	}
    }
    
    class Product {
    	$element: HTMLElement;
    	$inner: HTMLElement;
    	$imageWrapper: HTMLElement;
    	$image: HTMLElement;
    	$title: HTMLElement;
    	$description: HTMLElement;
    	$zoom?: HTMLElement;
    	$zoomRadars?: NodeListOf<HTMLElement>;
    
    	constructor($element: HTMLElement) {
    		this.$element = $element;
    		this.$inner = $element.querySelector(
    			'.supt-section-products-innovation__product__inner'
    		) as HTMLElement;
    		this.$imageWrapper = $element.querySelector(
    			'.supt-section-products-innovation__image-wrapper'
    		) as HTMLElement;
    		this.$image = $element.querySelector('.supt-section-products-innovation__image') as HTMLElement;
    		this.$title = $element.querySelector('.supt-section-products-innovation__title') as HTMLElement;
    		this.$description = $element.querySelector(
    			'.supt-section-products-innovation__description'
    		) as HTMLElement;
    
    		// Zoom for Wifi Vision
    		this.$zoom = $element.querySelector(
    			'.supt-section-products-innovation__image__zoom'
    		) as HTMLElement;
    		this.$zoomRadars = $element.querySelectorAll(
    			'.supt-section-products-innovation__image__zoom__radar'
    		) as NodeListOf<HTMLElement>;
    	}
    }
    
  • URL: /components/raw/section-products-innovation/index.ts
  • Filesystem Path: src/components/organisms/02-innovation/section-products-innovation/index.ts
  • Size: 7.6 KB
  • Content:
    .supt-section-products-innovation {
    	margin-top: -50vh;
    
    	height: 400vh;
    
    	&__inner {
    		height: 100vh;
    		position: sticky;
    		top: 0;
    	}
    
    	&__products {
    		height: 100vh;
    		position: sticky;
    		top: 0;
    	}
    
    	&__product {
    		position: relative;
    
    		&.-is-wifi-vision {
    			.supt-section-products-innovation__image-wrapper {
    				position: relative;
    			}
    			.supt-section-products-innovation__image {
    				opacity: 0;
    			}
    		}
    
    		&__inner {
    			position: absolute;
    			inset: 0;
    			height: 100vh;
    
    			padding: $spacing-6 0;
    			row-gap: $spacing-6;
    
    			overflow: hidden;
    
    			flex-flow: column nowrap;
    
    			@media (min-width: $breakpoint-xs) {
    				padding: $spacing-12 0;
    			}
    			@media (min-width: $breakpoint-sm) {
    				padding: $spacing-24 0;
    			}
    			@media (min-width: $breakpoint-md) {
    				height: 100vh;
    				flex-flow: row wrap;
    			}
    
    			.col-12 {
    				flex: none;
    				/* @media (min-width: $breakpoint-md) {
    					flex: 0 0 100%;
    				} */
    			}
    		}
    	}
    
    	&__title,
    	&__description {
    		color: $color-white;
    
    		opacity: 0;
    	}
    
    	&__title {
    		@extend %t-h2;
    		text-shadow: $text-shadow-glow;
    
    		margin-bottom: 0;
    	}
    
    	&__image {
    		display: block;
    		height: auto;
    		max-width: 100%;
    		max-height: 100%;
    		object-fit: contain;
    		margin: auto;
    		width: var(--supt-innovation-product-mobile-width);
    
    		@media (min-width: $breakpoint-md) {
    			width: var(--supt-innovation-product-tablet-width);
    		}
    		@media (min-width: $breakpoint-lg) {
    			width: var(--supt-innovation-product-desktop-width);
    		}
    
    		/* For Wifi Vision animation */
    		&__zoom {
    			position: relative;
    			display: block;
    
    			position: absolute;
    			inset: 0;
    
    			&__radar {
    				display: block;
    				position: absolute;
    				inset: 0;
    				height: 100%;
    				margin: auto;
    
    				width: 80px;
    				@media (min-width: $breakpoint-md) {
    					width: 110px;
    				}
    				@media (min-width: $breakpoint-lg) {
    					width: 142px;
    				}
    
    				transform-origin: 50% 15%;
    				z-index: 0;
    			}
    
    			&__default {
    				max-width: 150%;
    				max-height: 110%;
    				position: absolute;
    				bottom: 0;
    				left: 50%;
    				transform: translateX(-50%);
    				z-index: 1;
    			}
    		}
    	}
    	&__description {
    		@extend %paragraph-section-text;
    	}
    }
    
  • URL: /components/raw/section-products-innovation/section-products-innovation.css
  • Filesystem Path: src/components/organisms/02-innovation/section-products-innovation/section-products-innovation.css
  • Size: 2.1 KB
  • Handle: @section-products-innovation
  • Preview:
  • Filesystem Path: src/components/organisms/02-innovation/section-products-innovation/section-products-innovation.twig