From f76820edb0d2ca26c4159d6ab3aac52d42fc0c38 Mon Sep 17 00:00:00 2001 From: Markus Block Date: Tue, 16 Jul 2024 14:58:10 +0200 Subject: [PATCH] Created v1.0 --- README.md | 70 +++++++++--- package.json | 18 ++- src/lib/ngx-word-morph.component.html | 22 ++++ src/lib/ngx-word-morph.component.scss | 46 ++++++++ src/lib/ngx-word-morph.component.spec.ts | 23 ---- src/lib/ngx-word-morph.component.ts | 135 +++++++++++++++++++++-- src/lib/ngx-word-morph.service.spec.ts | 16 --- src/lib/ngx-word-morph.service.ts | 9 -- src/public-api.ts | 3 +- 9 files changed, 266 insertions(+), 76 deletions(-) create mode 100644 src/lib/ngx-word-morph.component.html create mode 100644 src/lib/ngx-word-morph.component.scss delete mode 100644 src/lib/ngx-word-morph.component.spec.ts delete mode 100644 src/lib/ngx-word-morph.service.spec.ts delete mode 100644 src/lib/ngx-word-morph.service.ts diff --git a/README.md b/README.md index 0bb6082..b975eea 100644 --- a/README.md +++ b/README.md @@ -1,24 +1,68 @@ -# NgxWordMorph +# Ngx Word-Morph Component -This library was generated with [Angular CLI](https://github.com/angular/angular-cli) version 17.3.0. +`@omnedia/ngx-word-morph` is an Angular library designed to facilitate word morphing animations within Angular applications. -## Code scaffolding +## Features +- Morph words within your Angular application. +- Easily customizable. -Run `ng generate component component-name --project ngx-word-morph` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module --project ngx-word-morph`. -> Note: Don't forget to add `--project ngx-word-morph` or else it will be added to the default project in your `angular.json` file. +## Installation -## Build +Install the library using npm: -Run `ng build ngx-word-morph` to build the project. The build artifacts will be stored in the `dist/` directory. +```bash +npm install @omnedia/ngx-word-morph +``` -## Publishing +## Usage -After building your library with `ng build ngx-word-morph`, go to the dist folder `cd dist/ngx-word-morph` and run `npm publish`. +Import the `NgxWordMorphComponent` in your Angular module: -## Running unit tests +```typescript +import { NgxWordMorphComponent } from '@omnedia/ngx-word-morph'; -Run `ng test ngx-word-morph` to execute the unit tests via [Karma](https://karma-runner.github.io). +@Component({ + ... + imports: [ + ... + NgxWordMorphComponent, + ], + ... +}) +``` -## Further help +Use the component in your template: -To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.io/cli) page. +```html + +``` + +## API + +```html + +``` + +Starts the word morphing effect. + +- `words`: An array of strings to be animated. +- `morphDuration`: (optional): The duration of the morphing animation in milliseconds. Default is 1000. +- `morphDelay`: (optional): The delay between morphing one word to the next in milliseconds. Default is 5000. +- `styleClass`: (optional): Add a class to the `
` wrapper tag. + +## Styling +If you want to style the text, do it globally via the `styleClass`.
+To change the font size make sure to change the `--om-word-morph-font-size` variable instead of directly changing the size. + +## Contributing + +Contributions are welcome. Please submit a pull request or open an issue to discuss your ideas. + +## License + +This project is licensed under the MIT License. \ No newline at end of file diff --git a/package.json b/package.json index 40165f6..50e0c92 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { - "name": "ngx-word-morph", - "version": "0.0.1", + "name": "@omnedia/ngx-word-morph", + "version": "1.0.0", "peerDependencies": { "@angular/common": "^17.3.0", "@angular/core": "^17.3.0" @@ -8,5 +8,17 @@ "dependencies": { "tslib": "^2.3.0" }, - "sideEffects": false + "sideEffects": false, + "keywords": [ + "rxjs", + "npm", + "morph", + "text", + "animation" + ], + "repository": { + "url": "https://github.com/omnedia/ngx-word-morph" + }, + "author": "Markus Block (markus.block@omnedia.com)", + "license": "MIT" } diff --git a/src/lib/ngx-word-morph.component.html b/src/lib/ngx-word-morph.component.html new file mode 100644 index 0000000..6afc3fd --- /dev/null +++ b/src/lib/ngx-word-morph.component.html @@ -0,0 +1,22 @@ +
+
+

+

+

{{ activeWord }}

+
+ + + + + + + + +
diff --git a/src/lib/ngx-word-morph.component.scss b/src/lib/ngx-word-morph.component.scss new file mode 100644 index 0000000..3b9b472 --- /dev/null +++ b/src/lib/ngx-word-morph.component.scss @@ -0,0 +1,46 @@ +.om-word-morph { + --om-word-morph-font-size: 8rem; + + width: fit-content; + position: relative; + + .words { + position: relative; + margin: auto; + width: fit-content; + height: fit-content; + top: 0; + bottom: 0; + + -webkit-transition: 1s -webkit-filter linear; + -moz-transition: 1s -moz-filter linear; + -moz-transition: 1s filter linear; + -ms-transition: 1s -ms-filter linear; + -o-transition: 1s -o-filter linear; + transition: 1s filter linear, 1s -webkit-filter linear; + + p { + margin: 0; + } + + .accessibility { + font-size: var(--om-word-morph-font-size); + visibility: hidden; + } + + #text1, + #text2 { + position: absolute; + width: 100%; + display: inline-block; + user-select: none; + font-size: var(--om-word-morph-font-size); + } + } + + #filters { + position: absolute; + width: 0; + height: 0; + } +} diff --git a/src/lib/ngx-word-morph.component.spec.ts b/src/lib/ngx-word-morph.component.spec.ts deleted file mode 100644 index 59156bc..0000000 --- a/src/lib/ngx-word-morph.component.spec.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { ComponentFixture, TestBed } from '@angular/core/testing'; - -import { NgxWordMorphComponent } from './ngx-word-morph.component'; - -describe('NgxWordMorphComponent', () => { - let component: NgxWordMorphComponent; - let fixture: ComponentFixture; - - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [NgxWordMorphComponent] - }) - .compileComponents(); - - fixture = TestBed.createComponent(NgxWordMorphComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/src/lib/ngx-word-morph.component.ts b/src/lib/ngx-word-morph.component.ts index 9d33029..772fb50 100644 --- a/src/lib/ngx-word-morph.component.ts +++ b/src/lib/ngx-word-morph.component.ts @@ -1,16 +1,131 @@ -import { Component } from '@angular/core'; +import { CommonModule } from "@angular/common"; +import { Component, Input, OnDestroy, OnInit } from "@angular/core"; +import { interval, of, repeat, Subject, takeUntil } from "rxjs"; @Component({ - selector: 'lib-ngx-word-morph', + selector: "om-word-morph", standalone: true, - imports: [], - template: ` -

