Merge pull request #5453 from mempool/mononaut/acceleration-sparkles
acceleration sparkles
This commit is contained in:
		
						commit
						5f6af83944
					
				| @ -0,0 +1,5 @@ | |||||||
|  | <div class="sparkles" #sparkleAnchor> | ||||||
|  |   <div *ngFor="let sparkle of sparkles" class="sparkle" [style]="sparkle.style"> | ||||||
|  |     <span class="inner-sparkle" [style]="sparkle.rotation">+</span> | ||||||
|  |   </div> | ||||||
|  | </div> | ||||||
| @ -0,0 +1,45 @@ | |||||||
|  | .sparkles { | ||||||
|  |   position: absolute; | ||||||
|  |   top: var(--block-size); | ||||||
|  |   height: 50px; | ||||||
|  |   right: 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .sparkle { | ||||||
|  |   position: absolute; | ||||||
|  |   color: rgba(152, 88, 255, 0.75); | ||||||
|  |   opacity: 0; | ||||||
|  |   transform: scale(0.8) rotate(0deg); | ||||||
|  |   animation: pop ease 2000ms forwards, sparkle ease 500ms infinite; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .inner-sparkle { | ||||||
|  |   display: block; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | @keyframes pop { | ||||||
|  |   0% { | ||||||
|  |     transform: scale(0.8) rotate(0deg); | ||||||
|  |     opacity: 0; | ||||||
|  |   } | ||||||
|  |   20% { | ||||||
|  |     transform: scale(1) rotate(72deg); | ||||||
|  |     opacity: 1; | ||||||
|  |   } | ||||||
|  |   100% { | ||||||
|  |     transform: scale(0) rotate(360deg); | ||||||
|  |     opacity: 0; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | @keyframes sparkle { | ||||||
|  |   0% { | ||||||
|  |     color: rgba(152, 88, 255, 0.75); | ||||||
|  |   } | ||||||
|  |   50% { | ||||||
|  |     color: rgba(198, 162, 255, 0.75); | ||||||
|  |   } | ||||||
|  |   100% { | ||||||
|  |     color: rgba(152, 88, 255, 0.75); | ||||||
|  |   } | ||||||
|  | } | ||||||
| @ -0,0 +1,73 @@ | |||||||
|  | import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, Input, OnChanges, SimpleChanges, ViewChild } from '@angular/core'; | ||||||
|  | 
 | ||||||
|  | @Component({ | ||||||
|  |   selector: 'app-acceleration-sparkles', | ||||||
|  |   templateUrl: './acceleration-sparkles.component.html', | ||||||
|  |   styleUrls: ['./acceleration-sparkles.component.scss'], | ||||||
|  |   changeDetection: ChangeDetectionStrategy.OnPush, | ||||||
|  | }) | ||||||
|  | export class AccelerationSparklesComponent implements OnChanges { | ||||||
|  |   @Input() arrow: ElementRef<HTMLDivElement>; | ||||||
|  |   @Input() run: boolean = false; | ||||||
|  | 
 | ||||||
|  |   @ViewChild('sparkleAnchor') | ||||||
|  |   sparkleAnchor: ElementRef<HTMLDivElement>; | ||||||
|  | 
 | ||||||
|  |   constructor( | ||||||
|  |     private cd: ChangeDetectorRef, | ||||||
|  |   ) {} | ||||||
|  | 
 | ||||||
|  |   endTimeout: any; | ||||||
|  |   lastSparkle: number = 0; | ||||||
|  |   sparkleWidth: number = 0; | ||||||
|  |   sparkles: any[] = []; | ||||||
|  | 
 | ||||||
|  |   ngOnChanges(changes: SimpleChanges): void { | ||||||
|  |     if (changes.run) { | ||||||
|  |       if (this.endTimeout) { | ||||||
|  |         clearTimeout(this.endTimeout); | ||||||
|  |         this.endTimeout = null; | ||||||
|  |       } | ||||||
|  |       if (this.run) { | ||||||
|  |         this.doSparkle(); | ||||||
|  |       } else { | ||||||
|  |         this.endTimeout = setTimeout(() => { | ||||||
|  |           this.sparkles = []; | ||||||
|  |         }, 2000); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   doSparkle(): void { | ||||||
|  |     if (this.run) { | ||||||
|  |       const now = performance.now(); | ||||||
|  |       if (now - this.lastSparkle > 20) { | ||||||
|  |         this.lastSparkle = now; | ||||||
|  |         if (this.arrow?.nativeElement && this.sparkleAnchor?.nativeElement) { | ||||||
|  |           const anchor = this.sparkleAnchor.nativeElement.getBoundingClientRect().right; | ||||||
|  |           const right = this.arrow.nativeElement.getBoundingClientRect().right; | ||||||
|  |           const dx = (anchor - right) + 30; | ||||||
|  |           const numSparkles = Math.ceil(Math.random() * 3); | ||||||
|  |           for (let i = 0; i < numSparkles; i++) { | ||||||
|  |             this.sparkles.push({ | ||||||
|  |               style: { | ||||||
|  |                 right: (dx + (Math.random() * 10)) + 'px', | ||||||
|  |                 top: (15 + (Math.random() * 30)) + 'px', | ||||||
|  |               }, | ||||||
|  |               rotation: { | ||||||
|  |                 transform: `rotate(${Math.random() * 360}deg)`, | ||||||
|  |               } | ||||||
|  |             }); | ||||||
|  |           } | ||||||
|  |           while (this.sparkles.length > 200) { | ||||||
|  |             this.sparkles.shift(); | ||||||
|  |           } | ||||||
|  |           this.cd.markForCheck(); | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |       requestAnimationFrame(() => { | ||||||
|  |         this.doSparkle(); | ||||||
|  |       }); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
| @ -51,7 +51,8 @@ | |||||||
|         </div> |         </div> | ||||||
|       </ng-template> |       </ng-template> | ||||||
|     </div> |     </div> | ||||||
|     <div *ngIf="arrowVisible" id="arrow-up" [ngStyle]="{'right': rightPosition + (blockWidth * 0.3) + containerOffset + 'px', transition: transition }" [class.blink]="txPosition?.accelerated"></div> |     <app-acceleration-sparkles [style]="{ position: 'absolute', right: 0}" [arrow]="arrowElement" [run]="acceleratingArrow"></app-acceleration-sparkles> | ||||||
|  |     <div *ngIf="arrowVisible" #arrowUp id="arrow-up" [ngStyle]="{'right': rightPosition + (blockWidth * 0.3) + containerOffset + 'px', transition: transition }" [class.blink]="txPosition?.accelerated"></div> | ||||||
|   </div> |   </div> | ||||||
| </ng-container> | </ng-container> | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,4 +1,4 @@ | |||||||
| import { Component, OnInit, OnDestroy, ChangeDetectionStrategy, ChangeDetectorRef, HostListener, Input, OnChanges, SimpleChanges, Output, EventEmitter } from '@angular/core'; | import { Component, OnInit, OnDestroy, ChangeDetectionStrategy, ChangeDetectorRef, HostListener, Input, OnChanges, SimpleChanges, Output, EventEmitter, ViewChild, ElementRef } from '@angular/core'; | ||||||
| import { Subscription, Observable, of, combineLatest } from 'rxjs'; | import { Subscription, Observable, of, combineLatest } from 'rxjs'; | ||||||
| import { MempoolBlock } from '../../interfaces/websocket.interface'; | import { MempoolBlock } from '../../interfaces/websocket.interface'; | ||||||
| import { StateService } from '../../services/state.service'; | import { StateService } from '../../services/state.service'; | ||||||
| @ -77,6 +77,9 @@ export class MempoolBlocksComponent implements OnInit, OnChanges, OnDestroy { | |||||||
|   maxArrowPosition = 0; |   maxArrowPosition = 0; | ||||||
|   rightPosition = 0; |   rightPosition = 0; | ||||||
|   transition = 'background 2s, right 2s, transform 1s'; |   transition = 'background 2s, right 2s, transform 1s'; | ||||||
|  |   @ViewChild('arrowUp') | ||||||
|  |   arrowElement: ElementRef<HTMLDivElement>; | ||||||
|  |   acceleratingArrow: boolean = false; | ||||||
| 
 | 
 | ||||||
|   markIndex: number; |   markIndex: number; | ||||||
|   txPosition: MempoolPosition; |   txPosition: MempoolPosition; | ||||||
| @ -201,6 +204,7 @@ export class MempoolBlocksComponent implements OnInit, OnChanges, OnDestroy { | |||||||
| 
 | 
 | ||||||
|     this.markBlocksSubscription = this.stateService.markBlock$ |     this.markBlocksSubscription = this.stateService.markBlock$ | ||||||
|       .subscribe((state) => { |       .subscribe((state) => { | ||||||
|  |         const oldTxPosition = this.txPosition; | ||||||
|         this.markIndex = undefined; |         this.markIndex = undefined; | ||||||
|         this.txPosition = undefined; |         this.txPosition = undefined; | ||||||
|         this.txFeePerVSize = undefined; |         this.txFeePerVSize = undefined; | ||||||
| @ -209,6 +213,12 @@ export class MempoolBlocksComponent implements OnInit, OnChanges, OnDestroy { | |||||||
|         } |         } | ||||||
|         if (state.mempoolPosition) { |         if (state.mempoolPosition) { | ||||||
|           this.txPosition = state.mempoolPosition; |           this.txPosition = state.mempoolPosition; | ||||||
|  |           if (this.txPosition.accelerated && !oldTxPosition.accelerated) { | ||||||
|  |             this.acceleratingArrow = true; | ||||||
|  |             setTimeout(() => { | ||||||
|  |               this.acceleratingArrow = false; | ||||||
|  |             }, 2000); | ||||||
|  |           } | ||||||
|         } |         } | ||||||
|         if (state.txFeePerVSize) { |         if (state.txFeePerVSize) { | ||||||
|           this.txFeePerVSize = state.txFeePerVSize; |           this.txFeePerVSize = state.txFeePerVSize; | ||||||
|  | |||||||
| @ -100,6 +100,7 @@ import { MempoolErrorComponent } from './components/mempool-error/mempool-error. | |||||||
| import { AccelerationsListComponent } from '../components/acceleration/accelerations-list/accelerations-list.component'; | import { AccelerationsListComponent } from '../components/acceleration/accelerations-list/accelerations-list.component'; | ||||||
| import { PendingStatsComponent } from '../components/acceleration/pending-stats/pending-stats.component'; | import { PendingStatsComponent } from '../components/acceleration/pending-stats/pending-stats.component'; | ||||||
| import { AccelerationStatsComponent } from '../components/acceleration/acceleration-stats/acceleration-stats.component'; | import { AccelerationStatsComponent } from '../components/acceleration/acceleration-stats/acceleration-stats.component'; | ||||||
|  | import { AccelerationSparklesComponent } from '../components/acceleration/sparkles/acceleration-sparkles.component'; | ||||||
| 
 | 
 | ||||||
| import { BlockViewComponent } from '../components/block-view/block-view.component'; | import { BlockViewComponent } from '../components/block-view/block-view.component'; | ||||||
| import { EightBlocksComponent } from '../components/eight-blocks/eight-blocks.component'; | import { EightBlocksComponent } from '../components/eight-blocks/eight-blocks.component'; | ||||||
| @ -225,6 +226,7 @@ import { OnlyVsizeDirective, OnlyWeightDirective } from './components/weight-dir | |||||||
|     AccelerationsListComponent, |     AccelerationsListComponent, | ||||||
|     AccelerationStatsComponent, |     AccelerationStatsComponent, | ||||||
|     PendingStatsComponent, |     PendingStatsComponent, | ||||||
|  |     AccelerationSparklesComponent, | ||||||
|     HttpErrorComponent, |     HttpErrorComponent, | ||||||
|     TwitterWidgetComponent, |     TwitterWidgetComponent, | ||||||
|     FaucetComponent, |     FaucetComponent, | ||||||
| @ -355,6 +357,7 @@ import { OnlyVsizeDirective, OnlyWeightDirective } from './components/weight-dir | |||||||
|     AccelerationsListComponent, |     AccelerationsListComponent, | ||||||
|     AccelerationStatsComponent, |     AccelerationStatsComponent, | ||||||
|     PendingStatsComponent, |     PendingStatsComponent, | ||||||
|  |     AccelerationSparklesComponent, | ||||||
|     HttpErrorComponent, |     HttpErrorComponent, | ||||||
|     TwitterWidgetComponent, |     TwitterWidgetComponent, | ||||||
|     TwitterLogin, |     TwitterLogin, | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user