UNPKG

ngx-parallax-stars

Version:

Angular library to create beautiful stars with parallax effect.

219 lines (213 loc) 8.76 kB
import * as i0 from '@angular/core'; import { input, inject, Renderer2, ElementRef, signal, effect, Component, ChangeDetectionStrategy } from '@angular/core'; import { Observable, filter, debounceTime, distinctUntilChanged } from 'rxjs'; import { toSignal } from '@angular/core/rxjs-interop'; /** * Default star layers that are rendered when no layers are provided to the ngx-parallax-stars component */ const defaultStarLayers = [ { color: '#ffffff', speed: 10, density: 7, size: 1, direction: 'up', blur: 5, glow: 1, isRound: false, }, { color: '#ffffff', speed: 15, density: 2, size: 2, direction: 'up', blur: 1, glow: 2, isRound: false, }, { color: '#ffffff', speed: 20, density: 1, size: 3, direction: 'up', blur: 0, glow: 5, isRound: false, }, ]; /** @internal */ function randomInt(min, max) { return Math.floor(Math.random() * (max - min + 1)) + min; } /** @internal */ function resizeObservable(element) { return new Observable((subscriber) => { const resizeObserver = new ResizeObserver((entries) => { for (const entry of entries) { const contentRect = entry.contentRect; if (Math.abs(contentRect.height) < 1 && Math.abs(contentRect.width) < 1) continue; return subscriber.next({ width: contentRect.width, height: contentRect.height, }); } }); resizeObserver.observe(element); return function unsubscribe() { resizeObserver.unobserve(element); }; }); } class NgxParallaxStarsComponent { #renderer; #elRef; #initialized; #size; constructor() { /** * Overrides default star layers */ this.layers = input(defaultStarLayers); /** * If responsive mode is enabled, the component will automatically be re-rendered when its size changes */ this.responsive = input(true); this.#renderer = inject(Renderer2); this.#elRef = inject(ElementRef); this.#initialized = signal(false); this.#size = toSignal(resizeObservable(this.#elRef.nativeElement).pipe(filter(() => this.responsive() || !this.#initialized()), debounceTime(100), distinctUntilChanged())); effect(() => { const size = this.#size(); if (size) { this.#renderStars(this.layers(), size.width, size.height); this.#initialized.set(true); } }); } #renderStars(layers, width, height) { this.#renderer.setProperty(this.#elRef.nativeElement, 'innerHTML', ''); layers.forEach((layer) => { const boxShadow = this.#createBoxShadow(width, height, layer.density, layer.color, layer.blur, layer.glow); const layerElement = this.createLayerElement(layer, boxShadow, width, height); const afterLayerElement = this.createAfterLayerElement(layer, boxShadow, height, width); this.#renderer.appendChild(layerElement, afterLayerElement); this.#renderer.appendChild(this.#elRef.nativeElement, layerElement); }); } createAfterLayerElement(layer, boxShadow, height, width) { const afterLayerElement = this.#renderer.createElement('span'); this.#renderer.setStyle(afterLayerElement, 'content', ' '); this.#renderer.setStyle(afterLayerElement, 'position', 'absolute'); this.#renderer.setStyle(afterLayerElement, 'width', `${layer.size}px`); this.#renderer.setStyle(afterLayerElement, 'height', `${layer.size}px`); this.#renderer.setStyle(afterLayerElement, 'background', 'transparent'); this.#renderer.setStyle(afterLayerElement, 'boxShadow', boxShadow); switch (layer.direction) { case 'up': this.#renderer.setStyle(afterLayerElement, 'top', `${height}px`); break; case 'down': this.#renderer.setStyle(afterLayerElement, 'bottom', `${height}px`); break; case 'left': this.#renderer.setStyle(afterLayerElement, 'left', `${width}px`); break; case 'right': this.#renderer.setStyle(afterLayerElement, 'right', `${width}px`); break; } return afterLayerElement; } createLayerElement(layer, boxShadow, width, height) { const layerElement = this.#renderer.createElement('div'); this.#renderer.setStyle(layerElement, 'width', `${layer.size}px`); this.#renderer.setStyle(layerElement, 'height', `${layer.size}px`); this.#renderer.setStyle(layerElement, 'background', 'transparent'); this.#renderer.setStyle(layerElement, 'boxShadow', boxShadow); this.#renderer.setStyle(layerElement, 'borderRadius', layer.isRound ? '50%' : '0'); layerElement.animate(this.#createKeyFramesForLayer(layer, width, height), { duration: 1000000 / layer.speed, iterations: Infinity, }); return layerElement; } #createKeyFramesForLayer(layer, width, height) { switch (layer.direction) { case 'up': { return [ { transform: 'translateY(0)', }, { transform: `translateY(-${height}px)`, }, ]; } case 'down': { return [ { transform: 'translateY(0)', }, { transform: `translateY(${height}px)`, }, ]; } case 'left': { return [ { transform: 'translateX(0)', }, { transform: `translateX(-${width}px)`, }, ]; } case 'right': { return [ { transform: 'translateX(0)', }, { transform: `translateX(${width}px)`, }, ]; } default: { throw new Error(`Invalid direction: ${layer.direction}`); } } } #createBoxShadow(width, height, density, color, blur, glow) { const count = this.#calculateCount(density, width, height); const shadows = []; for (let i = 0; i < count; i++) { const xPos = randomInt(1, width); const yPos = randomInt(1, height); const starShadow = `${xPos}px ${yPos}px ${blur}px 0 ${color}`; shadows.push(starShadow); if (glow !== 0) { const glowShadow = `${xPos}px ${yPos}px ${glow}px 0 ${color}`; shadows.push(glowShadow); } } return shadows.join(','); } #calculateCount(density, width, height) { return density * (width / 100) * (height / 100); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.5", ngImport: i0, type: NgxParallaxStarsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "19.0.5", type: NgxParallaxStarsComponent, isStandalone: true, selector: "ngx-parallax-stars", inputs: { layers: { classPropertyName: "layers", publicName: "layers", isSignal: true, isRequired: false, transformFunction: null }, responsive: { classPropertyName: "responsive", publicName: "responsive", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: '', isInline: true, styles: [":host{display:block;overflow:hidden}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.5", ngImport: i0, type: NgxParallaxStarsComponent, decorators: [{ type: Component, args: [{ selector: 'ngx-parallax-stars', imports: [], template: '', changeDetection: ChangeDetectionStrategy.OnPush, styles: [":host{display:block;overflow:hidden}\n"] }] }], ctorParameters: () => [] }); /** * Generated bundle index. Do not edit. */ export { NgxParallaxStarsComponent, defaultStarLayers }; //# sourceMappingURL=ngx-parallax-stars.mjs.map