From 32bf30872dea540d5d2718c34c4cbb1498ecb4a7 Mon Sep 17 00:00:00 2001 From: Mononaut Date: Tue, 27 Dec 2022 11:33:08 -0600 Subject: [PATCH] improve block scrolling & new block animation --- .../blockchain-blocks.component.html | 30 ++-- .../blockchain-blocks.component.scss | 14 ++ .../blockchain-blocks.component.ts | 96 ++++++------ .../blockchain/blockchain.component.ts | 4 +- .../app/components/start/start.component.ts | 148 +++++++++++------- .../src/app/interfaces/node-api.interface.ts | 2 - 6 files changed, 180 insertions(+), 114 deletions(-) diff --git a/frontend/src/app/components/blockchain-blocks/blockchain-blocks.component.html b/frontend/src/app/components/blockchain-blocks/blockchain-blocks.component.html index 9a305833f..b27dab69d 100644 --- a/frontend/src/app/components/blockchain-blocks/blockchain-blocks.component.html +++ b/frontend/src/app/components/blockchain-blocks/blockchain-blocks.component.html @@ -1,6 +1,6 @@
-
- +
+
  @@ -11,8 +11,11 @@
~{{ block?.extras?.medianFee | number:feeRounding }} sat/vB
-
- {{ block?.extras?.feeRange[1] | number:feeRounding }} - {{ block?.extras?.feeRange[block?.extras?.feeRange.length - 1] | number:feeRounding }} sat/vB +
+ {{ block?.extras?.feeRange?.[1] | number:feeRounding }} - {{ block?.extras?.feeRange[block?.extras?.feeRange?.length - 1] | number:feeRounding }} sat/vB +
+
+  
@@ -31,16 +34,19 @@
+ + +
+ +
+
+
-
-   -
- {{ block.height }} + +
+
-
- loading -
-
+
diff --git a/frontend/src/app/components/blockchain-blocks/blockchain-blocks.component.scss b/frontend/src/app/components/blockchain-blocks/blockchain-blocks.component.scss index adde4a945..64bfd2379 100644 --- a/frontend/src/app/components/blockchain-blocks/blockchain-blocks.component.scss +++ b/frontend/src/app/components/blockchain-blocks/blockchain-blocks.component.scss @@ -25,6 +25,10 @@ transition: background 2s, left 2s, transform 1s; } +.mined-block.placeholder-block { + background: none !important; +} + .block-size { font-size: 16px; font-weight: bold; @@ -96,6 +100,16 @@ transform-origin: top; } +.bitcoin-block.placeholder-block::after { + content: none; + background: 0; +} + +.bitcoin-block.placeholder-block::before { + content: none; + background: 0; +} + .black-background { background-color: #11131f; z-index: 100; diff --git a/frontend/src/app/components/blockchain-blocks/blockchain-blocks.component.ts b/frontend/src/app/components/blockchain-blocks/blockchain-blocks.component.ts index 8972afcae..df583b0af 100644 --- a/frontend/src/app/components/blockchain-blocks/blockchain-blocks.component.ts +++ b/frontend/src/app/components/blockchain-blocks/blockchain-blocks.component.ts @@ -8,6 +8,7 @@ import { config } from 'process'; import { CacheService } from 'src/app/services/cache.service'; interface BlockchainBlock extends BlockExtended { + placeholder?: boolean; loading?: boolean; } @@ -102,10 +103,6 @@ export class BlockchainBlocksComponent implements OnInit, OnChanges, OnDestroy { this.blocks.unshift(block); this.blocks = this.blocks.slice(0, this.stateService.env.KEEP_BLOCKS_AMOUNT); - if (this.blocksFilled && !this.tabHidden && block.extras) { - block.extras.stage = block.extras.matchRate >= 66 ? 1 : 2; - } - if (txConfirmed) { this.markHeight = block.height; this.moveArrowToPosition(true, true); @@ -114,12 +111,16 @@ export class BlockchainBlocksComponent implements OnInit, OnChanges, OnDestroy { } this.blockStyles = []; - this.blocks.forEach((b, i) => this.blockStyles.push(this.getStyleForBlock(b, i))); - setTimeout(() => { - this.blockStyles = []; + if (this.blocksFilled) { + this.blocks.forEach((b, i) => this.blockStyles.push(this.getStyleForBlock(b, i, i ? -155 : -205))); + setTimeout(() => { + this.blockStyles = []; + this.blocks.forEach((b, i) => this.blockStyles.push(this.getStyleForBlock(b, i))); + this.cd.markForCheck(); + }, 50); + } else { this.blocks.forEach((b, i) => this.blockStyles.push(this.getStyleForBlock(b, i))); - this.cd.markForCheck(); - }, 50); + } if (this.blocks.length === this.stateService.env.KEEP_BLOCKS_AMOUNT) { this.blocksFilled = true; @@ -210,31 +211,34 @@ export class BlockchainBlocksComponent implements OnInit, OnChanges, OnDestroy { // reset blocks this.blocks = []; this.blockStyles = []; - while (this.blocks.length < Math.min(this.height + 1, this.count)) { + while (this.blocks.length < this.count) { const height = this.height - this.blocks.length; + let block; if (height >= 0) { this.cacheService.loadBlock(height); - const block = this.cacheService.getCachedBlock(height) || null; - this.blocks.push(block || { - loading: true, - id: '', - height, - version: 0, - timestamp: 0, - bits: 0, - nonce: 0, - difficulty: 0, - merkle_root: '', - tx_count: 0, - size: 0, - weight: 0, - previousblockhash: '', - }); + block = this.cacheService.getCachedBlock(height) || null; } + this.blocks.push(block || { + placeholder: height < 0, + loading: height >= 0, + id: '', + height, + version: 0, + timestamp: 0, + bits: 0, + nonce: 0, + difficulty: 0, + merkle_root: '', + tx_count: 0, + size: 0, + weight: 0, + previousblockhash: '', + }); } this.blocks = this.blocks.slice(0, this.count); this.blockStyles = []; - this.blocks.forEach((b, i) => this.blockStyles.push(this.getStyleForBlock(b, i, animateSlide))); + this.blocks.forEach((b, i) => this.blockStyles.push(this.getStyleForBlock(b, i, animateSlide ? -155 : 0))); + this.cd.markForCheck(); if (animateSlide) { // animate blocks slide right setTimeout(() => { @@ -242,6 +246,9 @@ export class BlockchainBlocksComponent implements OnInit, OnChanges, OnDestroy { this.blocks.forEach((b, i) => this.blockStyles.push(this.getStyleForBlock(b, i))); this.cd.markForCheck(); }, 50); + this.moveArrowToPosition(true, true); + } else { + this.moveArrowToPosition(false, false); } } @@ -254,18 +261,17 @@ export class BlockchainBlocksComponent implements OnInit, OnChanges, OnDestroy { this.cd.markForCheck(); } - getStyleForBlock(block: BlockchainBlock, index: number, animateSlideStart: boolean = false) { - if (!block || block.loading) { - return this.getStyleForLoadingBlock(index, animateSlideStart); + getStyleForBlock(block: BlockchainBlock, index: number, animateEnterFrom: number = 0) { + if (!block || block.placeholder) { + return this.getStyleForPlaceholderBlock(index, animateEnterFrom); + } else if (block.loading) { + return this.getStyleForLoadingBlock(index, animateEnterFrom); } const greenBackgroundHeight = 100 - (block.weight / this.stateService.env.BLOCK_WEIGHT_UNITS) * 100; let addLeft = 0; - if (animateSlideStart) { - if (block?.extras) { - block.extras.stage = 2; - } - addLeft = -205; + if (animateEnterFrom) { + addLeft = animateEnterFrom || 0; } return { @@ -276,11 +282,12 @@ export class BlockchainBlocksComponent implements OnInit, OnChanges, OnDestroy { ${this.gradientColors[this.network][0]} ${Math.max(greenBackgroundHeight, 0)}%, ${this.gradientColors[this.network][1]} 100% )`, + transition: animateEnterFrom ? 'background 2s, transform 1s' : null, }; } - getStyleForLoadingBlock(index: number, animateSlideStart: boolean = false) { - const addLeft = animateSlideStart ? -205 : 0; + getStyleForLoadingBlock(index: number, animateEnterFrom: number = 0) { + const addLeft = animateEnterFrom || 0; return { left: addLeft + (155 * index) + 'px', @@ -288,13 +295,15 @@ export class BlockchainBlocksComponent implements OnInit, OnChanges, OnDestroy { }; } - getStyleForEmptyBlock(block: BlockExtended) { - let addLeft = 0; + getStyleForPlaceholderBlock(index: number, animateEnterFrom: number = 0) { + const addLeft = animateEnterFrom || 0; + return { + left: addLeft + (155 * index) + 'px', + }; + } - if (block?.extras?.stage === 1) { - block.extras.stage = 2; - addLeft = -205; - } + getStyleForEmptyBlock(block: BlockExtended, animateEnterFrom: number = 0) { + const addLeft = animateEnterFrom || 0; return { left: addLeft + 155 * this.emptyBlocks.indexOf(block) + 'px', @@ -319,7 +328,6 @@ export class BlockchainBlocksComponent implements OnInit, OnChanges, OnDestroy { weight: 0, previousblockhash: '', matchRate: 0, - stage: 0, }); } return emptyBlocks; diff --git a/frontend/src/app/components/blockchain/blockchain.component.ts b/frontend/src/app/components/blockchain/blockchain.component.ts index c25ebdafd..cd09b7430 100644 --- a/frontend/src/app/components/blockchain/blockchain.component.ts +++ b/frontend/src/app/components/blockchain/blockchain.component.ts @@ -33,8 +33,8 @@ export class BlockchainComponent implements OnInit, OnDestroy { this.timeLtrSubscription.unsubscribe(); } - trackByPageFn(index: number, item: { height: number }) { - return item.height; + trackByPageFn(index: number, item: { index: number }) { + return item.index; } toggleTimeDirection() { diff --git a/frontend/src/app/components/start/start.component.ts b/frontend/src/app/components/start/start.component.ts index ca8c2a47c..d4b3a6f67 100644 --- a/frontend/src/app/components/start/start.component.ts +++ b/frontend/src/app/components/start/start.component.ts @@ -47,7 +47,7 @@ export class StartComponent implements OnInit, OnDestroy { this.updatePages(); }); this.markBlockSubscription = this.stateService.markBlock$.subscribe((mark) => { - if (mark?.blockHeight != null) { + if (mark?.blockHeight != null && !this.blockInViewport(mark.blockHeight)) { this.scrollToBlock(mark.blockHeight); } }); @@ -82,15 +82,17 @@ export class StartComponent implements OnInit, OnDestroy { this.isMobile = window.innerWidth <= 767.98; let firstVisibleBlock; let offset; - this.pages.forEach(page => { - const left = page.offset - (this.blockchainContainer?.nativeElement?.scrollLeft || 0); - const right = left + this.pageWidth; - if (left <= 0 && right > 0) { - const blockIndex = Math.max(0, Math.floor(left / -this.blockWidth)); - firstVisibleBlock = page.height - blockIndex; - offset = left + (blockIndex * this.blockWidth); - } - }); + if (this.blockchainContainer?.nativeElement != null) { + this.pages.forEach(page => { + const left = page.offset - this.getConvertedScrollOffset(); + const right = left + this.pageWidth; + if (left <= 0 && right > 0) { + const blockIndex = Math.max(0, Math.floor(left / -this.blockWidth)); + firstVisibleBlock = page.height - blockIndex; + offset = left + (blockIndex * this.blockWidth); + } + }); + } this.blocksPerPage = Math.ceil(window.innerWidth / this.blockWidth); this.pageWidth = this.blocksPerPage * this.blockWidth; @@ -132,25 +134,16 @@ export class StartComponent implements OnInit, OnDestroy { const translation = (this.isMobile ? window.innerWidth * 0.95 : window.innerWidth * 0.5); const backThreshold = middlePage.offset + (this.pageWidth * 0.5) + translation; const forwardThreshold = middlePage.offset - (this.pageWidth * 0.5) + translation; - if (this.timeLtr) { - if (e.target.scrollLeft < -backThreshold) { - if (this.shiftPagesBack()) { - e.target.scrollLeft += this.pageWidth; - } - } else if (e.target.scrollLeft > -forwardThreshold) { - if (this.shiftPagesForward()) { - e.target.scrollLeft -= this.pageWidth; - } + const scrollLeft = this.getConvertedScrollOffset(); + if (scrollLeft > backThreshold) { + if (this.shiftPagesBack()) { + this.addConvertedScrollOffset(-this.pageWidth); + this.blockchainScrollLeftInit -= this.pageWidth; } - } else { - if (e.target.scrollLeft > backThreshold) { - if (this.shiftPagesBack()) { - e.target.scrollLeft -= this.pageWidth; - } - } else if (e.target.scrollLeft < forwardThreshold) { - if (this.shiftPagesForward()) { - e.target.scrollLeft += this.pageWidth; - } + } else if (scrollLeft < forwardThreshold) { + if (this.shiftPagesForward()) { + this.addConvertedScrollOffset(this.pageWidth); + this.blockchainScrollLeftInit += this.pageWidth; } } } @@ -160,27 +153,39 @@ export class StartComponent implements OnInit, OnDestroy { setTimeout(() => { this.scrollToBlock(height, blockOffset); }, 50); return; } - let targetHeight = this.isMobile ? height - 1 : height; - const middlePageIndex = this.getPageIndexOf(targetHeight); + const targetHeight = this.isMobile ? height - 1 : height; + const viewingPageIndex = this.getPageIndexOf(targetHeight); const pages = []; - if (middlePageIndex > 0) { - this.pageIndex = middlePageIndex - 1; - const middlePage = this.getPageAt(middlePageIndex); - const left = middlePage.offset - this.blockchainContainer.nativeElement.scrollLeft; - const blockIndex = middlePage.height - targetHeight; - const targetOffset = (this.blockWidth * blockIndex) + left; - const deltaOffset = targetOffset - blockOffset; - if (this.pageIndex > 0) { - pages.push(this.getPageAt(this.pageIndex)); - } - pages.push(middlePage); - pages.push(this.getPageAt(middlePageIndex + 1)); - this.pages = pages; - this.blockchainContainer.nativeElement.scrollLeft += deltaOffset; - } else { - this.pageIndex = 0; - this.updatePages(); + this.pageIndex = Math.max(viewingPageIndex - 1, 0); + let viewingPage = this.getPageAt(viewingPageIndex); + const isLastPage = viewingPage.height < this.blocksPerPage; + if (isLastPage) { + this.pageIndex = Math.max(viewingPageIndex - 2, 0); + viewingPage = this.getPageAt(viewingPageIndex); } + const left = viewingPage.offset - this.getConvertedScrollOffset(); + const blockIndex = viewingPage.height - targetHeight; + const targetOffset = (this.blockWidth * blockIndex) + left; + let deltaOffset = targetOffset - blockOffset; + + if (isLastPage) { + pages.push(this.getPageAt(viewingPageIndex - 2)); + } + if (viewingPageIndex > 1) { + pages.push(this.getPageAt(viewingPageIndex - 1)); + } + if (viewingPageIndex > 0) { + pages.push(viewingPage); + } + if (!isLastPage) { + pages.push(this.getPageAt(viewingPageIndex + 1)); + } + if (viewingPageIndex === 0) { + pages.push(this.getPageAt(viewingPageIndex + 2)); + } + + this.pages = pages; + this.addConvertedScrollOffset(deltaOffset); } updatePages() { @@ -194,13 +199,18 @@ export class StartComponent implements OnInit, OnDestroy { } shiftPagesBack(): boolean { - this.pageIndex++; - this.pages.forEach(page => page.offset -= this.pageWidth); - if (this.pageIndex !== 1) { - this.pages.shift(); + const nextPage = this.getPageAt(this.pageIndex + 3); + if (nextPage.height >= 0) { + this.pageIndex++; + this.pages.forEach(page => page.offset -= this.pageWidth); + if (this.pageIndex !== 1) { + this.pages.shift(); + } + this.pages.push(this.getPageAt(this.pageIndex + 2)); + return true; + } else { + return false; } - this.pages.push(this.getPageAt(this.pageIndex + 2)); - return true; } shiftPagesForward(): boolean { @@ -217,9 +227,12 @@ export class StartComponent implements OnInit, OnDestroy { } getPageAt(index: number) { + const height = this.chainTip - 8 - ((index - 1) * this.blocksPerPage) return { offset: this.firstPageWidth + (this.pageWidth * (index - 1 - this.pageIndex)), - height: this.chainTip - 8 - ((index - 1) * this.blocksPerPage), + height: height, + depth: this.chainTip - height, + index: index, }; } @@ -228,6 +241,33 @@ export class StartComponent implements OnInit, OnDestroy { return Math.max(0, Math.floor(delta / this.blocksPerPage) + 1); } + blockInViewport(height: number): boolean { + const firstHeight = this.pages[0].height; + const translation = (this.isMobile ? window.innerWidth * 0.95 : window.innerWidth * 0.5); + const firstX = this.pages[0].offset - this.getConvertedScrollOffset() + translation; + const xPos = firstX + ((firstHeight - height) * 155); + return xPos > -55 && xPos < (window.innerWidth - 100); + } + + getConvertedScrollOffset(): number { + if (this.timeLtr) { + return -this.blockchainContainer?.nativeElement?.scrollLeft || 0; + } else { + return this.blockchainContainer?.nativeElement?.scrollLeft || 0; + } + } + + addConvertedScrollOffset(offset: number): void { + if (!this.blockchainContainer?.nativeElement) { + return; + } + if (this.timeLtr) { + this.blockchainContainer.nativeElement.scrollLeft -= offset; + } else { + this.blockchainContainer.nativeElement.scrollLeft += offset; + } + } + ngOnDestroy() { this.timeLtrSubscription.unsubscribe(); } diff --git a/frontend/src/app/interfaces/node-api.interface.ts b/frontend/src/app/interfaces/node-api.interface.ts index 2e6b94988..b0045218e 100644 --- a/frontend/src/app/interfaces/node-api.interface.ts +++ b/frontend/src/app/interfaces/node-api.interface.ts @@ -122,8 +122,6 @@ export interface BlockExtension { name: string; slug: string; } - - stage?: number; // Frontend only } export interface BlockExtended extends Block {