@@ -318,9 +318,8 @@ module.exports = {
318
318
buildSavantSection ( value , metricSummaries ) +
319
319
'<h3>Hitting</h3>' +
320
320
buildSavantSection ( hitting , metricSummaries ) +
321
- '<h3>Fielding</h3>' +
322
- buildSavantSection ( fielding , metricSummaries ) +
323
- ( statcast . blocks_above_average !== null ? '<h3>Catching</h3>' + buildSavantSection ( catching , metricSummaries ) : '' ) +
321
+ ( fielding . find ( stat => stat . value !== null ) ? '<h3>Fielding</h3>' + buildSavantSection ( fielding , metricSummaries ) : '' ) +
322
+ ( catching . find ( stat => stat . value !== null ) ? '<h3>Catching</h3>' + buildSavantSection ( catching , metricSummaries ) : '' ) +
324
323
'<h3>Running</h3>' +
325
324
buildSavantSection ( running , metricSummaries ) +
326
325
'</div>' ;
@@ -352,9 +351,9 @@ module.exports = {
352
351
const html = `
353
352
<div id='savant-table'>` +
354
353
'<h3>Value</h3>' +
355
- buildSavantSection ( value , metricSummaries ) +
354
+ buildSavantSection ( value , metricSummaries , true ) +
356
355
'<h3>Pitching</h3>' +
357
- buildSavantSection ( pitching , metricSummaries ) +
356
+ buildSavantSection ( pitching , metricSummaries , true ) +
358
357
'</div>' ;
359
358
360
359
return ( await getScreenshotOfSavantTable ( html ) ) ;
@@ -699,20 +698,26 @@ async function getScreenshotOfSavantTable (savantHTML) {
699
698
#savant-table {
700
699
background-color: #151820;
701
700
color: whitesmoke;
702
- padding: 15px;
703
- font-size: 20px;
701
+ font-size: 25px;
704
702
font-family: 'Segoe UI', sans-serif;
705
- width: 40%;
703
+ width: 65%;
704
+ display: flex;
705
+ padding: 17px 27.5px 17px 10px;
706
+ flex-direction: column;
707
+ align-items: center;
706
708
}
707
709
.savant-stat {
708
710
display: flex;
709
- width: 100 %;
711
+ width: 95 %;
710
712
justify-content: space-between;
711
713
margin: 5px 0;
712
714
align-items: center;
713
715
}
716
+ .savant-stat-pitcher {
717
+ margin: 12px 0;
718
+ }
714
719
h3 {
715
- font-size: 22px ;
720
+ font-size: 30px ;
716
721
font-weight: bold;
717
722
width: 100%;
718
723
text-align: center;
@@ -722,46 +727,48 @@ async function getScreenshotOfSavantTable (savantHTML) {
722
727
margin: 10px 0;
723
728
}
724
729
.percentile {
725
- width: 28px ;
726
- height: 28px ;
730
+ width: 35px ;
731
+ height: 35px ;
727
732
font-size: 0.7em;
728
733
display: flex;
729
734
align-items: center;
730
735
justify-content: center;
731
736
font-weight: bold;
732
- border-radius: 50%
737
+ border-radius: 50%;
738
+ position: absolute;
739
+ top: 50%;
740
+ left: -20px;
741
+ transform: translateY(-50%);
742
+ }
743
+ .percentile-slider-not-qualified {
744
+ background-image: repeating-linear-gradient(
745
+ -45deg,
746
+ transparent,
747
+ transparent 3px,
748
+ rgba(0, 0, 0, 0.95) 3px,
749
+ rgba(0, 0, 0, 0.95) 6px
750
+ );
733
751
}
734
752
.percentile-not-qualified {
735
- background-image: linear-gradient(
736
- -45deg,
737
- rgba(0,0,0,0.95) 10%,
738
- transparent 10%,
739
- transparent 20%,
740
- rgba(0,0,0,0.95) 20%,
741
- rgba(0,0,0,0.95) 30%,
742
- transparent 30%,
743
- transparent 40%,
744
- rgba(0,0,0,0.95) 40%,
745
- rgba(0,0,0,0.95) 50%,
746
- transparent 50%,
747
- transparent 60%,
748
- rgba(0,0,0,0.95) 60%,
749
- rgba(0,0,0,0.95) 70%,
750
- transparent 70%,
751
- transparent 80%,
752
- rgba(0,0,0,0.95) 80%,
753
- rgba(0,0,0,0.95) 90%,
754
- transparent 90%,
755
- transparent 100%
756
- );
757
- background-size: 0.42em;
753
+ display: none;
758
754
}
759
755
.stat-values {
760
756
display: flex;
761
- width: 5em;
757
+ width: 8. 5em;
762
758
justify-content: space-between;
763
759
align-items: center;
764
760
}
761
+ .percentile-slider {
762
+ position: relative;
763
+ width: 150px;
764
+ height: 0.75em;
765
+ background: #80808045;
766
+ }
767
+ .percentile-slider-portion {
768
+ position: absolute;
769
+ width: 100%;
770
+ height: 100%;
771
+ }
765
772
</style>` +
766
773
savantHTML
767
774
) ;
@@ -773,27 +780,38 @@ async function getScreenshotOfSavantTable (savantHTML) {
773
780
return buffer ;
774
781
}
775
782
776
- function buildSavantSection ( statCollection , metricSummaries ) {
783
+ function buildSavantSection ( statCollection , metricSummaries , isPitcher = false ) {
777
784
const scale = chroma . scale ( [ '#325aa1' , '#a8c1c3' , '#c91f26' ] ) ;
785
+ const sliderScale = chroma . scale ( [ '#3661ad' , '#b4cfd1' , '#d8221f' ] ) ;
786
+ statCollection . forEach ( stat => {
787
+ if ( ! stat . percentile ) {
788
+ stat . percentile = calculateRoundedPercentileFromNormalDistribution (
789
+ stat . metric ,
790
+ stat . value ,
791
+ metricSummaries [ stat . metric ] . avg_metric ,
792
+ metricSummaries [ stat . metric ] . stddev_metric ,
793
+ stat . shouldInvert
794
+ ) ;
795
+ stat . isQualified = false ;
796
+ } else {
797
+ stat . isQualified = true ;
798
+ }
799
+ } ) ;
778
800
return statCollection . reduce ( ( acc , value ) => acc + ( value . value !== null
779
801
? `
780
- <div class='savant-stat'>
781
- <div class='label'>${ value . label } </div>
782
- <div class='stat-values'>
783
- <div class='value'>${ value . value } </div>
784
- <div class='percentile ${ value . percentile ? '' : 'percentile-not-qualified' } ' style='background-color: ${ value . percentile
785
- ? scale ( value . percentile / 100 )
786
- : scale ( caculateRoundedPercentileFromNormalDistribution (
787
- value . metric ,
788
- value . value ,
789
- metricSummaries [ value . metric ] . avg_metric ,
790
- metricSummaries [ value . metric ] . stddev_metric ,
791
- value . shouldInvert
792
- ) ) } '>${ value . percentile || ' ' }
793
- </div>
802
+ <div class='savant-stat${ ( isPitcher ? ' savant-stat-pitcher' : '' ) } '>
803
+ <div class='label'>${ value . label } </div>
804
+ <div class='stat-values'>
805
+ <div class='value'>${ value . value } </div>
806
+ <div class='percentile-slider'>
807
+ <div class='percentile-slider-portion ${ value . isQualified ? '' : 'percentile-slider-not-qualified' } '
808
+ style='background-color: ${ sliderScale ( value . percentile / 100 ) } ; width: ${ ( value . percentile / 100 ) * 150 } px'></div>
809
+ <div class='percentile ${ value . isQualified ? '' : 'percentile-not-qualified' } '
810
+ style='background-color: ${ scale ( value . percentile / 100 ) } ; left: ${ - 17.5 + ( value . percentile / 100 ) * 150 } px '>${ value . percentile || ' ' }
794
811
</div>
795
812
</div>
796
- `
813
+ </div>
814
+ </div>`
797
815
: '' ) , '' ) ;
798
816
}
799
817
@@ -842,7 +860,9 @@ async function getScreenshotOfLineScore (tables, inning, half, awayScore, homeSc
842
860
return buffer ;
843
861
}
844
862
845
- function caculateRoundedPercentileFromNormalDistribution ( metric , value , mean , standardDeviation , shouldInvert ) {
863
+ function calculateRoundedPercentileFromNormalDistribution ( metric , value , mean , standardDeviation , shouldInvert ) {
846
864
if ( typeof value === 'string' ) { value = parseFloat ( value ) ; }
847
- return shouldInvert ? ( 1.00 - ztable ( ( value - mean ) / standardDeviation ) ) : ztable ( ( value - mean ) / standardDeviation ) ;
865
+ return Math . round (
866
+ ( shouldInvert ? ( 1.00 - ztable ( ( value - mean ) / standardDeviation ) ) : ztable ( ( value - mean ) / standardDeviation ) ) * 100
867
+ ) ;
848
868
}
0 commit comments