UNPKG

ngx-skltn

Version:
317 lines (306 loc) 12.5 kB
import * as i0 from '@angular/core'; import { InjectionToken, Injectable, Inject, Component, ElementRef, ChangeDetectorRef, NgZone, Input, HostListener, Directive, ContentChild, NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { DomSanitizer } from '@angular/platform-browser'; import { BehaviorSubject, Subject } from 'rxjs'; import { debounceTime, tap, filter, takeUntil, throttleTime } from 'rxjs/operators'; const SKLTN_CONFIG_TOKEN = new InjectionToken('SkltnConfig'); class SkltnService { constructor(config) { this.ids = []; this.defaultConfig = { rectRadius: 4, bgFill: '#ddd', flareFill: 'rgba(255, 255, 255, 0.6)', flareWidth: '150px', duration: 1200, delay: 0, timing: 'ease-in-out', }; this.config = Object.assign({}, this.defaultConfig, config); } } SkltnService.ɵprov = i0.ɵɵdefineInjectable({ factory: function SkltnService_Factory() { return new SkltnService(i0.ɵɵinject(SKLTN_CONFIG_TOKEN)); }, token: SkltnService, providedIn: "root" }); SkltnService.decorators = [ { type: Injectable, args: [{ providedIn: 'root' },] } ]; SkltnService.ctorParameters = () => [ { type: undefined, decorators: [{ type: Inject, args: [SKLTN_CONFIG_TOKEN,] }] } ]; function generateId() { return Math.random().toString(32).substr(2, 4); } class BonesList { constructor() { this.changes = new BehaviorSubject([]); this.map = new Map(); } add(bone) { const id = generateId(); this.map.set(id, bone); this.emit(); return id; } remove(id) { this.map.delete(id); this.emit(); } emit() { const list = this.getList(); this.changes.next(list); } getList() { const values = this.map.values(); return Array.from(values); } } BonesList.decorators = [ { type: Injectable } ]; BonesList.ctorParameters = () => []; class SkltnComponent { constructor(skltnService, element, sanitizer, cd, zone, bonesList) { this.skltnService = skltnService; this.element = element; this.sanitizer = sanitizer; this.cd = cd; this.zone = zone; this.bonesList = bonesList; this.showSkltn = true; this.checkInterval = 100; this.updStream$ = new Subject(); this.checkStream$ = new Subject(); this.unsubscribe$ = new Subject(); this.defaultConfig = { rectRadius: 4, bgFill: '#ddd', flareFill: 'rgba(255, 255, 255, 0.6)', flareWidth: '150px', duration: 1200, delay: 0, timing: 'ease-in-out', }; const conf = this.skltnService.config; this.rectRadius = conf.rectRadius; this.bgFill = conf.bgFill; this.flareFill = conf.flareFill; this.flareWidth = conf.flareWidth; this.duration = conf.duration; this.delay = conf.delay; this.timing = conf.timing; this.sufix = this.getSufixWithID(); this.gradientId = 'gradient-' + this.sufix; this.maskId = 'mask-' + this.sufix; } onResize(event) { this.updStream$.next(); } ngOnInit() { this.animationCss = this.sanitizer.bypassSecurityTrustHtml(` <style> @keyframes skltnFlareAnimation { 0% { x: calc(0% - ${this.flareWidth}); } 100% { x: 100%; } } .skltn-flare { animation-name: skltnFlareAnimation; animation-duration: ${this.duration}ms; animation-timing-function: ${this.timing}; animation-iteration-count: infinite; animation-delay: ${this.delay}ms; } </style>`); // Bones Updates this.bonesList.changes.pipe(debounceTime(50), tap(list => { this.bones = list; this.calcShapes(); this.cd.detectChanges(); }), filter((list, index) => index === 0), tap(() => { this.checkStream$.next(); }), takeUntil(this.unsubscribe$)).subscribe(); // Update this.updStream$.pipe(throttleTime(50), takeUntil(this.unsubscribe$)).subscribe(() => this.calcShapes()); // Update href (Safari Bug, SVG Ref Path) this.href = window.location.href; this.zone.runOutsideAngular(() => { this.checkStream$.pipe(tap(() => { // Check if href changed if (this.href !== window.location.href) { this.href = window.location.href; this.cd.detectChanges(); } // Check if root element resized const el = this.element.nativeElement; const { width, height } = el.getBoundingClientRect(); if (this.parentClientRect.width !== width || this.parentClientRect.height !== height) { this.updStream$.next(); this.cd.detectChanges(); } setTimeout(() => { this.checkStream$.next(); }, this.checkInterval); }), takeUntil(this.unsubscribe$)).subscribe(); }); } ngAfterViewInit() { } ngOnDestroy() { this.unsubscribe$.next(); this.unsubscribe$.complete(); } getSufixWithID() { return generateId() + '-skltn'; } calcShapes() { var _a; // Root SVG Element const el = this.element.nativeElement; this.parentClientRect = el.getBoundingClientRect(); this.viewBox = `0 0 ${this.parentClientRect.width} ${this.parentClientRect.height}`; // SVG Shapes if (!((_a = this.bones) === null || _a === void 0 ? void 0 : _a.length)) { this.shapes = null; return; } this.shapes = this.bones.map(bone => { const boneEl = bone.element.nativeElement; const clientRect = boneEl.getBoundingClientRect(); const radius = bone.rectRadius || this.rectRadius; if (bone.type === 'circle') { return { type: 'circle', cx: clientRect.x - this.parentClientRect.x + clientRect.width / 2, cy: clientRect.y - this.parentClientRect.y + clientRect.height / 2, rx: clientRect.width / 2, ry: clientRect.height / 2, }; } if (bone.type === 'path') { const x = clientRect.x - this.parentClientRect.x; const y = clientRect.y - this.parentClientRect.y; let transform = `translate(${x}, ${y})`; if (bone.pathWidth && bone.pathHeight) { const xScale = (clientRect.width / bone.pathWidth).toFixed(2); const yScale = (clientRect.height / bone.pathHeight).toFixed(2); transform += ` scale(${xScale}, ${yScale})`; } return { type: 'path', width: clientRect.width, height: clientRect.height, transform, template: bone.template, }; } return { type: 'rect', x: clientRect.x - this.parentClientRect.x, y: clientRect.y - this.parentClientRect.y, width: clientRect.width, height: clientRect.height, rx: radius, ry: radius, }; }); } } SkltnComponent.decorators = [ { type: Component, args: [{ selector: 'skltn-root', template: "<div [innerHTML]=\"animationCss\"></div>\n\n<svg class=\"svg-root\" x=\"0px\" y=\"0px\" [attr.viewBox]=\"viewBox\" *ngIf=\"shapes?.length && showSkltn\">\n\n <!-- Gradient -->\n <linearGradient [attr.id]=\"gradientId\">\n <stop class=\"stop stop--1\" [attr.stop-color]=\"flareFill\" stop-opacity=\"0\" offset=\"0\"></stop>\n <stop class=\"stop stop--2\" [attr.stop-color]=\"flareFill\" stop-opacity=\"1\" offset=\"50%\"></stop>\n <stop class=\"stop stop--3\" [attr.stop-color]=\"flareFill\" stop-opacity=\"0\" offset=\"100%\"></stop>\n </linearGradient>\n\n <defs>\n <!-- Mask -->\n <mask [attr.id]=\"maskId\">\n <!-- Shapes -->\n <ng-container *ngFor=\"let shape of shapes\">\n <!-- Rect Type -->\n <rect *ngIf=\"shape.type === 'rect'\" [attr.x]=\"shape.x\" [attr.y]=\"shape.y\" [attr.rx]=\"shape.rx\"\n [attr.ry]=\"shape.ry\" [attr.width]=\"shape.width\" [attr.height]=\"shape.height\" fill=\"#FFFFFF\" />\n <!-- Circle Type -->\n <ellipse *ngIf=\"shape.type === 'circle'\" [attr.cx]=\"shape.cx\" [attr.cy]=\"shape.cy\" [attr.rx]=\"shape.rx\" [attr.ry]=\"shape.ry\" fill=\"#FFFFFF\"></ellipse>\n <!-- Path Type -->\n <ng-container *ngIf=\"shape.type === 'path'\">\n <g fill=\"#FFFFFF\" [attr.transform]=\"shape.transform\">\n <ng-container *ngTemplateOutlet=\"shape.template\"></ng-container>\n </g>\n </ng-container>\n </ng-container>\n </mask>\n </defs>\n\n <g [attr.mask]=\"'url(' + href + '#' + maskId + ')'\">\n <!-- Background Rectangle -->\n <rect class=\"bg-fill\" x=\"0\" y=\"0\" width=\"100%\" height=\"100%\" [attr.fill]=\"bgFill\"></rect>\n\n <!-- Flare -->\n <rect class=\"skltn-flare\" [attr.x]=\"'-' + flareWidth\" y=\"0\" [attr.width]=\"flareWidth\" height=\"100%\" [attr.fill]=\"'url(' + href + '#' + gradientId + ')'\"></rect>\n </g>\n\n</svg>\n\n<ng-content></ng-content>\n", providers: [{ provide: BonesList, useClass: BonesList, }], styles: [":host{display:block;position:relative}.svg-root{display:block;position:absolute;top:0;bottom:0;left:0;right:0;z-index:1}"] },] } ]; SkltnComponent.ctorParameters = () => [ { type: SkltnService }, { type: ElementRef }, { type: DomSanitizer }, { type: ChangeDetectorRef }, { type: NgZone }, { type: BonesList } ]; SkltnComponent.propDecorators = { rectRadius: [{ type: Input }], bgFill: [{ type: Input }], flareFill: [{ type: Input }], flareWidth: [{ type: Input }], duration: [{ type: Input }], delay: [{ type: Input }], timing: [{ type: Input }], showSkltn: [{ type: Input }], checkInterval: [{ type: Input }], onResize: [{ type: HostListener, args: ['window:resize', ['$event'],] }] }; class SkltnBoneDirective { constructor(element, bonesList) { this.element = element; this.bonesList = bonesList; } ngOnInit() { this.id = this.bonesList.add(this); } ngOnDestroy() { this.bonesList.remove(this.id); } } SkltnBoneDirective.decorators = [ { type: Directive, args: [{ selector: '[skltn-bone]', },] } ]; SkltnBoneDirective.ctorParameters = () => [ { type: ElementRef }, { type: BonesList } ]; SkltnBoneDirective.propDecorators = { type: [{ type: Input }], rectRadius: [{ type: Input }], pathWidth: [{ type: Input }], pathHeight: [{ type: Input }], template: [{ type: ContentChild, args: ['boneTemp', { static: true },] }] }; class NgxSkltnModule { static forRoot(config = {}) { return { ngModule: NgxSkltnModule, providers: [{ provide: SKLTN_CONFIG_TOKEN, useValue: config, }, { provide: SkltnService, useClass: SkltnService, deps: [SKLTN_CONFIG_TOKEN], }] }; } } NgxSkltnModule.decorators = [ { type: NgModule, args: [{ declarations: [ SkltnComponent, SkltnBoneDirective, ], imports: [ CommonModule, ], exports: [ SkltnComponent, SkltnBoneDirective, ] },] } ]; /* * Public API Surface of ngx-skltn */ /** * Generated bundle index. Do not edit. */ export { NgxSkltnModule, SKLTN_CONFIG_TOKEN, SkltnBoneDirective, SkltnComponent, SkltnService, BonesList as ɵa }; //# sourceMappingURL=ngx-skltn.js.map