@luceosports/play-rendering 2.5.5 → 2.5.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/play-rendering.js +4 -4
- package/dist/play-rendering.js.map +1 -1
- package/dist/types/models/AnimationModel.d.ts +1 -0
- package/dist/types/models/LineModel.d.ts +1 -0
- package/dist/types/models/PlayModel.d.ts +4 -0
- package/package.json +1 -1
- package/src/assets/balls/baseball.svg +2 -0
- package/src/assets/balls/basketball.svg +2 -0
- package/src/assets/balls/football.svg +2 -0
- package/src/assets/balls/hockey.svg +13 -0
- package/src/assets/balls/lacrosse.svg +21 -0
- package/src/assets/balls/soccer.svg +34 -0
- package/src/assets/balls/volleyball.svg +37 -0
- package/src/ballConfig.ts +44 -0
- package/src/helpers/draw.ts +47 -0
- package/src/layers/LineLayer.ts +1 -0
- package/src/layers/PlayerLayer.ts +27 -1
- package/src/layers/line/base/InternalLineLayer.ts +17 -2
- package/src/layers/line/layers/DribbleLineLayer.ts +13 -3
- package/src/layers/line/layers/ShotLineLayer.ts +13 -2
- package/src/models/AnimationModel.ts +19 -0
- package/src/models/FrameModel.ts +15 -3
- package/src/models/LineModel.ts +12 -1
- package/src/models/Play/Options.ts +4 -0
- package/src/models/PlayModel.ts +9 -0
- package/src/traits/LineDrawOperationsTrait.ts +58 -3
|
@@ -10,6 +10,7 @@ export class AnimationModel {
|
|
|
10
10
|
max: number;
|
|
11
11
|
}>;
|
|
12
12
|
get lastLineAnimationMax(): number;
|
|
13
|
+
setOptions(nextOptions: PlayModel['options']): this;
|
|
13
14
|
start(finishCallback: () => void, progressCallback: (progress: number) => void): void;
|
|
14
15
|
pause(): void;
|
|
15
16
|
setProgress(progress: number): this;
|
|
@@ -33,6 +33,10 @@ export type PlayModelOptions = {
|
|
|
33
33
|
playersMap: PlayersMapItem[];
|
|
34
34
|
labelsOverrideType: 'Initials' | 'Jersey number' | 'Headshot' | null;
|
|
35
35
|
inDrawingState: boolean;
|
|
36
|
+
showBallMode: boolean;
|
|
37
|
+
// sub-options for showBallMode
|
|
38
|
+
highlightPlayerPuck: boolean;
|
|
39
|
+
showPassLinesDuringPlayback: boolean;
|
|
36
40
|
}
|
|
37
41
|
|
|
38
42
|
export type TeamPlayer = {
|
package/package.json
CHANGED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="utf-8"?>
|
|
2
|
+
<svg fill="#000000" width="800px" height="800px" viewBox="-8 0 512 512" xmlns="http://www.w3.org/2000/svg"><path d="M368.5 363.9l28.8-13.9c11.1 22.9 26 43.2 44.1 60.9 34-42.5 54.5-96.3 54.5-154.9 0-58.5-20.4-112.2-54.2-154.6-17.8 17.3-32.6 37.1-43.6 59.5l-28.7-14.1c12.8-26 30-49 50.8-69C375.6 34.7 315 8 248 8 181.1 8 120.5 34.6 75.9 77.7c20.7 19.9 37.9 42.9 50.7 68.8l-28.7 14.1c-11-22.3-25.7-42.1-43.5-59.4C20.4 143.7 0 197.4 0 256c0 58.6 20.4 112.3 54.4 154.7 18.2-17.7 33.2-38 44.3-61l28.8 13.9c-12.9 26.7-30.3 50.3-51.5 70.7 44.5 43.1 105.1 69.7 172 69.7 66.8 0 127.3-26.5 171.9-69.5-21.1-20.4-38.5-43.9-51.4-70.6zm-228.3-32l-30.5-9.8c14.9-46.4 12.7-93.8-.6-134l30.4-10c15 45.6 18 99.9.7 153.8zm216.3-153.4l30.4 10c-13.2 40.1-15.5 87.5-.6 134l-30.5 9.8c-17.3-54-14.3-108.3.7-153.8z"/></svg>
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="utf-8"?>
|
|
2
|
+
<svg width="800px" height="800px" viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg"><path fill="#000000" d="M248.37 41.094c-49.643 1.754-98.788 20.64-137.89 56.656L210.53 197.8c31.283-35.635 45.59-88.686 37.84-156.706zm18.126.107c7.646 71.205-7.793 129.56-43.223 169.345L256 243.27 401.52 97.75c-38.35-35.324-86.358-54.18-135.024-56.55zM97.75 110.48c-36.017 39.102-54.902 88.247-56.656 137.89 68.02 7.75 121.07-6.557 156.707-37.84L97.75 110.48zm316.5 0L268.73 256l32.71 32.71c33.815-30.112 81.05-45.78 138.183-45.11 10.088.118 20.49.753 31.176 1.9-2.37-48.665-21.227-96.672-56.55-135.02zM210.545 223.272c-39.785 35.43-98.14 50.87-169.344 43.223 2.37 48.666 21.226 96.675 56.55 135.025L243.27 256l-32.725-32.727zm225.002 38.27c-51.25.042-92.143 14.29-121.348 39.928l100.05 100.05c36.017-39.102 54.902-88.247 56.656-137.89-12.275-1.4-24.074-2.096-35.36-2.087zM256 268.73L110.48 414.25c38.35 35.324 86.358 54.18 135.024 56.55-7.646-71.205 7.793-129.56 43.223-169.345L256 268.73zm45.47 45.47c-31.283 35.635-45.59 88.686-37.84 156.706 49.643-1.754 98.788-20.64 137.89-56.656L301.47 314.2z"/></svg>
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="utf-8"?>
|
|
2
|
+
<svg fill="#000000" width="800px" height="800px" viewBox="-8 0 512 512" xmlns="http://www.w3.org/2000/svg"><path d="M481.5 60.3c-4.8-18.2-19.1-32.5-37.3-37.4C420.3 16.5 383 8.9 339.4 8L496 164.8c-.8-43.5-8.2-80.6-14.5-104.5zm-467 391.4c4.8 18.2 19.1 32.5 37.3 37.4 23.9 6.4 61.2 14 104.8 14.9L0 347.2c.8 43.5 8.2 80.6 14.5 104.5zM4.2 283.4L220.4 500c132.5-19.4 248.8-118.7 271.5-271.4L275.6 12C143.1 31.4 26.8 130.7 4.2 283.4zm317.3-123.6c3.1-3.1 8.2-3.1 11.3 0l11.3 11.3c3.1 3.1 3.1 8.2 0 11.3l-28.3 28.3 28.3 28.3c3.1 3.1 3.1 8.2 0 11.3l-11.3 11.3c-3.1 3.1-8.2 3.1-11.3 0l-28.3-28.3-22.6 22.7 28.3 28.3c3.1 3.1 3.1 8.2 0 11.3l-11.3 11.3c-3.1 3.1-8.2 3.1-11.3 0L248 278.6l-22.6 22.6 28.3 28.3c3.1 3.1 3.1 8.2 0 11.3l-11.3 11.3c-3.1 3.1-8.2 3.1-11.3 0l-28.3-28.3-28.3 28.3c-3.1 3.1-8.2 3.1-11.3 0l-11.3-11.3c-3.1-3.1-3.1-8.2 0-11.3l28.3-28.3-28.3-28.2c-3.1-3.1-3.1-8.2 0-11.3l11.3-11.3c3.1-3.1 8.2-3.1 11.3 0l28.3 28.3 22.6-22.6-28.3-28.3c-3.1-3.1-3.1-8.2 0-11.3l11.3-11.3c3.1-3.1 8.2-3.1 11.3 0l28.3 28.3 22.6-22.6-28.3-28.3c-3.1-3.1-3.1-8.2 0-11.3l11.3-11.3c3.1-3.1 8.2-3.1 11.3 0l28.3 28.3 28.3-28.5z"/></svg>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="iso-8859-1"?>
|
|
2
|
+
<svg height="800px" width="800px" version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
|
|
3
|
+
viewBox="0 0 32.96 32.96" xml:space="preserve">
|
|
4
|
+
<g>
|
|
5
|
+
<g>
|
|
6
|
+
<g>
|
|
7
|
+
<path style="fill:#010002;" d="M16.48,18.084c-9.101,0-16.479-2.964-16.479-6.622v10.742h0.05c0,3.916,7.357,7.093,16.429,7.093
|
|
8
|
+
c9.077,0,16.432-3.177,16.432-7.093h0.048V11.462C32.961,15.12,25.582,18.084,16.48,18.084z"/>
|
|
9
|
+
<ellipse style="fill:#010002;" cx="16.48" cy="10.285" rx="16.48" ry="6.622"/>
|
|
10
|
+
</g>
|
|
11
|
+
</g>
|
|
12
|
+
</g>
|
|
13
|
+
</svg>
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="iso-8859-1"?>
|
|
2
|
+
<svg height="800px" width="800px" version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
|
|
3
|
+
viewBox="0 0 512 512" xml:space="preserve">
|
|
4
|
+
<path style="fill:#A0A0A0;" d="M512,256c0,141.384-114.616,256-256,256S0,397.384,0,256S114.616,0,256,0S512,114.616,512,256z"/>
|
|
5
|
+
<path style="fill:#868686;" d="M512,256c0,141.384-114.615,256-256,256C132.098,512,28.761,423.976,5.098,307.054
|
|
6
|
+
c39.175,56.575,104.53,93.641,178.554,93.641c119.87,0,217.043-97.174,217.043-217.043c0-74.024-37.067-139.38-93.641-178.554
|
|
7
|
+
C423.976,28.761,512,132.098,512,256z"/>
|
|
8
|
+
<path style="fill:#FFFFFF;" d="M464.696,256v0.493c-0.017,4.6-3.751,8.319-8.348,8.319c-0.01,0-0.02,0-0.03,0
|
|
9
|
+
c-4.61-0.017-8.334-3.767-8.319-8.377V256c0-4.61,3.738-8.348,8.348-8.348S464.696,251.39,464.696,256z M454.922,281.456
|
|
10
|
+
c-4.548-0.767-8.854,2.292-9.623,6.837C429.629,380.833,350.019,448,256,448c-4.61,0-8.348,3.738-8.348,8.348
|
|
11
|
+
s3.738,8.348,8.348,8.348c49.603,0,97.675-17.703,135.363-49.849c37.264-31.784,62.264-75.739,70.396-123.767
|
|
12
|
+
C462.529,286.534,459.468,282.226,454.922,281.456z"/>
|
|
13
|
+
<path style="fill:#AAAAAA;" d="M244.87,161.391c0,52.251-42.358,94.609-94.609,94.609s-94.609-42.358-94.609-94.609
|
|
14
|
+
s42.358-94.609,94.609-94.609S244.87,109.141,244.87,161.391z"/>
|
|
15
|
+
<path style="fill:#757575;" d="M166.957,244.862c0-10.725,7.637-19.959,18.187-21.889c20.537-3.757,44.826-5.929,70.856-5.929
|
|
16
|
+
s50.32,2.173,70.856,5.93c10.551,1.93,18.187,11.163,18.187,21.889v22.275c0,10.725-7.637,19.959-18.187,21.889
|
|
17
|
+
c-20.537,3.757-44.826,5.929-70.856,5.929s-50.32-2.173-70.856-5.93c-10.551-1.93-18.187-11.163-18.187-21.889V244.862z"/>
|
|
18
|
+
<path style="fill:#939393;" d="M309.258,239.617c7.236,1.509,12.632,8.234,13.525,16.383c-0.893,8.149-6.29,14.874-13.525,16.383
|
|
19
|
+
c-15.436,3.22-33.692,5.083-53.258,5.083s-37.821-1.862-53.258-5.083c-7.236-1.509-12.632-8.234-13.525-16.383
|
|
20
|
+
c0.893-8.149,6.29-14.874,13.525-16.383c15.436-3.22,33.692-5.083,53.258-5.083S293.821,236.397,309.258,239.617z"/>
|
|
21
|
+
</svg>
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="iso-8859-1"?>
|
|
2
|
+
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
|
3
|
+
<svg fill="#000000" version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
|
|
4
|
+
width="800px" height="800px" viewBox="0 0 72.371 72.372"
|
|
5
|
+
xml:space="preserve">
|
|
6
|
+
<g>
|
|
7
|
+
<path d="M22.57,2.648c-4.489,1.82-8.517,4.496-11.971,7.949C7.144,14.051,4.471,18.08,2.65,22.568C0.892,26.904,0,31.486,0,36.186
|
|
8
|
+
c0,4.699,0.892,9.281,2.65,13.615c1.821,4.489,4.495,8.518,7.949,11.971c3.454,3.455,7.481,6.129,11.971,7.949
|
|
9
|
+
c4.336,1.76,8.917,2.649,13.617,2.649c4.7,0,9.28-0.892,13.616-2.649c4.488-1.82,8.518-4.494,11.971-7.949
|
|
10
|
+
c3.455-3.453,6.129-7.48,7.949-11.971c1.758-4.334,2.648-8.916,2.648-13.615c0-4.7-0.891-9.282-2.648-13.618
|
|
11
|
+
c-1.82-4.488-4.496-8.518-7.949-11.971s-7.479-6.129-11.971-7.949C45.467,0.891,40.887,0,36.187,0
|
|
12
|
+
C31.487,0,26.906,0.891,22.57,2.648z M9.044,51.419c-1.743-1.094-3.349-2.354-4.771-3.838c-2.172-6.112-2.54-12.729-1.101-19.01
|
|
13
|
+
c0.677-1.335,1.447-2.617,2.318-3.845c0.269-0.379,0.518-0.774,0.806-1.142l8.166,4.832c0,0.064,0,0.134,0,0.205
|
|
14
|
+
c-0.021,4.392,0.425,8.752,1.313,13.049c0.003,0.02,0.006,0.031,0.01,0.049l-6.333,9.93C9.314,51.579,9.177,51.503,9.044,51.419z
|
|
15
|
+
M33.324,68.206c1.409,0.719,2.858,1.326,4.347,1.82c-6.325,0.275-12.713-1.207-18.36-4.447L33,68.018
|
|
16
|
+
C33.105,68.085,33.212,68.149,33.324,68.206z M33.274,65.735L17.12,62.856c-1.89-2.295-3.59-4.723-5.051-7.318
|
|
17
|
+
c-0.372-0.66-0.787-1.301-1.102-1.99l6.327-9.92c0.14,0.035,0.296,0.072,0.473,0.119c3.958,1.059,7.986,1.812,12.042,2.402
|
|
18
|
+
c0.237,0.033,0.435,0.062,0.604,0.08l7.584,13.113c-1.316,1.85-2.647,3.69-4.007,5.51C33.764,65.155,33.524,65.446,33.274,65.735z
|
|
19
|
+
M60.15,60.149c-1.286,1.287-2.651,2.447-4.08,3.481c-0.237-1.894-0.646-3.75-1.223-5.563l8.092-15.096
|
|
20
|
+
c2.229-1.015,4.379-2.166,6.375-3.593c0.261-0.185,0.478-0.392,0.646-0.618C69.374,46.561,66.104,54.196,60.15,60.149z
|
|
21
|
+
M59.791,40.571c0.301,0.574,0.598,1.154,0.896,1.742l-7.816,14.58c-0.045,0.01-0.088,0.02-0.133,0.026
|
|
22
|
+
c-4.225,0.789-8.484,1.209-12.779,1.229l-7.8-13.487c1.214-2.254,2.417-4.517,3.61-6.781c0.81-1.536,1.606-3.082,2.401-4.627
|
|
23
|
+
l16.143-1.658C56.29,34.495,58.163,37.457,59.791,40.571z M56.516,23.277c-0.766,2.023-1.586,4.025-2.401,6.031l-15.726,1.615
|
|
24
|
+
c-0.188-0.248-0.383-0.492-0.588-0.725c-1.857-2.103-3.726-4.193-5.592-6.289c0.017-0.021,0.034-0.037,0.051-0.056
|
|
25
|
+
c-0.753-0.752-1.508-1.504-2.261-2.258l4.378-13.181c0.302-0.08,0.606-0.147,0.913-0.18c2.38-0.242,4.763-0.516,7.149-0.654
|
|
26
|
+
c1.461-0.082,2.93-0.129,4.416-0.024l10.832,12.209C57.314,20.943,56.95,22.124,56.516,23.277z M60.15,12.221
|
|
27
|
+
c2.988,2.99,5.302,6.402,6.938,10.047c-2.024-1.393-4.188-2.539-6.463-3.473c-0.354-0.146-0.717-0.275-1.086-0.402L48.877,6.376
|
|
28
|
+
c0.074-0.519,0.113-1.039,0.129-1.563C53.062,6.464,56.864,8.936,60.15,12.221z M25.334,4.182c0.042,0.031,0.062,0.057,0.086,0.064
|
|
29
|
+
c2.437,0.842,4.654,2.082,6.744,3.553l-4.09,12.317c-0.021,0.006-0.041,0.012-0.061,0.021c-0.837,0.346-1.69,0.656-2.514,1.031
|
|
30
|
+
c-3.395,1.543-6.705,3.252-9.823,5.301l-8.071-4.775c0.012-0.252,0.055-0.508,0.141-0.736c0.542-1.444,1.075-2.896,1.688-4.311
|
|
31
|
+
c0.472-1.09,1.01-2.143,1.597-3.172c0.384-0.424,0.782-0.844,1.192-1.254c3.833-3.832,8.363-6.553,13.186-8.162
|
|
32
|
+
C25.384,4.098,25.358,4.139,25.334,4.182z"/>
|
|
33
|
+
</g>
|
|
34
|
+
</svg>
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="utf-8"?>
|
|
2
|
+
|
|
3
|
+
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
|
4
|
+
<svg height="800px" width="800px" version="1.1" id="_x32_" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
|
|
5
|
+
viewBox="0 0 512 512" xml:space="preserve">
|
|
6
|
+
<style type="text/css">
|
|
7
|
+
.st0{fill:#000000;}
|
|
8
|
+
</style>
|
|
9
|
+
<g>
|
|
10
|
+
<path class="st0" d="M383.301,168.546c-6.107,8.825-12.711,17.27-19.676,25.413c-25.851,30.156-57.049,55.51-92.144,74.679
|
|
11
|
+
c-0.058,2.22-0.058,4.442-0.058,6.604c0,34.227,5.98,68.202,17.455,100.219c5.98-2.591,11.834-5.367,17.698-8.27
|
|
12
|
+
c76.344-38.046,139.189-97.131,181.939-171.012c3.75-6.535,7.335-13.139,10.724-19.86c-5.367-16.461-12.584-32.688-21.585-48.292
|
|
13
|
+
c-13.081-22.626-29.046-42.487-47.172-59.328c-8.64,31.082-21.722,60.322-38.552,86.961
|
|
14
|
+
C389.155,160.043,386.32,164.358,383.301,168.546z"/>
|
|
15
|
+
<path class="st0" d="M358.19,148.385h0.068c2.289-3.273,4.442-6.536,6.536-9.867c17.63-27.877,30.78-58.9,38.231-92.076
|
|
16
|
+
c-6.78-4.752-13.744-9.194-20.893-13.198C343.638,11.532,299.983,0,255.762,0.059c-43.422,0-87.332,10.986-127.725,34.286
|
|
17
|
+
c-11.532,6.662-22.266,14.064-32.318,22.15c86.221,2.834,169.413,29.356,241.443,77.085
|
|
18
|
+
C344.32,138.332,351.353,143.261,358.19,148.385z"/>
|
|
19
|
+
<path class="st0" d="M110.952,193.404c10.676,0.926,21.283,2.465,31.822,4.5c39.283,7.587,77.095,22.325,111.323,43.666
|
|
20
|
+
c32.191-17.269,60.867-40.334,84.488-67.714c-6.224-4.686-12.643-9.185-19.179-13.51c-71.056-47.124-153.692-72.04-239.037-72.04
|
|
21
|
+
c-6.048,0-12.155,0.137-18.195,0.439c-11.045,12.828-20.718,26.581-28.929,41.133c-12.458,22.14-21.585,45.955-27.069,70.618
|
|
22
|
+
c24.546-5.796,49.452-8.572,74.192-8.572C90.605,191.924,100.773,192.42,110.952,193.404z"/>
|
|
23
|
+
<path class="st0" d="M104.601,246.625c0.429-7.149,1.052-14.309,1.782-21.458c-8.63-0.809-17.328-1.179-26.016-1.179
|
|
24
|
+
c-26.523,0-53.29,3.585-79.433,10.92C0.311,242.009,0,249.089,0,256.249c0.059,43.422,11.045,87.332,34.354,127.725
|
|
25
|
+
c23.748,41.074,57.05,73.013,95.534,94.783c8.873,5.065,18.068,9.506,27.496,13.383c-35.328-66.974-53.708-141.04-53.708-216.528
|
|
26
|
+
C103.676,265.931,103.988,256.249,104.601,246.625z"/>
|
|
27
|
+
<path class="st0" d="M271.482,416.964c-4.685-9.682-8.814-19.549-12.399-29.601c-13.003-35.835-19.724-73.764-19.724-112.121
|
|
28
|
+
c0-1.666,0-3.331,0.059-4.997c-31.024-19.801-65.435-33.487-101.202-40.51c-0.681,6.224-1.237,12.506-1.607,18.867
|
|
29
|
+
c-0.556,9-0.867,18.01-0.867,27.01c0,75.799,19.861,149.924,57.975,216.041c2.834,4.928,5.795,9.799,8.873,14.61h0.069
|
|
30
|
+
c17.58,3.769,35.523,5.678,53.601,5.678c24.477,0,49.022-3.516,73.071-10.733c-18.555-19.549-34.412-41.075-47.416-64.082
|
|
31
|
+
C278.203,430.532,274.686,423.811,271.482,416.964z"/>
|
|
32
|
+
<path class="st0" d="M509.848,222.878c-45.576,74.387-110.7,134.086-188.962,172.989c-6.477,3.214-12.956,6.292-19.549,9.136
|
|
33
|
+
c2.649,5.542,5.484,10.978,8.503,16.335c13.939,24.672,31.452,47.425,52.423,67.53c7.344-3.39,14.62-7.082,21.712-11.212
|
|
34
|
+
c41.133-23.756,73.013-57.049,94.793-95.533C500.477,343.639,512,299.973,512,255.752
|
|
35
|
+
C511.941,244.774,511.27,233.807,509.848,222.878z"/>
|
|
36
|
+
</g>
|
|
37
|
+
</svg>
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { loadImage } from './helpers/common';
|
|
2
|
+
|
|
3
|
+
import basketballImageData from './assets/balls/basketball.svg';
|
|
4
|
+
import baseballImageData from './assets/balls/baseball.svg';
|
|
5
|
+
import footballImageData from './assets/balls/football.svg';
|
|
6
|
+
import soccerImageData from './assets/balls/soccer.svg';
|
|
7
|
+
import hockeyImageData from './assets/balls/hockey.svg';
|
|
8
|
+
import volleyballImageData from './assets/balls/volleyball.svg';
|
|
9
|
+
import lacrosseImageData from './assets/balls/lacrosse.svg';
|
|
10
|
+
|
|
11
|
+
import { ImageConfigItem } from './models/PlayModel';
|
|
12
|
+
import {
|
|
13
|
+
SPORT_TYPE_BASEBALL,
|
|
14
|
+
SPORT_TYPE_SOFTBALL,
|
|
15
|
+
SPORT_TYPE_BASKETBALL,
|
|
16
|
+
SPORT_TYPE_FOOTBALL,
|
|
17
|
+
SPORT_TYPE_HOCKEY,
|
|
18
|
+
SPORT_TYPE_LACROSSE,
|
|
19
|
+
SPORT_TYPE_LACROSSE_BOX,
|
|
20
|
+
SPORT_TYPE_SOCCER,
|
|
21
|
+
SPORT_TYPE_VOLLEYBALL
|
|
22
|
+
} from './constants';
|
|
23
|
+
|
|
24
|
+
export default async function (): Promise<readonly ImageConfigItem[]> {
|
|
25
|
+
const basketballShape = await loadImage(basketballImageData);
|
|
26
|
+
const baseballShape = await loadImage(baseballImageData);
|
|
27
|
+
const footballShape = await loadImage(footballImageData);
|
|
28
|
+
const soccerShape = await loadImage(soccerImageData);
|
|
29
|
+
const hockeyShape = await loadImage(hockeyImageData);
|
|
30
|
+
const volleyballShape = await loadImage(volleyballImageData);
|
|
31
|
+
const lacrosseShape = await loadImage(lacrosseImageData);
|
|
32
|
+
|
|
33
|
+
return Object.freeze([
|
|
34
|
+
{ key: SPORT_TYPE_BASKETBALL, image: basketballShape, dX: 0, dY: 0 },
|
|
35
|
+
{ key: SPORT_TYPE_BASEBALL, image: baseballShape, dX: 0, dY: 0 },
|
|
36
|
+
{ key: SPORT_TYPE_SOFTBALL, image: baseballShape, dX: 0, dY: 0 },
|
|
37
|
+
{ key: SPORT_TYPE_FOOTBALL, image: footballShape, dX: 0, dY: 0 },
|
|
38
|
+
{ key: SPORT_TYPE_SOCCER, image: soccerShape, dX: 0, dY: 0 },
|
|
39
|
+
{ key: SPORT_TYPE_HOCKEY, image: hockeyShape, dX: 0, dY: 0 },
|
|
40
|
+
{ key: SPORT_TYPE_VOLLEYBALL, image: volleyballShape, dX: 0, dY: 0 },
|
|
41
|
+
{ key: SPORT_TYPE_LACROSSE, image: lacrosseShape, dX: 0, dY: 0 },
|
|
42
|
+
{ key: SPORT_TYPE_LACROSSE_BOX, image: lacrosseShape, dX: 0, dY: 0 }
|
|
43
|
+
]);
|
|
44
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { SPORT_TYPE_BASKETBALL } from '../constants';
|
|
2
|
+
import { SportType } from '../types';
|
|
3
|
+
|
|
4
|
+
type StaticDataWithBalls = {
|
|
5
|
+
balls?: ReadonlyArray<{ key: string; image: CanvasImageSource }>;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
export type DrawBallObjectPlacement = 'center' | 'overlapBottomRight';
|
|
9
|
+
|
|
10
|
+
export type DrawBallObjectParams = {
|
|
11
|
+
sport: SportType;
|
|
12
|
+
ctx: CanvasRenderingContext2D;
|
|
13
|
+
staticData: StaticDataWithBalls | undefined;
|
|
14
|
+
center: { x: number; y: number };
|
|
15
|
+
puckRadius: number;
|
|
16
|
+
placement: DrawBallObjectPlacement;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Draw a "ball object" (currently basketball) using shared sizing rules.
|
|
21
|
+
* Future: swap selection logic based on sport type / config without changing call sites.
|
|
22
|
+
*/
|
|
23
|
+
export function drawBallObject(params: DrawBallObjectParams): boolean {
|
|
24
|
+
const { sport, ctx, staticData, center, puckRadius, placement } = params;
|
|
25
|
+
|
|
26
|
+
const ball = staticData?.balls?.find(b => b.key === sport);
|
|
27
|
+
if (!ball) return false;
|
|
28
|
+
|
|
29
|
+
// Same sizing rule used elsewhere: ball is 25% of puck diameter => puckRadius * 0.5
|
|
30
|
+
const ballSize = puckRadius * 0.5;
|
|
31
|
+
|
|
32
|
+
let x = center.x - ballSize / 2;
|
|
33
|
+
let y = center.y - ballSize / 2;
|
|
34
|
+
|
|
35
|
+
if (placement === 'overlapBottomRight') {
|
|
36
|
+
// Overlap inside the puck near bottom-right
|
|
37
|
+
const offset = puckRadius * 0.65;
|
|
38
|
+
x = center.x + offset - ballSize / 2;
|
|
39
|
+
y = center.y + offset - ballSize / 2;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
ctx.save();
|
|
43
|
+
ctx.drawImage(ball.image, x, y, ballSize, ballSize);
|
|
44
|
+
ctx.restore();
|
|
45
|
+
|
|
46
|
+
return true;
|
|
47
|
+
}
|
package/src/layers/LineLayer.ts
CHANGED
|
@@ -16,6 +16,7 @@ export default class LineLayer extends BaseLayer {
|
|
|
16
16
|
this.ctx.lineWidth = this.lineWidth;
|
|
17
17
|
this.playData.lines.forEach(line => {
|
|
18
18
|
if (this.options.linesHiddenIds.indexOf(line.id) >= 0) return;
|
|
19
|
+
|
|
19
20
|
const layerKey = `${_.capitalize(line.type)}LineLayer`;
|
|
20
21
|
if (lineLayers[layerKey as keyof typeof lineLayers]) {
|
|
21
22
|
new lineLayers[layerKey as keyof typeof lineLayers](this, line).apply();
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import BaseLayer from './base/BaseLayer';
|
|
2
2
|
import PlayerModel from '../models/PlayerModel';
|
|
3
|
+
import { SPORT_TYPE_BASKETBALL } from '../constants';
|
|
4
|
+
import { drawBallObject } from '../helpers/draw';
|
|
3
5
|
|
|
4
6
|
export default class PlayerLayer extends BaseLayer {
|
|
5
7
|
get staticData() {
|
|
@@ -57,6 +59,25 @@ export default class PlayerLayer extends BaseLayer {
|
|
|
57
59
|
}
|
|
58
60
|
}
|
|
59
61
|
}
|
|
62
|
+
|
|
63
|
+
if (player.possession && this.options.showBallMode) {
|
|
64
|
+
const playerBall = this.staticData.balls.find(ball => {
|
|
65
|
+
return ball.key === SPORT_TYPE_BASKETBALL;
|
|
66
|
+
});
|
|
67
|
+
if (playerBall) {
|
|
68
|
+
const radiusMultiplier = this.options.legacyPrintStyle ? 1.2 : 1;
|
|
69
|
+
const puckRadius = radiusMultiplier * this.radius;
|
|
70
|
+
|
|
71
|
+
drawBallObject({
|
|
72
|
+
sport: this.playData.sport,
|
|
73
|
+
ctx: this.ctx,
|
|
74
|
+
staticData: this.staticData,
|
|
75
|
+
center: { x, y },
|
|
76
|
+
puckRadius,
|
|
77
|
+
placement: 'overlapBottomRight'
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
}
|
|
60
81
|
});
|
|
61
82
|
|
|
62
83
|
this.ctx.restore();
|
|
@@ -77,7 +98,12 @@ export default class PlayerLayer extends BaseLayer {
|
|
|
77
98
|
if (player.isDefender && !payerInPosition && isDefaultInputColor) {
|
|
78
99
|
color = '#bb271b';
|
|
79
100
|
}
|
|
80
|
-
|
|
101
|
+
|
|
102
|
+
const shouldHighlightPossession = !this.options.showBallMode || this.options.highlightPlayerPuck;
|
|
103
|
+
|
|
104
|
+
if (player.possession && shouldHighlightPossession) {
|
|
105
|
+
color = `rgba(255, 128, 0, ${alpha})`;
|
|
106
|
+
}
|
|
81
107
|
|
|
82
108
|
if (this.options.legacyPrintStyle) {
|
|
83
109
|
this.ctx.lineWidth = this.courtTypeConstants.LINE_WIDTH;
|
|
@@ -2,7 +2,7 @@ import InternalBaseLayer from '../../base/InternalBaseLayer';
|
|
|
2
2
|
import { adjustedBezierCurveWithExclusionZones } from '../../../math/LineDrawingMath';
|
|
3
3
|
import LineDrawOperationsTrait from '../../../traits/LineDrawOperationsTrait';
|
|
4
4
|
import LineLayer from '../../LineLayer';
|
|
5
|
-
import LineModel from '../../../models/LineModel';
|
|
5
|
+
import LineModel, { LinePartAdjusted } from '../../../models/LineModel';
|
|
6
6
|
import { CourtPoint, LinePart } from '../../../types';
|
|
7
7
|
|
|
8
8
|
export type MaskSettings = {
|
|
@@ -19,6 +19,13 @@ export default class InternalLineLayer extends InternalBaseLayer {
|
|
|
19
19
|
protected angleBetweenLastTwoPoints(): number {
|
|
20
20
|
return 0;
|
|
21
21
|
}
|
|
22
|
+
maybeDrawBallAtStartPoint(params: {
|
|
23
|
+
linePart: Pick<LinePartAdjusted, 'animationSegment'>;
|
|
24
|
+
controlPoints: CourtPoint[];
|
|
25
|
+
alreadyDrawn: boolean;
|
|
26
|
+
}): boolean {
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
22
29
|
// ==============================================================
|
|
23
30
|
|
|
24
31
|
protected lineWidth: number;
|
|
@@ -62,7 +69,15 @@ export default class InternalLineLayer extends InternalBaseLayer {
|
|
|
62
69
|
const g = lineForPlayerInPosition ? 0 : Math.ceil(green * 255);
|
|
63
70
|
const b = lineForPlayerInPosition ? 255 : Math.ceil(blue * 255);
|
|
64
71
|
|
|
65
|
-
const
|
|
72
|
+
const hidePassLikeLinesDuringPlayback =
|
|
73
|
+
!!this.options.showBallMode &&
|
|
74
|
+
!!this.options.animationGlobalProgress &&
|
|
75
|
+
this.options.showPassLinesDuringPlayback === false &&
|
|
76
|
+
this.line.isBallTransferLine;
|
|
77
|
+
|
|
78
|
+
const effectiveAlpha = hidePassLikeLinesDuringPlayback ? 0 : alphaOverride ?? alpha;
|
|
79
|
+
|
|
80
|
+
const color = `rgba(${r}, ${g}, ${b}, ${effectiveAlpha})`;
|
|
66
81
|
|
|
67
82
|
this.ctx.fillStyle = color;
|
|
68
83
|
this.ctx.strokeStyle = color;
|
|
@@ -19,20 +19,30 @@ export default class DribbleLineLayer extends ActionLineLayer {
|
|
|
19
19
|
setLineOptions() {
|
|
20
20
|
const lineParts = [...this.line.getLineParts()];
|
|
21
21
|
const dribbleLineParts = this.convertLinePartsToDribble(lineParts);
|
|
22
|
+
|
|
22
23
|
this.line.setLinePartsAdjusted([]);
|
|
24
|
+
|
|
23
25
|
dribbleLineParts.forEach(lp => {
|
|
24
26
|
const { controlPoints, lpIndex } = lp;
|
|
25
27
|
const [firstPoint] = controlPoints;
|
|
26
|
-
|
|
28
|
+
|
|
29
|
+
let animationSegment: LinePartAdjusted['animationSegment'] | undefined = undefined;
|
|
30
|
+
|
|
27
31
|
if (this.options.animationGlobalProgress) {
|
|
28
32
|
const [start, end] = this.line.animationKeyTimeChunks[lpIndex!];
|
|
33
|
+
|
|
29
34
|
if (_.inRange(this.options.animationGlobalProgress, start, end)) {
|
|
30
35
|
if (animationProgress(this.options.animationGlobalProgress, start, end) > firstPoint.time!) {
|
|
31
|
-
|
|
36
|
+
animationSegment = 'processed';
|
|
37
|
+
} else {
|
|
38
|
+
animationSegment = 'active';
|
|
32
39
|
}
|
|
40
|
+
} else if (this.options.animationGlobalProgress > end) {
|
|
41
|
+
animationSegment = 'processed';
|
|
33
42
|
}
|
|
34
43
|
}
|
|
35
|
-
|
|
44
|
+
|
|
45
|
+
this.line.addLinePartAdjusted({ ...lp, controlPoints, animationSegment });
|
|
36
46
|
});
|
|
37
47
|
}
|
|
38
48
|
}
|
|
@@ -32,11 +32,13 @@ export default class ShotLineLayer extends ActionLineLayer {
|
|
|
32
32
|
|
|
33
33
|
this.ctx.lineJoin = 'round';
|
|
34
34
|
|
|
35
|
+
let isBallDrawn = false;
|
|
36
|
+
|
|
35
37
|
this.getProcessedLinePaths().forEach(linePart => {
|
|
36
38
|
this.ctx.save();
|
|
37
39
|
|
|
38
|
-
if (linePart.
|
|
39
|
-
this.setColor(
|
|
40
|
+
if (linePart.animationSegment === 'processed') {
|
|
41
|
+
this.setColor(0.1); // processed segment (before animation progress)
|
|
40
42
|
}
|
|
41
43
|
|
|
42
44
|
this.ctx.beginPath();
|
|
@@ -78,6 +80,15 @@ export default class ShotLineLayer extends ActionLineLayer {
|
|
|
78
80
|
this.ctx.stroke();
|
|
79
81
|
});
|
|
80
82
|
|
|
83
|
+
// Draw the ball AFTER the stroke so it overlaps the line.
|
|
84
|
+
if (!isBallDrawn) {
|
|
85
|
+
isBallDrawn = this.maybeDrawBallAtStartPoint({
|
|
86
|
+
linePart,
|
|
87
|
+
controlPoints: cp,
|
|
88
|
+
alreadyDrawn: isBallDrawn
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
|
|
81
92
|
this.ctx.restore();
|
|
82
93
|
});
|
|
83
94
|
|
|
@@ -30,6 +30,25 @@ export default class AnimationModel {
|
|
|
30
30
|
this.timeElapsedSaved = 0;
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
+
/**
|
|
34
|
+
* Extend current options (do not replace object) – same semantics as PlayModel.setOptions().
|
|
35
|
+
* This is needed because AnimationModel keeps an internal cloned PlayModel instance.
|
|
36
|
+
*/
|
|
37
|
+
setOptions(options: Partial<PlayModel['options']>) {
|
|
38
|
+
this.play.options = { ...this.play.options, ...options };
|
|
39
|
+
this.playBase.options = { ...this.playBase.options, ...options };
|
|
40
|
+
|
|
41
|
+
// Recreate frame so any layers/models that cached options are refreshed immediately.
|
|
42
|
+
this.animationFrame = new FrameModel(this.play, this.currentPlayPhase, this.globalProgress).setContext(this.ctx);
|
|
43
|
+
|
|
44
|
+
// If paused/stopped, force immediate visual update.
|
|
45
|
+
if (!this.running) {
|
|
46
|
+
this.animationFrame.setPhase(this.currentPlayPhase).setAnimationGlobalProgress(this.globalProgress).draw();
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return this;
|
|
50
|
+
}
|
|
51
|
+
|
|
33
52
|
get animationDuration() {
|
|
34
53
|
return this.play.playData.animationDuration / this.play.options.speed;
|
|
35
54
|
}
|
package/src/models/FrameModel.ts
CHANGED
|
@@ -252,14 +252,26 @@ export default class FrameModel {
|
|
|
252
252
|
if (line.type === 'DRIBBLE') {
|
|
253
253
|
return linePartsAdjusted.push({ ...line.getLineParts()[index] });
|
|
254
254
|
}
|
|
255
|
+
|
|
255
256
|
const linePathSplitted = splitBezierCurveAtTVal(
|
|
256
257
|
line.getLineParts()[index].controlPoints,
|
|
257
258
|
this.animationProgress(start, end)
|
|
258
259
|
) as LinePartAdjusted['controlPoints'][];
|
|
259
|
-
|
|
260
|
-
linePartsAdjusted.push({
|
|
260
|
+
|
|
261
|
+
linePartsAdjusted.push({
|
|
262
|
+
controlPoints: linePathSplitted[0],
|
|
263
|
+
animationSegment: 'processed'
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
linePartsAdjusted.push({
|
|
267
|
+
controlPoints: linePathSplitted[1],
|
|
268
|
+
animationSegment: 'active'
|
|
269
|
+
});
|
|
261
270
|
} else if (this.animationGlobalProgress > end) {
|
|
262
|
-
linePartsAdjusted.push({
|
|
271
|
+
linePartsAdjusted.push({
|
|
272
|
+
...line.getLineParts()[index],
|
|
273
|
+
animationSegment: 'processed'
|
|
274
|
+
});
|
|
263
275
|
} else {
|
|
264
276
|
linePartsAdjusted.push({ ...line.getLineParts()[index] });
|
|
265
277
|
}
|
package/src/models/LineModel.ts
CHANGED
|
@@ -9,7 +9,14 @@ export type LinePartAdjusted = {
|
|
|
9
9
|
| [CourtPointAdjusted, CourtPointAdjusted]
|
|
10
10
|
| [CourtPointAdjusted, CourtPointAdjusted, CourtPointAdjusted]
|
|
11
11
|
| [CourtPointAdjusted, CourtPointAdjusted, CourtPointAdjusted, CourtPointAdjusted];
|
|
12
|
-
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Indicates how this segment should be rendered during animation.
|
|
15
|
+
* - 'processed': segment is before current animation progress (dimmed)
|
|
16
|
+
* - 'active': segment is the currently "drawn" / normal colored segment
|
|
17
|
+
*/
|
|
18
|
+
animationSegment?: 'processed' | 'active';
|
|
19
|
+
|
|
13
20
|
lpIndex?: number;
|
|
14
21
|
};
|
|
15
22
|
|
|
@@ -97,6 +104,10 @@ export default class LineModel extends Model<LineData, LineAdjusted> {
|
|
|
97
104
|
return this._getAttr('type');
|
|
98
105
|
}
|
|
99
106
|
|
|
107
|
+
get isBallTransferLine() {
|
|
108
|
+
return ['PASS', 'HANDOFF', 'SHOT'].includes(this.type);
|
|
109
|
+
}
|
|
110
|
+
|
|
100
111
|
get phase() {
|
|
101
112
|
return this._getAttr('phase');
|
|
102
113
|
}
|
|
@@ -22,6 +22,10 @@ export function useDefaults(options?: Partial<PlayModelOptions>): PlayModelOptio
|
|
|
22
22
|
flipPlayerLabels: false,
|
|
23
23
|
legacyPrintStyle: false,
|
|
24
24
|
playerTokenScale: 1,
|
|
25
|
+
showBallMode: false,
|
|
26
|
+
// showBallMode sub-options (defaults match current behavior)
|
|
27
|
+
highlightPlayerPuck: true,
|
|
28
|
+
showPassLinesDuringPlayback: true,
|
|
25
29
|
// TODO: refactor NBA court type constants below
|
|
26
30
|
showHalfCourtCircle: true,
|
|
27
31
|
playersMap: [],
|
package/src/models/PlayModel.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import _ from 'lodash';
|
|
2
2
|
import playerHatsConfig from '../playerHatsConfig';
|
|
3
3
|
import shapesConfig from '../shapesConfig';
|
|
4
|
+
import ballConfig from '../ballConfig';
|
|
4
5
|
import { useDefaults } from './Play/Options';
|
|
5
6
|
|
|
6
7
|
import {
|
|
@@ -44,6 +45,7 @@ export type PlayStaticData = {
|
|
|
44
45
|
watermark: typeof PlayModel.watermark;
|
|
45
46
|
playerHats: readonly ImageConfigItem[];
|
|
46
47
|
shapes: readonly ImageConfigItem[];
|
|
48
|
+
balls: readonly ImageConfigItem[];
|
|
47
49
|
playerHeadshots: typeof PlayModel.playerHeadshots;
|
|
48
50
|
teamPlayers: typeof PlayModel.teamPlayers;
|
|
49
51
|
};
|
|
@@ -88,6 +90,10 @@ export type PlayModelOptions = {
|
|
|
88
90
|
playersMap: PlayersMapItem[];
|
|
89
91
|
labelsOverrideType: 'Initials' | 'Jersey number' | 'Headshot' | null;
|
|
90
92
|
inDrawingState: boolean;
|
|
93
|
+
showBallMode: boolean;
|
|
94
|
+
// sub-options for showBallMode
|
|
95
|
+
highlightPlayerPuck: boolean;
|
|
96
|
+
showPassLinesDuringPlayback: boolean;
|
|
91
97
|
};
|
|
92
98
|
|
|
93
99
|
export default class PlayModel {
|
|
@@ -98,6 +104,7 @@ export default class PlayModel {
|
|
|
98
104
|
public static playerHeadshots: PlayerHeadshotItem[] = [];
|
|
99
105
|
public static playerHats: readonly ImageConfigItem[];
|
|
100
106
|
public static shapes: readonly ImageConfigItem[];
|
|
107
|
+
public static balls: readonly ImageConfigItem[];
|
|
101
108
|
public static watermark: { LuceoSports: HTMLImageElement; TeamLogo: HTMLImageElement | null };
|
|
102
109
|
public static backgroundOptions: Record<SportType, HTMLImageElement> & {
|
|
103
110
|
Hardwood: HTMLImageElement;
|
|
@@ -113,6 +120,7 @@ export default class PlayModel {
|
|
|
113
120
|
static async init({ teamLogoPath = '' } = {}) {
|
|
114
121
|
PlayModel.playerHats = await playerHatsConfig();
|
|
115
122
|
PlayModel.shapes = await shapesConfig();
|
|
123
|
+
PlayModel.balls = await ballConfig();
|
|
116
124
|
|
|
117
125
|
const hardwoodImage = await loadImage(hardwoodImageData);
|
|
118
126
|
const grassImage = await loadImage(grassImageData);
|
|
@@ -190,6 +198,7 @@ export default class PlayModel {
|
|
|
190
198
|
watermark: PlayModel.watermark,
|
|
191
199
|
playerHats: PlayModel.playerHats,
|
|
192
200
|
shapes: PlayModel.shapes,
|
|
201
|
+
balls: PlayModel.balls,
|
|
193
202
|
playerHeadshots: PlayModel.playerHeadshots,
|
|
194
203
|
teamPlayers: PlayModel.teamPlayers
|
|
195
204
|
};
|