Merge pull request #3621 from mempool/mononaut/sharper-blocks
Pixel-aligned grids for sharper block visualizations
This commit is contained in:
		
						commit
						0ec98d03e5
					
				| @ -1,15 +1,17 @@ | ||||
| <div class="block-overview-graph"> | ||||
|   <canvas class="block-overview-canvas" [class.clickable]="!!hoverTx" #blockCanvas></canvas> | ||||
|   <div class="loader-wrapper" [class.hidden]="(!isLoading || disableSpinner) && !unavailable"> | ||||
|     <div *ngIf="isLoading" class="spinner-border ml-3 loading" role="status"></div> | ||||
|     <div *ngIf="!isLoading && unavailable" class="ml-3" i18n="block.not-available">not available</div> | ||||
|   </div> | ||||
| 
 | ||||
|   <app-block-overview-tooltip | ||||
|     [tx]="selectedTx || hoverTx" | ||||
|     [cursorPosition]="tooltipPosition" | ||||
|     [clickable]="!!selectedTx" | ||||
|     [auditEnabled]="auditHighlighting" | ||||
|     [blockConversion]="blockConversion" | ||||
|   ></app-block-overview-tooltip> | ||||
| <div class="grid-align" [style.gridTemplateColumns]="'repeat(auto-fit, ' + resolution + 'px)'"> | ||||
|   <div class="block-overview-graph"> | ||||
|     <canvas class="block-overview-canvas" [class.clickable]="!!hoverTx" #blockCanvas></canvas> | ||||
|     <div class="loader-wrapper" [class.hidden]="(!isLoading || disableSpinner) && !unavailable"> | ||||
|       <div *ngIf="isLoading" class="spinner-border ml-3 loading" role="status"></div> | ||||
|       <div *ngIf="!isLoading && unavailable" class="ml-3" i18n="block.not-available">not available</div> | ||||
|     </div> | ||||
|     <app-block-overview-tooltip | ||||
|       [tx]="selectedTx || hoverTx" | ||||
|       [cursorPosition]="tooltipPosition" | ||||
|       [clickable]="!!selectedTx" | ||||
|       [auditEnabled]="auditHighlighting" | ||||
|       [blockConversion]="blockConversion" | ||||
|     ></app-block-overview-tooltip> | ||||
|   </div> | ||||
| </div> | ||||
|  | ||||
| @ -6,8 +6,16 @@ | ||||
|   display: flex; | ||||
|   justify-content: center; | ||||
|   align-items: center; | ||||
|   grid-column: 1/-1; | ||||
| } | ||||
| 
 | ||||
| .grid-align { | ||||
|   position: relative; | ||||
|   width: 100%; | ||||
|   display: grid; | ||||
|   grid-template-columns: repeat(auto-fit, 75px); | ||||
|   justify-content: center; | ||||
| } | ||||
| 
 | ||||