- ngx-word-morph works! -

- `, - styles: `` + imports: [CommonModule], + templateUrl: "./ngx-word-morph.component.html", + styleUrl: "./ngx-word-morph.component.scss", }) -export class NgxWordMorphComponent { +export class NgxWordMorphComponent implements OnInit, OnDestroy { + @Input("styleClass") + styleClass?: string; + @Input("words") + set wordsInput(words: string[]) { + this.words = words; + this.textIndex = this.words.length - 1; + } + + @Input("morphDuration") + morphDuration = 1000; + + @Input("morphDelay") + morphDelay = 5000; + + words!: string[]; + activeWord!: string; + + private elts!: any; + + private fontSize!: number; + + private textIndex: number = 0; + + ngOnInit(): void { + if (!this.words || this.words.length <= 0) { + throw new Error("om-word-morph: No words were passed to the component!"); + } + + if (this.words.length === 1) { + this.words = [...this.words, ...this.words]; + } + + this.elts = { + words: document.getElementById("words") as HTMLElement, + text1: document.getElementById("text1") as HTMLElement, + text2: document.getElementById("text2") as HTMLElement, + }; + + this.fontSize = parseFloat( + window + .getComputedStyle(this.elts.text1, null) + .getPropertyValue("font-size") + ); + + this.activeWord = this.words[1]; + + this.initMorph(); + } + + destroy$ = new Subject(); + + ngOnDestroy(): void { + this.destroy$.next(); + this.destroy$.complete(); + } + + initMorph(): void { + this.elts.text2.style.filter = ""; + this.elts.text2.style.opacity = "100%"; + + this.elts.text1.style.filter = ""; + this.elts.text1.style.opacity = "0%"; + + this.morphText(); + + interval(this.morphDelay + this.morphDuration) + .pipe(takeUntil(this.destroy$)) + .subscribe(() => { + this.morphText(); + }); + } + + private morphText() { + let fraction = 0; + + this.elts.words.style.filter = "url(#threshold) blur(0.6px)"; + + this.elts.text1.textContent = + this.words[this.textIndex % this.words.length]; + this.elts.text2.textContent = + this.words[(this.textIndex + 1) % this.words.length]; + + of([]) + .pipe( + repeat({ count: 100, delay: this.morphDuration / 100 }), + takeUntil(this.destroy$) + ) + .subscribe(() => { + fraction += 1; + this.setMorph(fraction / 100); + + if (fraction === 50) { + this.activeWord = + this.words[(this.textIndex + 1) % this.words.length]; + } + + if (fraction === 100) { + this.textIndex++; + this.elts.words.style.filter = ""; + } + }); + } + + private setMorph(fraction: number) { + this.elts.text2.style.filter = `blur(${Math.min( + 8 / fraction - 8, + this.fontSize + )}px)`; + this.elts.text2.style.opacity = `${Math.pow(fraction, 0.4) * 100}%`; + + fraction = 1 - fraction; + this.elts.text1.style.filter = `blur(${Math.min( + 8 / fraction - 8, + this.fontSize + )}px)`; + this.elts.text1.style.opacity = `${Math.pow(fraction, 0.4) * 100}%`; + } } diff --git a/src/lib/ngx-word-morph.service.spec.ts b/src/lib/ngx-word-morph.service.spec.ts deleted file mode 100644 index c5e6a9f..0000000 --- a/src/lib/ngx-word-morph.service.spec.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { TestBed } from '@angular/core/testing'; - -import { NgxWordMorphService } from './ngx-word-morph.service'; - -describe('NgxWordMorphService', () => { - let service: NgxWordMorphService; - - beforeEach(() => { - TestBed.configureTestingModule({}); - service = TestBed.inject(NgxWordMorphService); - }); - - it('should be created', () => { - expect(service).toBeTruthy(); - }); -}); diff --git a/src/lib/ngx-word-morph.service.ts b/src/lib/ngx-word-morph.service.ts deleted file mode 100644 index 21672e7..0000000 --- a/src/lib/ngx-word-morph.service.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { Injectable } from '@angular/core'; - -@Injectable({ - providedIn: 'root' -}) -export class NgxWordMorphService { - - constructor() { } -} diff --git a/src/public-api.ts b/src/public-api.ts index 488fc0c..3611c50 100644 --- a/src/public-api.ts +++ b/src/public-api.ts @@ -2,5 +2,4 @@ * Public API Surface of ngx-word-morph */ -export * from './lib/ngx-word-morph.service'; -export * from './lib/ngx-word-morph.component'; +export * from "./lib/ngx-word-morph.component";