| .block-overview-canvas { | ||||
|   position: absolute; | ||||
|  | ||||
| @ -25,7 +25,6 @@ export class BlockOverviewGraphComponent implements AfterViewInit, OnDestroy, On | ||||
|   @Input() unavailable: boolean = false; | ||||
|   @Input() auditHighlighting: boolean = false; | ||||
|   @Input() blockConversion: Price; | ||||
|   @Input() pixelAlign: boolean = false; | ||||
|   @Output() txClickEvent = new EventEmitter<{ tx: TransactionStripped, keyModifier: boolean}>(); | ||||
|   @Output() txHoverEvent = new EventEmitter<string>(); | ||||
|   @Output() readyEvent = new EventEmitter(); | ||||
| @ -219,7 +218,7 @@ export class BlockOverviewGraphComponent implements AfterViewInit, OnDestroy, On | ||||
|     } else { | ||||
|       this.scene = new BlockScene({ width: this.displayWidth, height: this.displayHeight, resolution: this.resolution, | ||||
|         blockLimit: this.blockLimit, orientation: this.orientation, flip: this.flip, vertexArray: this.vertexArray, | ||||
|         highlighting: this.auditHighlighting, pixelAlign: this.pixelAlign }); | ||||
|         highlighting: this.auditHighlighting }); | ||||
|       this.start(); | ||||
|     } | ||||
|   } | ||||
|  | ||||
| @ -15,7 +15,6 @@ export default class BlockScene { | ||||
|   gridWidth: number; | ||||
|   gridHeight: number; | ||||
|   gridSize: number; | ||||
|   pixelAlign: boolean; | ||||
|   vbytesPerUnit: number; | ||||
|   unitPadding: number; | ||||
|   unitWidth: number; | ||||
| @ -24,24 +23,19 @@ export default class BlockScene { | ||||
|   animateUntil = 0; | ||||
|   dirty: boolean; | ||||
| 
 | ||||
|   constructor({ width, height, resolution, blockLimit, orientation, flip, vertexArray, highlighting, pixelAlign }: | ||||
|   constructor({ width, height, resolution, blockLimit, orientation, flip, vertexArray, highlighting }: | ||||
|       { width: number, height: number, resolution: number, blockLimit: number, | ||||
|         orientation: string, flip: boolean, vertexArray: FastVertexArray, highlighting: boolean, pixelAlign: boolean } | ||||
|         orientation: string, flip: boolean, vertexArray: FastVertexArray, highlighting: boolean } | ||||
|   ) { | ||||
|     this.init({ width, height, resolution, blockLimit, orientation, flip, vertexArray, highlighting, pixelAlign }); | ||||
|     this.init({ width, height, resolution, blockLimit, orientation, flip, vertexArray, highlighting }); | ||||
|   } | ||||
| 
 | ||||
|   resize({ width = this.width, height = this.height, animate = true }: { width?: number, height?: number, animate: boolean }): void { | ||||
|     this.width = width; | ||||
|     this.height = height; | ||||
|     this.gridSize = this.width / this.gridWidth; | ||||
|     if (this.pixelAlign) { | ||||
|       this.unitPadding =  Math.max(1, Math.floor(this.gridSize / 2.5)); | ||||
|       this.unitWidth = this.gridSize - (this.unitPadding); | ||||
|     } else { | ||||
|       this.unitPadding =  width / 500; | ||||
|       this.unitWidth = this.gridSize - (this.unitPadding * 2); | ||||
|     } | ||||
|     this.unitPadding =  Math.max(1, Math.floor(this.gridSize / 5)); | ||||
|     this.unitWidth = this.gridSize - (this.unitPadding * 2); | ||||
| 
 | ||||
|     this.dirty = true; | ||||
|     if (this.initialised && this.scene) { | ||||
| @ -219,15 +213,14 @@ export default class BlockScene { | ||||
|     this.animateUntil = Math.max(this.animateUntil, tx.setHighlight(value)); | ||||
|   } | ||||
| 
 | ||||
|   private init({ width, height, resolution, blockLimit, orientation, flip, vertexArray, highlighting, pixelAlign }: | ||||
|   private init({ width, height, resolution, blockLimit, orientation, flip, vertexArray, highlighting }: | ||||
|       { width: number, height: number, resolution: number, blockLimit: number, | ||||
|         orientation: string, flip: boolean, vertexArray: FastVertexArray, highlighting: boolean, pixelAlign: boolean } | ||||
|         orientation: string, flip: boolean, vertexArray: FastVertexArray, highlighting: boolean } | ||||
|   ): void { | ||||
|     this.orientation = orientation; | ||||
|     this.flip = flip; | ||||
|     this.vertexArray = vertexArray; | ||||
|     this.highlightingEnabled = highlighting; | ||||
|     this.pixelAlign = pixelAlign; | ||||
| 
 | ||||
|     this.scene = { | ||||
|       count: 0, | ||||
| @ -353,12 +346,7 @@ export default class BlockScene { | ||||
|   private gridToScreen(position: Square | void): Square { | ||||
|     if (position) { | ||||
|       const slotSize = (position.s * this.gridSize); | ||||
|       let squareSize; | ||||
|       if (this.pixelAlign) { | ||||
|         squareSize = slotSize - (this.unitPadding); | ||||
|       } else { | ||||
|         squareSize = slotSize - (this.unitPadding * 2); | ||||
|       } | ||||
|       const squareSize = slotSize - (this.unitPadding * 2); | ||||
| 
 | ||||
|       // The grid is laid out notionally left-to-right, bottom-to-top,
 | ||||
|       // so we rotate and/or flip the y axis to match the target configuration.
 | ||||
| @ -434,7 +422,7 @@ export default class BlockScene { | ||||
| 
 | ||||
|   // calculates and returns the size of the tx in multiples of the grid size
 | ||||
|   private txSize(tx: TxView): number { | ||||
|     const scale = Math.max(1, Math.round(Math.sqrt(tx.vsize / this.vbytesPerUnit))); | ||||
|     const scale = Math.max(1, Math.round(Math.sqrt(1.1 * tx.vsize / this.vbytesPerUnit))); | ||||
|     return Math.min(this.gridWidth, Math.max(1, scale)); // bound between 1 and the max displayable size (just in case!)
 | ||||
|   } | ||||
| 
 | ||||
|  | ||||
| @ -71,7 +71,7 @@ | ||||
|       <app-block-overview-graph | ||||
|         #blockGraph | ||||
|         [isLoading]="false" | ||||
|         [resolution]="75" | ||||
|         [resolution]="80" | ||||
|         [blockLimit]="stateService.blockVSize" | ||||
|         [orientation]="'top'" | ||||
|         [flip]="false" | ||||
|  | ||||
| @ -52,8 +52,8 @@ | ||||
| .chart-container { | ||||
|   flex-grow: 0; | ||||
|   flex-shrink: 0; | ||||
|   width: 470px; | ||||
|   min-width: 470px; | ||||
|   width: 480px; | ||||
|   min-width: 480px; | ||||
|   padding: 0; | ||||
|   margin-right: 15px; | ||||
| } | ||||
|  | ||||
| @ -100,7 +100,7 @@ | ||||
|           </tbody> | ||||
|         </table> | ||||
|       </div> | ||||
|       <div class="col-sm"> | ||||
|       <div class="col-sm" [class.graph-col]="webGlEnabled && !showAudit"> | ||||
|         <table class="table table-borderless table-striped" *ngIf="!isMobile && !(webGlEnabled && !showAudit)"> | ||||
|           <tbody> | ||||
|             <ng-container *ngTemplateOutlet="restOfTable"></ng-container> | ||||
| @ -110,7 +110,7 @@ | ||||
|           <app-block-overview-graph | ||||
|             #blockGraphActual | ||||
|             [isLoading]="isLoadingOverview" | ||||
|             [resolution]="75" | ||||
|             [resolution]="86" | ||||
|             [blockLimit]="stateService.blockVSize" | ||||
|             [orientation]="'top'" | ||||
|             [flip]="false" | ||||
| @ -227,7 +227,7 @@ | ||||
|       <div class="col-sm"> | ||||
|         <h3 class="block-subtitle" *ngIf="!isMobile"><ng-container i18n="block.expected-block">Expected Block</ng-container> <span class="badge badge-pill badge-warning beta" i18n="beta">beta</span></h3> | ||||
|         <div class="block-graph-wrapper"> | ||||
|           <app-block-overview-graph #blockGraphProjected [isLoading]="isLoadingOverview" [resolution]="75" | ||||
|           <app-block-overview-graph #blockGraphProjected [isLoading]="isLoadingOverview" [resolution]="86" | ||||
|             [blockLimit]="stateService.blockVSize" [orientation]="'top'" [flip]="false" [mirrorTxid]="hoverTx" [auditHighlighting]="showAudit" | ||||
|             (txClickEvent)="onTxClick($event)" (txHoverEvent)="onTxHover($event)" [unavailable]="!isMobile && !showAudit"></app-block-overview-graph> | ||||
|           <ng-container *ngIf="!isMobile || mode !== 'actual'; else emptyBlockInfo"></ng-container> | ||||
| @ -239,7 +239,7 @@ | ||||
|       <div class="col-sm" *ngIf="!isMobile"> | ||||
|         <h3 class="block-subtitle actual" *ngIf="!isMobile"><ng-container i18n="block.actual-block">Actual Block</ng-container> <a class="info-link" [routerLink]="['/docs/faq' | relativeUrl ]" fragment="how-do-block-audits-work"><fa-icon [icon]="['fas', 'info-circle']" [fixedWidth]="true"></fa-icon></a></h3> | ||||
|         <div class="block-graph-wrapper"> | ||||
|           <app-block-overview-graph #blockGraphActual [isLoading]="isLoadingOverview" [resolution]="75" | ||||
|           <app-block-overview-graph #blockGraphActual [isLoading]="isLoadingOverview" [resolution]="86" | ||||
|             [blockLimit]="stateService.blockVSize" [orientation]="'top'" [flip]="false" [mirrorTxid]="hoverTx" mode="mined"  [auditHighlighting]="showAudit" | ||||
|             (txClickEvent)="onTxClick($event)" (txHoverEvent)="onTxHover($event)" [unavailable]="isMobile && !showAudit"></app-block-overview-graph> | ||||
|           <ng-container *ngTemplateOutlet="emptyBlockInfo"></ng-container> | ||||
|  | ||||
| @ -239,6 +239,7 @@ h1 { | ||||
| .nav-tabs { | ||||
|   border-color: white; | ||||
|   border-width: 1px; | ||||
|   margin-bottom: 1em; | ||||
| } | ||||
| 
 | ||||
| .nav-tabs .nav-link { | ||||
| @ -293,3 +294,7 @@ h1 { | ||||
|     margin-top: 0.75rem; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| .graph-col { | ||||
|   flex-grow: 1.11; | ||||
| } | ||||
|  | ||||
| @ -25,7 +25,7 @@ | ||||
|           </ng-container> | ||||
|           <ng-template #mempoolMode> | ||||
|             <div class="block-sizer" [style]="blockSizerStyle"> | ||||
|               <app-mempool-block-overview [index]="blockIndex" [pixelAlign]="true"></app-mempool-block-overview> | ||||
|               <app-mempool-block-overview [index]="blockIndex"></app-mempool-block-overview> | ||||
|             </div> | ||||
|           </ng-template> | ||||
|           <div class="fader"></div> | ||||
|  | ||||
| @ -1,10 +1,9 @@ | ||||
| <app-block-overview-graph | ||||
|   #blockGraph | ||||
|   [isLoading]="isLoading$ | async" | ||||
|   [resolution]="75" | ||||
|   [resolution]="86" | ||||
|   [blockLimit]="stateService.blockVSize" | ||||
|   [orientation]="timeLtr ? 'right' : 'left'" | ||||
|   [flip]="true" | ||||
|   [pixelAlign]="pixelAlign" | ||||
|   (txClickEvent)="onTxClick($event)" | ||||
| ></app-block-overview-graph> | ||||
|  | ||||
| @ -16,7 +16,6 @@ import { Router } from '@angular/router'; | ||||
| }) | ||||
| export class MempoolBlockOverviewComponent implements OnInit, OnDestroy, OnChanges, AfterViewInit { | ||||
|   @Input() index: number; | ||||
|   @Input() pixelAlign: boolean = false; | ||||
|   @Output() txPreviewEvent = new EventEmitter<TransactionStripped | void>(); | ||||
| 
 | ||||
|   @ViewChild('blockGraph') blockGraph: BlockOverviewGraphComponent; | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user