` tag.
54 |
55 | ```
56 | .icon-account-login {
57 | fill: #f00;
58 | }
59 | ```
60 |
61 | To learn more about SVG Sprites, read [Chris Coyier's guide](http://css-tricks.com/svg-sprites-use-better-icon-fonts/).
62 |
63 | #### Using Open Iconic's Icon Font...
64 |
65 |
66 | ##### …with Bootstrap
67 |
68 | You can find our Bootstrap stylesheets in `font/css/open-iconic-bootstrap.{css, less, scss, styl}`
69 |
70 |
71 | ```
72 |
73 | ```
74 |
75 |
76 | ```
77 |
78 | ```
79 |
80 | ##### …with Foundation
81 |
82 | You can find our Foundation stylesheets in `font/css/open-iconic-foundation.{css, less, scss, styl}`
83 |
84 | ```
85 |
86 | ```
87 |
88 |
89 | ```
90 |
91 | ```
92 |
93 | ##### …on its own
94 |
95 | You can find our default stylesheets in `font/css/open-iconic.{css, less, scss, styl}`
96 |
97 | ```
98 |
99 | ```
100 |
101 | ```
102 |
103 | ```
104 |
105 |
106 | ## License
107 |
108 | ### Icons
109 |
110 | All code (including SVG markup) is under the [MIT License](http://opensource.org/licenses/MIT).
111 |
112 | ### Fonts
113 |
114 | All fonts are under the [SIL Licensed](http://scripts.sil.org/cms/scripts/page.php?item_id=OFL_web).
115 |
--------------------------------------------------------------------------------
/samples/KristofferStrube.Blazor.SVGAnimation.WasmSample/wwwroot/css/open-iconic/font/css/open-iconic-bootstrap.min.css:
--------------------------------------------------------------------------------
1 | @font-face{font-family:Icons;src:url(../fonts/open-iconic.eot);src:url(../fonts/open-iconic.eot?#iconic-sm) format('embedded-opentype'),url(../fonts/open-iconic.woff) format('woff'),url(../fonts/open-iconic.ttf) format('truetype'),url(../fonts/open-iconic.otf) format('opentype'),url(../fonts/open-iconic.svg#iconic-sm) format('svg');font-weight:400;font-style:normal}.oi{position:relative;top:1px;display:inline-block;speak:none;font-family:Icons;font-style:normal;font-weight:400;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.oi:empty:before{width:1em;text-align:center;box-sizing:content-box}.oi.oi-align-center:before{text-align:center}.oi.oi-align-left:before{text-align:left}.oi.oi-align-right:before{text-align:right}.oi.oi-flip-horizontal:before{-webkit-transform:scale(-1,1);-ms-transform:scale(-1,1);transform:scale(-1,1)}.oi.oi-flip-vertical:before{-webkit-transform:scale(1,-1);-ms-transform:scale(-1,1);transform:scale(1,-1)}.oi.oi-flip-horizontal-vertical:before{-webkit-transform:scale(-1,-1);-ms-transform:scale(-1,1);transform:scale(-1,-1)}.oi-account-login:before{content:'\e000'}.oi-account-logout:before{content:'\e001'}.oi-action-redo:before{content:'\e002'}.oi-action-undo:before{content:'\e003'}.oi-align-center:before{content:'\e004'}.oi-align-left:before{content:'\e005'}.oi-align-right:before{content:'\e006'}.oi-aperture:before{content:'\e007'}.oi-arrow-bottom:before{content:'\e008'}.oi-arrow-circle-bottom:before{content:'\e009'}.oi-arrow-circle-left:before{content:'\e00a'}.oi-arrow-circle-right:before{content:'\e00b'}.oi-arrow-circle-top:before{content:'\e00c'}.oi-arrow-left:before{content:'\e00d'}.oi-arrow-right:before{content:'\e00e'}.oi-arrow-thick-bottom:before{content:'\e00f'}.oi-arrow-thick-left:before{content:'\e010'}.oi-arrow-thick-right:before{content:'\e011'}.oi-arrow-thick-top:before{content:'\e012'}.oi-arrow-top:before{content:'\e013'}.oi-audio-spectrum:before{content:'\e014'}.oi-audio:before{content:'\e015'}.oi-badge:before{content:'\e016'}.oi-ban:before{content:'\e017'}.oi-bar-chart:before{content:'\e018'}.oi-basket:before{content:'\e019'}.oi-battery-empty:before{content:'\e01a'}.oi-battery-full:before{content:'\e01b'}.oi-beaker:before{content:'\e01c'}.oi-bell:before{content:'\e01d'}.oi-bluetooth:before{content:'\e01e'}.oi-bold:before{content:'\e01f'}.oi-bolt:before{content:'\e020'}.oi-book:before{content:'\e021'}.oi-bookmark:before{content:'\e022'}.oi-box:before{content:'\e023'}.oi-briefcase:before{content:'\e024'}.oi-british-pound:before{content:'\e025'}.oi-browser:before{content:'\e026'}.oi-brush:before{content:'\e027'}.oi-bug:before{content:'\e028'}.oi-bullhorn:before{content:'\e029'}.oi-calculator:before{content:'\e02a'}.oi-calendar:before{content:'\e02b'}.oi-camera-slr:before{content:'\e02c'}.oi-caret-bottom:before{content:'\e02d'}.oi-caret-left:before{content:'\e02e'}.oi-caret-right:before{content:'\e02f'}.oi-caret-top:before{content:'\e030'}.oi-cart:before{content:'\e031'}.oi-chat:before{content:'\e032'}.oi-check:before{content:'\e033'}.oi-chevron-bottom:before{content:'\e034'}.oi-chevron-left:before{content:'\e035'}.oi-chevron-right:before{content:'\e036'}.oi-chevron-top:before{content:'\e037'}.oi-circle-check:before{content:'\e038'}.oi-circle-x:before{content:'\e039'}.oi-clipboard:before{content:'\e03a'}.oi-clock:before{content:'\e03b'}.oi-cloud-download:before{content:'\e03c'}.oi-cloud-upload:before{content:'\e03d'}.oi-cloud:before{content:'\e03e'}.oi-cloudy:before{content:'\e03f'}.oi-code:before{content:'\e040'}.oi-cog:before{content:'\e041'}.oi-collapse-down:before{content:'\e042'}.oi-collapse-left:before{content:'\e043'}.oi-collapse-right:before{content:'\e044'}.oi-collapse-up:before{content:'\e045'}.oi-command:before{content:'\e046'}.oi-comment-square:before{content:'\e047'}.oi-compass:before{content:'\e048'}.oi-contrast:before{content:'\e049'}.oi-copywriting:before{content:'\e04a'}.oi-credit-card:before{content:'\e04b'}.oi-crop:before{content:'\e04c'}.oi-dashboard:before{content:'\e04d'}.oi-data-transfer-download:before{content:'\e04e'}.oi-data-transfer-upload:before{content:'\e04f'}.oi-delete:before{content:'\e050'}.oi-dial:before{content:'\e051'}.oi-document:before{content:'\e052'}.oi-dollar:before{content:'\e053'}.oi-double-quote-sans-left:before{content:'\e054'}.oi-double-quote-sans-right:before{content:'\e055'}.oi-double-quote-serif-left:before{content:'\e056'}.oi-double-quote-serif-right:before{content:'\e057'}.oi-droplet:before{content:'\e058'}.oi-eject:before{content:'\e059'}.oi-elevator:before{content:'\e05a'}.oi-ellipses:before{content:'\e05b'}.oi-envelope-closed:before{content:'\e05c'}.oi-envelope-open:before{content:'\e05d'}.oi-euro:before{content:'\e05e'}.oi-excerpt:before{content:'\e05f'}.oi-expand-down:before{content:'\e060'}.oi-expand-left:before{content:'\e061'}.oi-expand-right:before{content:'\e062'}.oi-expand-up:before{content:'\e063'}.oi-external-link:before{content:'\e064'}.oi-eye:before{content:'\e065'}.oi-eyedropper:before{content:'\e066'}.oi-file:before{content:'\e067'}.oi-fire:before{content:'\e068'}.oi-flag:before{content:'\e069'}.oi-flash:before{content:'\e06a'}.oi-folder:before{content:'\e06b'}.oi-fork:before{content:'\e06c'}.oi-fullscreen-enter:before{content:'\e06d'}.oi-fullscreen-exit:before{content:'\e06e'}.oi-globe:before{content:'\e06f'}.oi-graph:before{content:'\e070'}.oi-grid-four-up:before{content:'\e071'}.oi-grid-three-up:before{content:'\e072'}.oi-grid-two-up:before{content:'\e073'}.oi-hard-drive:before{content:'\e074'}.oi-header:before{content:'\e075'}.oi-headphones:before{content:'\e076'}.oi-heart:before{content:'\e077'}.oi-home:before{content:'\e078'}.oi-image:before{content:'\e079'}.oi-inbox:before{content:'\e07a'}.oi-infinity:before{content:'\e07b'}.oi-info:before{content:'\e07c'}.oi-italic:before{content:'\e07d'}.oi-justify-center:before{content:'\e07e'}.oi-justify-left:before{content:'\e07f'}.oi-justify-right:before{content:'\e080'}.oi-key:before{content:'\e081'}.oi-laptop:before{content:'\e082'}.oi-layers:before{content:'\e083'}.oi-lightbulb:before{content:'\e084'}.oi-link-broken:before{content:'\e085'}.oi-link-intact:before{content:'\e086'}.oi-list-rich:before{content:'\e087'}.oi-list:before{content:'\e088'}.oi-location:before{content:'\e089'}.oi-lock-locked:before{content:'\e08a'}.oi-lock-unlocked:before{content:'\e08b'}.oi-loop-circular:before{content:'\e08c'}.oi-loop-square:before{content:'\e08d'}.oi-loop:before{content:'\e08e'}.oi-magnifying-glass:before{content:'\e08f'}.oi-map-marker:before{content:'\e090'}.oi-map:before{content:'\e091'}.oi-media-pause:before{content:'\e092'}.oi-media-play:before{content:'\e093'}.oi-media-record:before{content:'\e094'}.oi-media-skip-backward:before{content:'\e095'}.oi-media-skip-forward:before{content:'\e096'}.oi-media-step-backward:before{content:'\e097'}.oi-media-step-forward:before{content:'\e098'}.oi-media-stop:before{content:'\e099'}.oi-medical-cross:before{content:'\e09a'}.oi-menu:before{content:'\e09b'}.oi-microphone:before{content:'\e09c'}.oi-minus:before{content:'\e09d'}.oi-monitor:before{content:'\e09e'}.oi-moon:before{content:'\e09f'}.oi-move:before{content:'\e0a0'}.oi-musical-note:before{content:'\e0a1'}.oi-paperclip:before{content:'\e0a2'}.oi-pencil:before{content:'\e0a3'}.oi-people:before{content:'\e0a4'}.oi-person:before{content:'\e0a5'}.oi-phone:before{content:'\e0a6'}.oi-pie-chart:before{content:'\e0a7'}.oi-pin:before{content:'\e0a8'}.oi-play-circle:before{content:'\e0a9'}.oi-plus:before{content:'\e0aa'}.oi-power-standby:before{content:'\e0ab'}.oi-print:before{content:'\e0ac'}.oi-project:before{content:'\e0ad'}.oi-pulse:before{content:'\e0ae'}.oi-puzzle-piece:before{content:'\e0af'}.oi-question-mark:before{content:'\e0b0'}.oi-rain:before{content:'\e0b1'}.oi-random:before{content:'\e0b2'}.oi-reload:before{content:'\e0b3'}.oi-resize-both:before{content:'\e0b4'}.oi-resize-height:before{content:'\e0b5'}.oi-resize-width:before{content:'\e0b6'}.oi-rss-alt:before{content:'\e0b7'}.oi-rss:before{content:'\e0b8'}.oi-script:before{content:'\e0b9'}.oi-share-boxed:before{content:'\e0ba'}.oi-share:before{content:'\e0bb'}.oi-shield:before{content:'\e0bc'}.oi-signal:before{content:'\e0bd'}.oi-signpost:before{content:'\e0be'}.oi-sort-ascending:before{content:'\e0bf'}.oi-sort-descending:before{content:'\e0c0'}.oi-spreadsheet:before{content:'\e0c1'}.oi-star:before{content:'\e0c2'}.oi-sun:before{content:'\e0c3'}.oi-tablet:before{content:'\e0c4'}.oi-tag:before{content:'\e0c5'}.oi-tags:before{content:'\e0c6'}.oi-target:before{content:'\e0c7'}.oi-task:before{content:'\e0c8'}.oi-terminal:before{content:'\e0c9'}.oi-text:before{content:'\e0ca'}.oi-thumb-down:before{content:'\e0cb'}.oi-thumb-up:before{content:'\e0cc'}.oi-timer:before{content:'\e0cd'}.oi-transfer:before{content:'\e0ce'}.oi-trash:before{content:'\e0cf'}.oi-underline:before{content:'\e0d0'}.oi-vertical-align-bottom:before{content:'\e0d1'}.oi-vertical-align-center:before{content:'\e0d2'}.oi-vertical-align-top:before{content:'\e0d3'}.oi-video:before{content:'\e0d4'}.oi-volume-high:before{content:'\e0d5'}.oi-volume-low:before{content:'\e0d6'}.oi-volume-off:before{content:'\e0d7'}.oi-warning:before{content:'\e0d8'}.oi-wifi:before{content:'\e0d9'}.oi-wrench:before{content:'\e0da'}.oi-x:before{content:'\e0db'}.oi-yen:before{content:'\e0dc'}.oi-zoom-in:before{content:'\e0dd'}.oi-zoom-out:before{content:'\e0de'}
--------------------------------------------------------------------------------
/samples/KristofferStrube.Blazor.SVGAnimation.WasmSample/wwwroot/css/open-iconic/font/fonts/open-iconic.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/KristofferStrube/Blazor.SVGAnimation/e39f7386e0b5bf7e7c98ef0441c181dc8c812d5f/samples/KristofferStrube.Blazor.SVGAnimation.WasmSample/wwwroot/css/open-iconic/font/fonts/open-iconic.eot
--------------------------------------------------------------------------------
/samples/KristofferStrube.Blazor.SVGAnimation.WasmSample/wwwroot/css/open-iconic/font/fonts/open-iconic.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/KristofferStrube/Blazor.SVGAnimation/e39f7386e0b5bf7e7c98ef0441c181dc8c812d5f/samples/KristofferStrube.Blazor.SVGAnimation.WasmSample/wwwroot/css/open-iconic/font/fonts/open-iconic.otf
--------------------------------------------------------------------------------
/samples/KristofferStrube.Blazor.SVGAnimation.WasmSample/wwwroot/css/open-iconic/font/fonts/open-iconic.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
7 |
8 | Created by FontForge 20120731 at Tue Jul 1 20:39:22 2014
9 | By P.J. Onori
10 | Created by P.J. Onori with FontForge 2.0 (http://fontforge.sf.net)
11 |
12 |
13 |
14 |
27 |
28 |
30 |
32 |
34 |
36 |
38 |
40 |
42 |
45 |
47 |
49 |
51 |
53 |
55 |
57 |
59 |
61 |
63 |
65 |
67 |
69 |
71 |
74 |
76 |
79 |
81 |
84 |
86 |
88 |
91 |
93 |
95 |
98 |
100 |
102 |
104 |
106 |
109 |
112 |
115 |
117 |
121 |
123 |
125 |
127 |
130 |
132 |
134 |
136 |
138 |
141 |
143 |
145 |
147 |
149 |
151 |
153 |
155 |
157 |
159 |
162 |
165 |
167 |
169 |
172 |
174 |
177 |
179 |
181 |
183 |
185 |
189 |
191 |
194 |
196 |
198 |
200 |
202 |
205 |
207 |
209 |
211 |
213 |
215 |
218 |
220 |
222 |
224 |
226 |
228 |
230 |
232 |
234 |
236 |
238 |
241 |
243 |
245 |
247 |
249 |
251 |
253 |
256 |
259 |
261 |
263 |
265 |
267 |
269 |
272 |
274 |
276 |
280 |
282 |
285 |
287 |
289 |
292 |
295 |
298 |
300 |
302 |
304 |
306 |
309 |
312 |
314 |
316 |
318 |
320 |
322 |
324 |
326 |
330 |
334 |
338 |
340 |
343 |
345 |
347 |
349 |
351 |
353 |
355 |
358 |
360 |
363 |
365 |
367 |
369 |
371 |
373 |
375 |
377 |
379 |
381 |
383 |
386 |
388 |
390 |
392 |
394 |
396 |
399 |
401 |
404 |
406 |
408 |
410 |
412 |
414 |
416 |
419 |
421 |
423 |
425 |
428 |
431 |
435 |
438 |
440 |
442 |
444 |
446 |
448 |
451 |
453 |
455 |
457 |
460 |
462 |
464 |
466 |
468 |
471 |
473 |
477 |
479 |
481 |
483 |
486 |
488 |
490 |
492 |
494 |
496 |
499 |
501 |
504 |
506 |
509 |
512 |
515 |
517 |
520 |
522 |
524 |
526 |
529 |
532 |
534 |
536 |
539 |
542 |
543 |
544 |
--------------------------------------------------------------------------------
/samples/KristofferStrube.Blazor.SVGAnimation.WasmSample/wwwroot/css/open-iconic/font/fonts/open-iconic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/KristofferStrube/Blazor.SVGAnimation/e39f7386e0b5bf7e7c98ef0441c181dc8c812d5f/samples/KristofferStrube.Blazor.SVGAnimation.WasmSample/wwwroot/css/open-iconic/font/fonts/open-iconic.ttf
--------------------------------------------------------------------------------
/samples/KristofferStrube.Blazor.SVGAnimation.WasmSample/wwwroot/css/open-iconic/font/fonts/open-iconic.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/KristofferStrube/Blazor.SVGAnimation/e39f7386e0b5bf7e7c98ef0441c181dc8c812d5f/samples/KristofferStrube.Blazor.SVGAnimation.WasmSample/wwwroot/css/open-iconic/font/fonts/open-iconic.woff
--------------------------------------------------------------------------------
/samples/KristofferStrube.Blazor.SVGAnimation.WasmSample/wwwroot/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/KristofferStrube/Blazor.SVGAnimation/e39f7386e0b5bf7e7c98ef0441c181dc8c812d5f/samples/KristofferStrube.Blazor.SVGAnimation.WasmSample/wwwroot/favicon.ico
--------------------------------------------------------------------------------
/samples/KristofferStrube.Blazor.SVGAnimation.WasmSample/wwwroot/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | KristofferStrube.Blazor.SVGAnimation.WasmSample
8 |
9 |
10 |
32 |
43 |
44 |
45 |
46 |
47 |
48 |
49 | Loading...
50 |
51 |
52 | An unhandled error has occurred.
53 |
Reload
54 |
🗙
55 |
56 |
57 |
58 |
59 |
60 |
--------------------------------------------------------------------------------
/samples/KristofferStrube.Blazor.SVGAnimation.WebAppSample/Components/App.razor:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/samples/KristofferStrube.Blazor.SVGAnimation.WebAppSample/Components/Layout/MainLayout.razor:
--------------------------------------------------------------------------------
1 | @using KristofferStrube.Blazor.SVGAnimation.WebAppSample.Confetti
2 | @inherits LayoutComponentBase
3 | @inject NavigationManager NavigationManager
4 |
5 |
6 |
9 |
10 |
11 |
16 |
17 |
18 | @Body
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 | An unhandled error has occurred.
27 |
Reload
28 |
🗙
29 |
30 |
31 | @code {
32 | private string relativeUri => NavigationManager.ToBaseRelativePath(NavigationManager.Uri);
33 |
34 | protected string page => (string.IsNullOrEmpty(relativeUri) ? "Index" : relativeUri) + ".razor";
35 | }
--------------------------------------------------------------------------------
/samples/KristofferStrube.Blazor.SVGAnimation.WebAppSample/Components/Layout/MainLayout.razor.css:
--------------------------------------------------------------------------------
1 | .page {
2 | position: relative;
3 | display: flex;
4 | flex-direction: column;
5 | }
6 |
7 | main {
8 | flex: 1;
9 | }
10 |
11 | .sidebar {
12 | background-image: linear-gradient(180deg, rgb(5, 39, 103) 0%, #3a0647 70%);
13 | }
14 |
15 | .top-row {
16 | background-color: #f7f7f7;
17 | border-bottom: 1px solid #d6d5d5;
18 | justify-content: flex-end;
19 | height: 3.5rem;
20 | display: flex;
21 | align-items: center;
22 | }
23 |
24 | .top-row ::deep a, .top-row ::deep .btn-link {
25 | white-space: nowrap;
26 | margin-left: 1.5rem;
27 | text-decoration: none;
28 | }
29 |
30 | .top-row ::deep a:hover, .top-row ::deep .btn-link:hover {
31 | text-decoration: underline;
32 | }
33 |
34 | .top-row ::deep a:first-child {
35 | overflow: hidden;
36 | text-overflow: ellipsis;
37 | }
38 |
39 | @media (max-width: 640.98px) {
40 | .top-row {
41 | justify-content: space-between;
42 | }
43 |
44 | .top-row ::deep a, .top-row ::deep .btn-link {
45 | margin-left: 0;
46 | }
47 | }
48 |
49 | @media (min-width: 641px) {
50 | .page {
51 | flex-direction: row;
52 | }
53 |
54 | .sidebar {
55 | width: 250px;
56 | height: 100vh;
57 | position: sticky;
58 | top: 0;
59 | }
60 |
61 | .top-row {
62 | position: sticky;
63 | top: 0;
64 | z-index: 1;
65 | }
66 |
67 | .top-row.auth ::deep a:first-child {
68 | flex: 1;
69 | text-align: right;
70 | width: 0;
71 | }
72 |
73 | .top-row, article {
74 | padding-left: 2rem !important;
75 | padding-right: 1.5rem !important;
76 | }
77 | }
78 |
79 | #blazor-error-ui {
80 | background: lightyellow;
81 | bottom: 0;
82 | box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2);
83 | display: none;
84 | left: 0;
85 | padding: 0.6rem 1.25rem 0.7rem 1.25rem;
86 | position: fixed;
87 | width: 100%;
88 | z-index: 1000;
89 | }
90 |
91 | #blazor-error-ui .dismiss {
92 | cursor: pointer;
93 | position: absolute;
94 | right: 0.75rem;
95 | top: 0.5rem;
96 | }
97 |
--------------------------------------------------------------------------------
/samples/KristofferStrube.Blazor.SVGAnimation.WebAppSample/Components/Layout/NavMenu.razor:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
9 |
28 |
29 |
--------------------------------------------------------------------------------
/samples/KristofferStrube.Blazor.SVGAnimation.WebAppSample/Components/Layout/NavMenu.razor.css:
--------------------------------------------------------------------------------
1 | .navbar-toggler {
2 | appearance: none;
3 | cursor: pointer;
4 | width: 3.5rem;
5 | height: 2.5rem;
6 | color: white;
7 | position: absolute;
8 | top: 0.5rem;
9 | right: 1rem;
10 | border: 1px solid rgba(255, 255, 255, 0.1);
11 | background: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e") no-repeat center/1.75rem rgba(255, 255, 255, 0.1);
12 | }
13 |
14 | .navbar-toggler:checked {
15 | background-color: rgba(255, 255, 255, 0.5);
16 | }
17 |
18 | .top-row {
19 | height: 3.5rem;
20 | background-color: rgba(0,0,0,0.4);
21 | }
22 |
23 | .navbar-brand {
24 | font-size: 1.1rem;
25 | }
26 |
27 | .bi {
28 | display: inline-block;
29 | position: relative;
30 | width: 1.25rem;
31 | height: 1.25rem;
32 | margin-right: 0.75rem;
33 | top: -1px;
34 | background-size: cover;
35 | }
36 |
37 | .bi-house-door-fill-nav-menu {
38 | background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-house-door-fill' viewBox='0 0 16 16'%3E%3Cpath d='M6.5 14.5v-3.505c0-.245.25-.495.5-.495h2c.25 0 .5.25.5.5v3.5a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5v-7a.5.5 0 0 0-.146-.354L13 5.793V2.5a.5.5 0 0 0-.5-.5h-1a.5.5 0 0 0-.5.5v1.293L8.354 1.146a.5.5 0 0 0-.708 0l-6 6A.5.5 0 0 0 1.5 7.5v7a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5Z'/%3E%3C/svg%3E");
39 | }
40 |
41 | .bi-reload-nav-menu {
42 | background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-list-nested' viewBox='0 0 8 8'%3E%3Cpath fill-rule='evenodd' d='M4 0C1.8 0 0 1.8 0 4s1.8 4 4 4c1.1 0 2.12-.43 2.84-1.16l-.72-.72c-.54.54-1.29.88-2.13.88c-1.66 0-3-1.34-3-3s1.34-3 3-3c.83 0 1.55.36 2.09.91L4.99 3h3V0L6.8 1.19C6.08.47 5.09 0 3.99 0'/%3E%3C/svg%3E");
43 | }
44 |
45 | .nav-item {
46 | font-size: 0.9rem;
47 | padding-bottom: 0.5rem;
48 | }
49 |
50 | .nav-item:first-of-type {
51 | padding-top: 1rem;
52 | }
53 |
54 | .nav-item:last-of-type {
55 | padding-bottom: 1rem;
56 | }
57 |
58 | .nav-item ::deep .nav-link {
59 | color: #d7d7d7;
60 | background: none;
61 | border: none;
62 | border-radius: 4px;
63 | height: 3rem;
64 | display: flex;
65 | align-items: center;
66 | line-height: 3rem;
67 | width: 100%;
68 | }
69 |
70 | .nav-item ::deep a.active {
71 | background-color: rgba(255,255,255,0.37);
72 | color: white;
73 | }
74 |
75 | .nav-item ::deep .nav-link:hover {
76 | background-color: rgba(255,255,255,0.1);
77 | color: white;
78 | }
79 |
80 | .nav-scrollable {
81 | display: none;
82 | }
83 |
84 | .navbar-toggler:checked ~ .nav-scrollable {
85 | display: block;
86 | }
87 |
88 | @media (min-width: 641px) {
89 | .navbar-toggler {
90 | display: none;
91 | }
92 |
93 | .nav-scrollable {
94 | /* Never collapse the sidebar for wide screens */
95 | display: block;
96 |
97 | /* Allow sidebar to scroll for tall menus */
98 | height: calc(100vh - 3.5rem);
99 | overflow-y: auto;
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/samples/KristofferStrube.Blazor.SVGAnimation.WebAppSample/Components/Pages/Celebration.razor:
--------------------------------------------------------------------------------
1 | @page "/Celebration"
2 | @using KristofferStrube.Blazor.SVGAnimation.WebAppSample.Confetti
3 | @rendermode InteractiveServer
4 | @inject IJSRuntime JSRuntime
5 |
6 | Blazor.SVGAnimation - Celebration
7 |
8 | Confetti from bottom!
9 |
10 |
11 |
12 | Confetti from this button!
13 |
14 | @code {
15 | private ElementReference button;
16 |
17 | [Inject]
18 | public required ConfettiService ConfettiService { get; set; }
19 |
20 | public void ConfettiFromBottom() => ConfettiService.Activate(new());
21 |
22 | public void ConfettiFromButton() =>
23 | ConfettiService.Activate(new() {
24 | Mode = ConfettiOriginMode.FromElement,
25 | Origin = button
26 | });
27 | }
28 |
29 |
30 |
--------------------------------------------------------------------------------
/samples/KristofferStrube.Blazor.SVGAnimation.WebAppSample/Components/Pages/Error.razor:
--------------------------------------------------------------------------------
1 | @page "/Error"
2 | @using System.Diagnostics
3 |
4 | Error
5 |
6 | Error.
7 | An error occurred while processing your request.
8 |
9 | @if (ShowRequestId)
10 | {
11 |
12 | Request ID: @RequestId
13 |
14 | }
15 |
16 | Development Mode
17 |
18 | Swapping to Development environment will display more detailed information about the error that occurred.
19 |
20 |
21 | The Development environment shouldn't be enabled for deployed applications.
22 | It can result in displaying sensitive information from exceptions to end users.
23 | For local debugging, enable the Development environment by setting the ASPNETCORE_ENVIRONMENT environment variable to Development
24 | and restarting the app.
25 |
26 |
27 | @code{
28 | [CascadingParameter]
29 | private HttpContext? HttpContext { get; set; }
30 |
31 | private string? RequestId { get; set; }
32 | private bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
33 |
34 | protected override void OnInitialized() =>
35 | RequestId = Activity.Current?.Id ?? HttpContext?.TraceIdentifier;
36 | }
37 |
--------------------------------------------------------------------------------
/samples/KristofferStrube.Blazor.SVGAnimation.WebAppSample/Components/Pages/Index.razor:
--------------------------------------------------------------------------------
1 | @page "/"
2 | @rendermode InteractiveServer
3 | @using KristofferStrube.Blazor.DOM
4 | @implements IAsyncDisposable
5 | @inject IJSRuntime JSRuntime
6 |
7 | Blazor.SVGAnimation - Begin Animation
8 |
9 | Click the paper to begin
a SVG Animation
10 |
11 |
12 |
20 |
25 |
31 |
37 |
38 |
39 |
42 |
43 | @code {
44 | protected ElementReference dataAnimationElementReference { get; set; }
45 | protected ElementReference colorAnimationElementReference { get; set; }
46 | protected SVGAnimationElement dataAnimation = default!;
47 | protected SVGAnimationElement colorAnimation = default!;
48 | protected EventListener beginEventListener = default!;
49 | protected EventListener endEventListener = default!;
50 | protected EventListener repeatEventListener = default!;
51 | protected string log = "";
52 |
53 | protected override async Task OnAfterRenderAsync(bool firstRender)
54 | {
55 | if (!firstRender) return;
56 |
57 | dataAnimation = await SVGAnimationElement.CreateAsync(JSRuntime, dataAnimationElementReference);
58 | colorAnimation = await SVGAnimationElement.CreateAsync(JSRuntime, colorAnimationElementReference);
59 |
60 | beginEventListener = await dataAnimation.AddOnBeginEventListenerAsync(_ =>
61 | {
62 | log += "Begin event fired\n";
63 | StateHasChanged();
64 | return Task.CompletedTask;
65 | });
66 | endEventListener = await dataAnimation.AddOnEndEventListenerAsync(_ =>
67 | {
68 | log += "End event fired\n";
69 | StateHasChanged();
70 | return Task.CompletedTask;
71 | });
72 | repeatEventListener = await dataAnimation.AddOnRepeatEventListenerAsync(_ =>
73 | {
74 | log += "Repeat event fired\n";
75 | StateHasChanged();
76 | return Task.CompletedTask;
77 | });
78 | }
79 |
80 | private async Task PaperClicked()
81 | {
82 | await dataAnimation.BeginElementAsync();
83 | await colorAnimation.BeginElementAsync();
84 | }
85 |
86 | public async ValueTask DisposeAsync()
87 | {
88 | await dataAnimation.RemoveOnBeginEventListenerAsync(beginEventListener);
89 | await dataAnimation.RemoveOnBeginEventListenerAsync(endEventListener);
90 | await dataAnimation.RemoveOnBeginEventListenerAsync(repeatEventListener);
91 | }
92 | }
--------------------------------------------------------------------------------
/samples/KristofferStrube.Blazor.SVGAnimation.WebAppSample/Components/Pages/Loading.razor:
--------------------------------------------------------------------------------
1 | @page "/Loading"
2 | @rendermode InteractiveServer
3 | @using KristofferStrube.Blazor.DOM
4 | @implements IAsyncDisposable
5 | @inject IJSRuntime JSRuntime
6 |
7 | Blazor.SVGAnimation - Loading Animation
8 | We can begin
an animation, have it run indefinite
, and end
it at a full cycle to have a nice stop
9 |
10 | @switch (Status)
11 | {
12 | case AnimationStatus.Still:
13 | Start Loading Animation
14 | break;
15 | case AnimationStatus.Running:
16 | End Loading Animation
17 | break;
18 | case AnimationStatus.Stopping:
19 | Stopping ...
20 | break;
21 | }
22 |
23 |
24 |
25 |
26 |
32 |
33 |
34 |
42 |
43 |
44 |
45 | @code {
46 | protected ElementReference arrowAnimateElement { get; set; }
47 | protected SVGAnimationElement? arrowAnimation;
48 | protected EventListener beginEventListener = default!;
49 | protected EventListener endEventListener = default!;
50 | protected AnimationStatus Status = AnimationStatus.Still;
51 | protected float animationTime = 2f;
52 |
53 | protected override async Task OnAfterRenderAsync(bool firstRender)
54 | {
55 | if (!firstRender) return;
56 |
57 | arrowAnimation = await SVGAnimationElement.CreateAsync(JSRuntime, arrowAnimateElement);
58 |
59 | beginEventListener = await arrowAnimation.AddOnBeginEventListenerAsync(_ =>
60 | {
61 | Status = AnimationStatus.Running;
62 | StateHasChanged();
63 | return Task.CompletedTask;
64 | });
65 | endEventListener = await arrowAnimation.AddOnEndEventListenerAsync(_ =>
66 | {
67 | Status = AnimationStatus.Still;
68 | StateHasChanged();
69 | return Task.CompletedTask;
70 | });
71 | }
72 |
73 | private async void StartAnimation()
74 | {
75 | if (Status is not AnimationStatus.Still || arrowAnimation is null)
76 | {
77 | return;
78 | }
79 | await arrowAnimation.BeginElementAsync();
80 | }
81 |
82 | private async void EndAnimation()
83 | {
84 | if (Status is not AnimationStatus.Running || arrowAnimation is null)
85 | {
86 | return;
87 | }
88 | Status = AnimationStatus.Stopping;
89 | StateHasChanged();
90 | var startTime = await arrowAnimation.GetStartTimeAsync();
91 | var timeInPeriod = (await arrowAnimation.GetCurrentTimeAsync() - startTime) % animationTime;
92 | await arrowAnimation.EndElementAtAsync(animationTime - timeInPeriod);
93 | }
94 |
95 | protected enum AnimationStatus
96 | {
97 | Still,
98 | Running,
99 | Stopping
100 | }
101 |
102 | public async ValueTask DisposeAsync()
103 | {
104 | if (arrowAnimation is null) return;
105 |
106 | await arrowAnimation.RemoveOnBeginEventListenerAsync(beginEventListener);
107 | await arrowAnimation.RemoveOnEndEventListenerAsync(endEventListener);
108 | }
109 | }
--------------------------------------------------------------------------------
/samples/KristofferStrube.Blazor.SVGAnimation.WebAppSample/Components/Routes.razor:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/samples/KristofferStrube.Blazor.SVGAnimation.WebAppSample/Components/_Imports.razor:
--------------------------------------------------------------------------------
1 | @using System.Net.Http
2 | @using System.Net.Http.Json
3 | @using Microsoft.AspNetCore.Components.Forms
4 | @using Microsoft.AspNetCore.Components.Routing
5 | @using Microsoft.AspNetCore.Components.Web
6 | @using static Microsoft.AspNetCore.Components.Web.RenderMode
7 | @using Microsoft.AspNetCore.Components.Web.Virtualization
8 | @using Microsoft.JSInterop
9 | @using KristofferStrube.Blazor.SVGAnimation.WebAppSample
10 | @using KristofferStrube.Blazor.SVGAnimation.WebAppSample.Components
11 |
--------------------------------------------------------------------------------
/samples/KristofferStrube.Blazor.SVGAnimation.WebAppSample/Confetti/Confetti.razor:
--------------------------------------------------------------------------------
1 | @using KristofferStrube.Blazor.DOM.Extensions
2 | @using Microsoft.JSInterop
3 | @implements IDisposable
4 |
5 |
6 | @foreach (var confettiPiece in confettiPieces)
7 | {
8 |
9 | }
10 |
11 |
12 | @code {
13 | private double width;
14 | private double height;
15 | private bool playing = false;
16 |
17 | [Inject]
18 | public required IJSRuntime JSRuntime { get; set; }
19 |
20 | [Inject]
21 | public required ConfettiService ConfettiService { get; set; }
22 |
23 | List confettiPieces = new();
24 |
25 | protected override async Task OnAfterRenderAsync(bool firstRender)
26 | {
27 | if (!firstRender) return;
28 |
29 | ConfettiService.Activated += HandleActivated;
30 |
31 | IJSObjectReference windowReference = await JSRuntime.InvokeAsync("window.valueOf");
32 |
33 | var helper = await JSRuntime.GetHelperAsync();
34 | width = await helper.InvokeAsync("getAttribute", windowReference, "innerWidth");
35 | height = await helper.InvokeAsync("getAttribute", windowReference, "innerHeight");
36 | }
37 |
38 | public record ClientRect(double x, double y, double width, double height);
39 |
40 | public async void HandleActivated(object? sender, ConfettiOptions options)
41 | {
42 | if (playing) return;
43 |
44 | (double X, double Y)? originPosition = null;
45 | if (options.Origin is { } origin)
46 | {
47 | var helper = await JSRuntime.GetHelperAsync();
48 | var jSInstance = await helper.InvokeAsync("getJSReference", origin);
49 | var client = await jSInstance.InvokeAsync("getBoundingClientRect");
50 | originPosition = (X: client.x + client.width / 2, Y: client.y + client.height / 2);
51 | }
52 |
53 | confettiPieces = Enumerable
54 | .Range(0, options.Pieces)
55 | .Select(i =>
56 | new ConfettiPiece(
57 | options.Colors[Random.Shared.Next(options.Colors.Length)],
58 | options.Milliseconds + (int)((Random.Shared.NextDouble() * 2 - 1) * options.VariationInMilliseconds),
59 | options.Mode switch
60 | {
61 | ConfettiOriginMode.FromBottomCenter => FromBottomCenterPositions(),
62 | ConfettiOriginMode.FromElement when originPosition is not null => FromElementPositions(originPosition.Value),
63 | _ => FromBottomCenterPositions()
64 | }
65 | )
66 | ).ToList();
67 | playing = true;
68 | StateHasChanged();
69 | await Task.Delay(options.Milliseconds + options.VariationInMilliseconds);
70 | confettiPieces = new();
71 | playing = false;
72 | StateHasChanged();
73 | }
74 |
75 | private (double X, double Y)[] FromBottomCenterPositions()
76 | {
77 | var x = Random.Shared.NextDouble() - 0.5;
78 | var y = Random.Shared.NextDouble();
79 | var end = Random.Shared.NextDouble();
80 | return [(width / 2, height), (width / 2 + x * width / 3 * 2, height / 2 - y * height / 2), (width / 2 + x * width / 3 * 4, height + end * height / 3)];
81 | }
82 |
83 | private (double X, double Y)[] FromElementPositions((double x, double y) originPosition)
84 | {
85 | var x = Random.Shared.NextDouble() - 0.5;
86 | var y = Random.Shared.NextDouble();
87 | var end = Random.Shared.NextDouble();
88 | return [originPosition, (originPosition.x + x * width / 3 * 2, height / 2 - y * height / 2), (originPosition.x + x * width / 3 * 4, height + end * height / 3)];
89 | }
90 |
91 | public void Dispose()
92 | {
93 | ConfettiService.Activated -= HandleActivated;
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/samples/KristofferStrube.Blazor.SVGAnimation.WebAppSample/Confetti/ConfettiAnimator.razor:
--------------------------------------------------------------------------------
1 | @using Microsoft.JSInterop
2 |
3 |
8 |
9 |
10 | @code {
11 | protected ElementReference animateReference { get; set; }
12 | protected SVGAnimationElement? animationElement;
13 |
14 | [Inject]
15 | public required IJSRuntime JSRuntime { get; set; }
16 |
17 | [Parameter, EditorRequired]
18 | public required ConfettiPiece ConfettiPiece { get; set; }
19 |
20 | protected override async Task OnAfterRenderAsync(bool firstRender)
21 | {
22 | if (!firstRender) return;
23 |
24 | animationElement = await SVGAnimationElement.CreateAsync(JSRuntime, animateReference);
25 |
26 | await animationElement.BeginElementAsync();
27 | await animationElement.EndElementAtAsync(ConfettiPiece.Milliseconds);
28 | }
29 |
30 | private string SmoothPath((double X, double Y)[] points)
31 | {
32 | // Parameter for smoothness of path in interval [0, 0.5]
33 | double smoothness = 1.0 / 3.0;
34 |
35 | var result = "";
36 | if (points.Length >= 2)
37 | {
38 | result = $"M {points[0].X.AsString()} {points[0].Y.AsString()} ";
39 | for (int i = 1; i < points.Length - 1; i++)
40 | {
41 | result += $"S {(points[i - 1].X * smoothness / 2 + points[i].X - points[i + 1].X * smoothness / 2).AsString()} {(points[i - 1].Y * smoothness / 2 + points[i].Y - points[i + 1].Y * smoothness / 2).AsString()} {points[i].X.AsString()} {points[i].Y.AsString()} ";
42 | }
43 | result += $"S {(points[^2].X * smoothness + points[^1].X * (1 - smoothness)).AsString()} {(points[^2].Y * smoothness + points[^1].Y * (1 - smoothness)).AsString()} {points[^1].X.AsString()} {points[^1].Y.AsString()} ";
44 | }
45 | return result;
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/samples/KristofferStrube.Blazor.SVGAnimation.WebAppSample/Confetti/ConfettiOptions.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Components;
2 |
3 | namespace KristofferStrube.Blazor.SVGAnimation.WebAppSample.Confetti;
4 |
5 | public class ConfettiOptions : EventArgs
6 | {
7 | public int Pieces { get; set; } = 300;
8 |
9 | public int Milliseconds { get; set; } = 1000;
10 |
11 | public int VariationInMilliseconds { get; set; } = 200;
12 |
13 | public string[] Colors { get; set; } = ["#F4F", "#44F", "#4F4", "#F44", "#9F0"];
14 |
15 | public ConfettiOriginMode Mode { get; set; } = ConfettiOriginMode.FromBottomCenter;
16 |
17 | public ElementReference? Origin { get; set; }
18 | }
19 |
--------------------------------------------------------------------------------
/samples/KristofferStrube.Blazor.SVGAnimation.WebAppSample/Confetti/ConfettiOriginMode.cs:
--------------------------------------------------------------------------------
1 | namespace KristofferStrube.Blazor.SVGAnimation.WebAppSample.Confetti;
2 |
3 | public enum ConfettiOriginMode
4 | {
5 | FromBottomCenter,
6 | FromElement
7 | }
8 |
--------------------------------------------------------------------------------
/samples/KristofferStrube.Blazor.SVGAnimation.WebAppSample/Confetti/ConfettiPiece.cs:
--------------------------------------------------------------------------------
1 | namespace KristofferStrube.Blazor.SVGAnimation.WebAppSample.Confetti;
2 |
3 | public record struct ConfettiPiece(string Color, int Milliseconds, (double X, double Y)[] Positions);
--------------------------------------------------------------------------------
/samples/KristofferStrube.Blazor.SVGAnimation.WebAppSample/Confetti/ConfettiService.cs:
--------------------------------------------------------------------------------
1 | namespace KristofferStrube.Blazor.SVGAnimation.WebAppSample.Confetti;
2 |
3 | public class ConfettiService
4 | {
5 | public event ActivatedEventHandler Activated;
6 |
7 | public void Activate(ConfettiOptions options)
8 | {
9 | Activated?.Invoke(this, options);
10 | }
11 |
12 | public delegate void ActivatedEventHandler(Object sender, ConfettiOptions e);
13 | }
14 |
--------------------------------------------------------------------------------
/samples/KristofferStrube.Blazor.SVGAnimation.WebAppSample/Confetti/DoubleExtensions.cs:
--------------------------------------------------------------------------------
1 | using System.Globalization;
2 |
3 | namespace KristofferStrube.Blazor.SVGAnimation.WebAppSample.Confetti;
4 |
5 | public static class DoubleExtensions
6 | {
7 | public static string AsString(this double d) => d.ToString(CultureInfo.InvariantCulture);
8 | }
--------------------------------------------------------------------------------
/samples/KristofferStrube.Blazor.SVGAnimation.WebAppSample/Confetti/IServiceExtensions.cs:
--------------------------------------------------------------------------------
1 | namespace KristofferStrube.Blazor.SVGAnimation.WebAppSample.Confetti;
2 |
3 | public static class IServiceExtensions
4 | {
5 | public static IServiceCollection AddConfettiService(this IServiceCollection serviceCollection)
6 | {
7 | return serviceCollection.AddScoped();
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/samples/KristofferStrube.Blazor.SVGAnimation.WebAppSample/CustomRenderModes.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Components.Web;
2 |
3 | namespace KristofferStrube.Blazor.SVGAnimation.WebAppSample;
4 | public static class CustomRenderModes
5 | {
6 | public static readonly InteractiveServerRenderMode InteractiveServerRenderModeNoPreRender
7 | = new InteractiveServerRenderMode(prerender: false);
8 | }
--------------------------------------------------------------------------------
/samples/KristofferStrube.Blazor.SVGAnimation.WebAppSample/KristofferStrube.Blazor.SVGAnimation.WebAppSample.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net8.0
5 | enable
6 | enable
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/samples/KristofferStrube.Blazor.SVGAnimation.WebAppSample/Program.cs:
--------------------------------------------------------------------------------
1 | using KristofferStrube.Blazor.SVGAnimation.WebAppSample.Components;
2 | using KristofferStrube.Blazor.SVGAnimation.WebAppSample.Confetti;
3 | using Microsoft.AspNetCore.Components.Web;
4 |
5 | var builder = WebApplication.CreateBuilder(args);
6 |
7 | // Add services to the container.
8 | builder.Services
9 | .AddConfettiService()
10 | .AddRazorComponents()
11 | .AddInteractiveServerComponents();
12 |
13 | var app = builder.Build();
14 |
15 | // Configure the HTTP request pipeline.
16 | if (!app.Environment.IsDevelopment())
17 | {
18 | app.UseExceptionHandler("/Error", createScopeForErrors: true);
19 | // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
20 | app.UseHsts();
21 | }
22 |
23 | app.UseHttpsRedirection();
24 |
25 | app.UseStaticFiles();
26 | app.UseAntiforgery();
27 |
28 | app.MapRazorComponents()
29 | .AddInteractiveServerRenderMode();
30 |
31 | app.Run();
32 |
33 |
--------------------------------------------------------------------------------
/samples/KristofferStrube.Blazor.SVGAnimation.WebAppSample/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "http://json.schemastore.org/launchsettings.json",
3 | "iisSettings": {
4 | "windowsAuthentication": false,
5 | "anonymousAuthentication": true,
6 | "iisExpress": {
7 | "applicationUrl": "http://localhost:41357",
8 | "sslPort": 44353
9 | }
10 | },
11 | "profiles": {
12 | "http": {
13 | "commandName": "Project",
14 | "dotnetRunMessages": true,
15 | "launchBrowser": true,
16 | "applicationUrl": "http://localhost:5255",
17 | "environmentVariables": {
18 | "ASPNETCORE_ENVIRONMENT": "Development"
19 | }
20 | },
21 | "https": {
22 | "commandName": "Project",
23 | "dotnetRunMessages": true,
24 | "launchBrowser": true,
25 | "applicationUrl": "https://localhost:7052;http://localhost:5255",
26 | "environmentVariables": {
27 | "ASPNETCORE_ENVIRONMENT": "Development"
28 | }
29 | },
30 | "IIS Express": {
31 | "commandName": "IISExpress",
32 | "launchBrowser": true,
33 | "environmentVariables": {
34 | "ASPNETCORE_ENVIRONMENT": "Development"
35 | }
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/samples/KristofferStrube.Blazor.SVGAnimation.WebAppSample/appsettings.Development.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Information",
5 | "Microsoft.AspNetCore": "Warning"
6 | }
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/samples/KristofferStrube.Blazor.SVGAnimation.WebAppSample/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Information",
5 | "Microsoft.AspNetCore": "Warning"
6 | }
7 | },
8 | "AllowedHosts": "*"
9 | }
10 |
--------------------------------------------------------------------------------
/samples/KristofferStrube.Blazor.SVGAnimation.WebAppSample/wwwroot/app.css:
--------------------------------------------------------------------------------
1 | html, body {
2 | font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
3 | }
4 |
5 | a, .btn-link {
6 | color: #006bb7;
7 | }
8 |
9 | .btn-primary {
10 | color: #fff;
11 | background-color: #1b6ec2;
12 | border-color: #1861ac;
13 | }
14 |
15 | .btn:focus, .btn:active:focus, .btn-link.nav-link:focus, .form-control:focus, .form-check-input:focus {
16 | box-shadow: 0 0 0 0.1rem white, 0 0 0 0.25rem #258cfb;
17 | }
18 |
19 | .content {
20 | padding-top: 1.1rem;
21 | }
22 |
23 | h1:focus {
24 | outline: none;
25 | }
26 |
27 | .valid.modified:not([type=checkbox]) {
28 | outline: 1px solid #26b050;
29 | }
30 |
31 | .invalid {
32 | outline: 1px solid #e50000;
33 | }
34 |
35 | .validation-message {
36 | color: #e50000;
37 | }
38 |
39 | .blazor-error-boundary {
40 | background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121;
41 | padding: 1rem 1rem 1rem 3.7rem;
42 | color: white;
43 | }
44 |
45 | .blazor-error-boundary::after {
46 | content: "An error has occurred."
47 | }
48 |
49 | .darker-border-checkbox.form-check-input {
50 | border-color: #929292;
51 | }
52 |
--------------------------------------------------------------------------------
/samples/KristofferStrube.Blazor.SVGAnimation.WebAppSample/wwwroot/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/KristofferStrube/Blazor.SVGAnimation/e39f7386e0b5bf7e7c98ef0441c181dc8c812d5f/samples/KristofferStrube.Blazor.SVGAnimation.WebAppSample/wwwroot/favicon.png
--------------------------------------------------------------------------------
/src/KristofferStrube.Blazor.SVGAnimation/Extensions/IJSRuntimeExtensions.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.JSInterop;
2 |
3 | namespace KristofferStrube.Blazor.SVGAnimation.Extensions;
4 |
5 | internal static class IJSRuntimeExtensions
6 | {
7 | private const string scriptUrl = "./_content/KristofferStrube.Blazor.SVGAnimation/KristofferStrube.Blazor.SVGAnimation.js";
8 |
9 | internal static async Task GetHelperAsync(this IJSRuntime jSRuntime)
10 | {
11 | return await jSRuntime.InvokeAsync(
12 | "import", scriptUrl);
13 | }
14 | internal static async Task GetInProcessHelperAsync(this IJSRuntime jSRuntime)
15 | {
16 | return await jSRuntime.InvokeAsync(
17 | "import", scriptUrl);
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/KristofferStrube.Blazor.SVGAnimation/Extensions/IServiceCollectionExtensions.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.DependencyInjection;
2 | using Microsoft.JSInterop;
3 |
4 | namespace KristofferStrube.Blazor.SVGAnimation;
5 |
6 | public static class IServiceCollectionExtensions
7 | {
8 | [Obsolete("This doesn't follow the pattern for creation of wrapper object that we wish to continue with so it will be removed in the next major release. Use one of the static SVGAnimationElement.CreateAsync methods instead.")]
9 | public static IServiceCollection AddSVGAnimationService(this IServiceCollection services)
10 | {
11 | return services.AddScoped(sp => new SVGAnimationService(sp.GetRequiredService()));
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/KristofferStrube.Blazor.SVGAnimation/ISVGAnimationService.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Components;
2 |
3 | namespace KristofferStrube.Blazor.SVGAnimation
4 | {
5 | public interface ISVGAnimationService
6 | {
7 | ValueTask DisposeAsync();
8 |
9 | [Obsolete("This doesn't follow the pattern for creation of wrapper object that we wish to continue with so it will be removed in the next major release. Use one of the static SVGAnimationElement.CreateAsync methods instead.")]
10 | ValueTask GreateSVGAnimationElement(ElementReference elementReference);
11 | }
12 | }
--------------------------------------------------------------------------------
/src/KristofferStrube.Blazor.SVGAnimation/KristofferStrube.Blazor.SVGAnimation.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net7.0
5 | enable
6 | enable
7 | SVG Animation API wrapper
8 | SVG Animation API wrapper implementation for Blazor.
9 | KristofferStrube.Blazor.SVGAnimation
10 | Blazor;Wasm;Wrapper;SVG;Animation;SVGAnimation;JSInterop
11 | https://github.com/KristofferStrube/Blazor.SVGAnimation
12 | git
13 | MIT
14 | 0.2.0
15 | Kristoffer Strube
16 | README.md
17 | icon.png
18 |
19 |
20 |
21 |
22 | True
23 | \
24 |
25 |
26 | True
27 | \
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/src/KristofferStrube.Blazor.SVGAnimation/SVGAnimationElement.InProcess.cs:
--------------------------------------------------------------------------------
1 | using KristofferStrube.Blazor.DOM;
2 | using KristofferStrube.Blazor.DOM.Extensions;
3 | using KristofferStrube.Blazor.WebIDL;
4 | using Microsoft.AspNetCore.Components;
5 | using Microsoft.JSInterop;
6 |
7 | namespace KristofferStrube.Blazor.SVGAnimation;
8 |
9 | public class SVGAnimationElementInProcess : SVGAnimationElement, IEventTargetInProcess
10 | {
11 | protected readonly IJSInProcessObjectReference domInProcessHelper;
12 | protected readonly IJSInProcessObjectReference inProcessHelper;
13 |
14 | public new IJSInProcessObjectReference JSReference { get; set; }
15 |
16 | public static async Task CreateAsync(IJSRuntime jSRuntime, IJSInProcessObjectReference jSReference)
17 | {
18 | IJSInProcessObjectReference domInProcessHelper = await DOM.Extensions.IJSRuntimeExtensions.GetInProcessHelperAsync(jSRuntime);
19 | IJSInProcessObjectReference inProcessHelper = await Extensions.IJSRuntimeExtensions.GetInProcessHelperAsync(jSRuntime);
20 | return new SVGAnimationElementInProcess(jSRuntime, domInProcessHelper, inProcessHelper, jSReference);
21 | }
22 |
23 | public static new async Task CreateAsync(IJSRuntime jSRuntime, ElementReference elementReference)
24 | {
25 | IJSInProcessObjectReference domInProcessHelper = await DOM.Extensions.IJSRuntimeExtensions.GetInProcessHelperAsync(jSRuntime);
26 | IJSInProcessObjectReference inProcessHelper = await Extensions.IJSRuntimeExtensions.GetInProcessHelperAsync(jSRuntime);
27 | IJSInProcessObjectReference jSInstance = await inProcessHelper.InvokeAsync("getJSReference", elementReference);
28 | return new SVGAnimationElementInProcess(jSRuntime, domInProcessHelper, inProcessHelper, jSInstance);
29 | }
30 |
31 | protected SVGAnimationElementInProcess(IJSRuntime jSRuntime, IJSInProcessObjectReference domInProcessHelper, IJSInProcessObjectReference inProcessHelper, IJSInProcessObjectReference jSReference) : base(jSRuntime, jSReference)
32 | {
33 | this.domInProcessHelper = domInProcessHelper;
34 | this.inProcessHelper = inProcessHelper;
35 | JSReference = jSReference;
36 | }
37 |
38 | public new IJSObjectReference TargetElement => inProcessHelper.Invoke("getAttribute", JSReference, "targetElement");
39 |
40 | ///
41 | public void AddEventListener(string type, EventListenerInProcess? callback, AddEventListenerOptions? options = null)
42 | where TEvent : Event, IJSCreatable where TInProcessEvent : IJSInProcessCreatable
43 | {
44 | this.AddEventListener(domInProcessHelper, type, callback, options);
45 | }
46 |
47 | ///
48 | public void AddEventListener(EventListenerInProcess? callback, AddEventListenerOptions? options = null)
49 | where TEvent : Event, IJSCreatable where TInProcessEvent : IJSInProcessCreatable
50 | {
51 | this.AddEventListener(domInProcessHelper, callback, options);
52 | }
53 |
54 | ///
55 | public void RemoveEventListener(string type, EventListenerInProcess? callback, EventListenerOptions? options = null)
56 | where TEvent : Event, IJSCreatable where TInProcessEvent : IJSInProcessCreatable
57 | {
58 | this.RemoveEventListener(domInProcessHelper, type, callback, options);
59 | }
60 |
61 | ///
62 | public void RemoveEventListener(EventListenerInProcess? callback, EventListenerOptions? options = null)
63 | where TEvent : Event, IJSCreatable where TInProcessEvent : IJSInProcessCreatable
64 | {
65 | this.RemoveEventListener(domInProcessHelper, callback, options);
66 | }
67 |
68 | public bool DispatchEvent(Event eventInstance)
69 | {
70 | return IEventTargetInProcessExtensions.DispatchEvent(this, eventInstance);
71 | }
72 |
73 | public async Task> AddOnBeginEventListenerAsync(Action callback, AddEventListenerOptions? options = null)
74 | {
75 | EventListenerInProcess eventListener = await EventListenerInProcess.CreateAsync(JSRuntime, callback);
76 | AddEventListener("beginEvent", eventListener, options);
77 | return eventListener;
78 | }
79 |
80 | public void RemoveOnBeginEventListener(EventListenerInProcess callback, EventListenerOptions? options = null)
81 | {
82 | RemoveEventListener("beginEvent", callback, options);
83 | }
84 |
85 | public async Task> AddOnEndEventListenerAsync(Action callback, AddEventListenerOptions? options = null)
86 | {
87 | EventListenerInProcess eventListener = await EventListenerInProcess.CreateAsync(JSRuntime, callback);
88 | AddEventListener("endEvent", eventListener, options);
89 | return eventListener;
90 | }
91 |
92 | public void RemoveOnEndEventListener(EventListenerInProcess callback, EventListenerOptions? options = null)
93 | {
94 | RemoveEventListener("endEvent", callback, options);
95 | }
96 |
97 | public async Task> AddOnRepeatEventListenerAsync(Action callback, AddEventListenerOptions? options = null)
98 | {
99 | EventListenerInProcess eventListener = await EventListenerInProcess.CreateAsync(JSRuntime, callback);
100 | AddEventListener("repeatEvent", eventListener, options);
101 | return eventListener;
102 | }
103 |
104 | public void RemoveOnRepeatEventListener(EventListenerInProcess callback, EventListenerOptions? options = null)
105 | {
106 | RemoveEventListener("repeatEvent", callback, options);
107 | }
108 |
109 | public float GetStartTime()
110 | {
111 | return JSReference.Invoke("getStartTime");
112 | }
113 |
114 | public float GetCurrentTime()
115 | {
116 | return JSReference.Invoke("getCurrentTime");
117 | }
118 |
119 | public float GetSimpleDuration()
120 | {
121 | return JSReference.Invoke("getSimpleDuration");
122 | }
123 |
124 | public void BeginElement()
125 | {
126 | JSReference.InvokeVoid("beginElement");
127 | }
128 |
129 | public void BeginElementAt(float offset)
130 | {
131 | JSReference.InvokeVoid("beginElementAt", offset);
132 | }
133 |
134 | public void EndElement()
135 | {
136 | JSReference.InvokeVoid("endElement");
137 | }
138 |
139 | public void EndElementAt(float offset)
140 | {
141 | JSReference.InvokeVoid("endElementAt", offset);
142 | }
143 |
144 | public new async ValueTask DisposeAsync()
145 | {
146 | await base.DisposeAsync();
147 | await domInProcessHelper.DisposeAsync();
148 | await inProcessHelper.DisposeAsync();
149 | GC.SuppressFinalize(this);
150 | }
151 | }
152 |
--------------------------------------------------------------------------------
/src/KristofferStrube.Blazor.SVGAnimation/SVGAnimationElement.cs:
--------------------------------------------------------------------------------
1 | using KristofferStrube.Blazor.DOM;
2 | using KristofferStrube.Blazor.SVGAnimation.Extensions;
3 | using Microsoft.AspNetCore.Components;
4 | using Microsoft.JSInterop;
5 |
6 | namespace KristofferStrube.Blazor.SVGAnimation;
7 |
8 | ///
9 | /// SVGAnimationElement browser specs
10 | ///
11 | public class SVGAnimationElement : EventTarget
12 | {
13 | // Old Obsolete fields.
14 | protected readonly IJSInProcessObjectReference helper;
15 |
16 | // New fields that will be the only ones used after the next major version
17 | protected readonly Lazy> svgAnimationHelperTask;
18 |
19 | public static new Task CreateAsync(IJSRuntime jSRuntime, IJSObjectReference jSReference)
20 | {
21 | return Task.FromResult(new(jSRuntime, jSReference));
22 | }
23 |
24 | public static new async Task CreateAsync(IJSRuntime jSRuntime, ElementReference elementReference)
25 | {
26 | IJSObjectReference helper = await jSRuntime.GetHelperAsync();
27 | IJSObjectReference jSInstance = await helper.InvokeAsync("getJSReference", elementReference);
28 | return new(jSRuntime, jSInstance);
29 | }
30 |
31 | [Obsolete("This is not compatible with Blazor WASM so it will be removed in the next major version. Either use the SVGAnimationElement(IJSRuntime jSRuntime, IJSObjectReference jSReference) constructor instead.")]
32 | internal SVGAnimationElement(IJSRuntime jSRuntime, IJSObjectReference jSReference, IJSInProcessObjectReference helper) : this(jSRuntime, jSReference)
33 | {
34 | this.helper = helper;
35 | }
36 |
37 | #pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. This will be removed once we move await from the IJSInProcessObjectReference in next major release.
38 | protected SVGAnimationElement(IJSRuntime jSRuntime, IJSObjectReference jSReference) : base(jSRuntime, jSReference)
39 | #pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
40 | {
41 | svgAnimationHelperTask = new(jSRuntime.GetHelperAsync);
42 | }
43 |
44 | [Obsolete("This is not compatible with Blazor WASM so it will be removed in the next major version. Either use the SVGAnimationElementInProcess variant or use the GetTargetElementAsync method instead.")]
45 | public IJSObjectReference TargetElement => helper.Invoke("getAttribute", JSReference, "targetElement");
46 |
47 | public async Task GetTargetElementAsync()
48 | {
49 | return await helper.InvokeAsync("getAttribute", JSReference, "targetElement");
50 | }
51 |
52 | public async Task> AddOnBeginEventListenerAsync(Func callback, AddEventListenerOptions? options = null)
53 | {
54 | EventListener eventListener = await EventListener.CreateAsync(JSRuntime, callback);
55 | await AddEventListenerAsync("beginEvent", eventListener, options);
56 | return eventListener;
57 | }
58 |
59 | public async Task RemoveOnBeginEventListenerAsync(EventListener callback, EventListenerOptions? options = null)
60 | {
61 | await RemoveEventListenerAsync("beginEvent", callback, options);
62 | }
63 |
64 | [Obsolete("This used an old way to make events where you could not unsubscribe so it will be removed in the next major version. Use AddOnBeginEventListener instead.")]
65 | public async ValueTask SubscribeToBeginAsync(Action action)
66 | {
67 | EventListener eventListener = await EventListener.CreateAsync(JSRuntime, (Event e) => { action(); return Task.CompletedTask; });
68 | await AddEventListenerAsync("beginEvent", eventListener);
69 | }
70 |
71 | public async Task> AddOnEndEventListenerAsync(Func callback, AddEventListenerOptions? options = null)
72 | {
73 | EventListener eventListener = await EventListener.CreateAsync(JSRuntime, callback);
74 | await AddEventListenerAsync("endEvent", eventListener, options);
75 | return eventListener;
76 | }
77 |
78 | public async Task RemoveOnEndEventListenerAsync(EventListener callback, EventListenerOptions? options = null)
79 | {
80 | await RemoveEventListenerAsync("endEvent", callback, options);
81 | }
82 |
83 | [Obsolete("This used an old way to make events where you could not unsubscribe so it will be removed in the next major version. Use AddOnEndEventListener instead.")]
84 | public async ValueTask SubscribeToEndAsync(Action action)
85 | {
86 | EventListener eventListener = await EventListener.CreateAsync(JSRuntime, (Event e) => { action(); return Task.CompletedTask; });
87 | await AddEventListenerAsync("endEvent", eventListener);
88 | }
89 |
90 | public async Task> AddOnRepeatEventListenerAsync(Func callback, AddEventListenerOptions? options = null)
91 | {
92 | EventListener eventListener = await EventListener.CreateAsync(JSRuntime, callback);
93 | await AddEventListenerAsync("repeatEvent", eventListener, options);
94 | return eventListener;
95 | }
96 |
97 | public async Task RemoveOnRepeatEventListenerAsync(EventListener callback, EventListenerOptions? options = null)
98 | {
99 | await RemoveEventListenerAsync("repeatEvent", callback, options);
100 | }
101 |
102 | [Obsolete("This used an old way to make events where you could not unsubscribe so it will be removed in the next major version. Use AddOnRepeatEventListener instead.")]
103 | public async ValueTask SubscribeToRepeatAsync(Action action)
104 | {
105 | EventListener eventListener = await EventListener.CreateAsync(JSRuntime, (Event e) => { action(); return Task.CompletedTask; });
106 | await AddEventListenerAsync("repeatEvent", eventListener);
107 | }
108 |
109 | public async ValueTask GetStartTimeAsync()
110 | {
111 | return await JSReference.InvokeAsync("getStartTime");
112 | }
113 |
114 | public async ValueTask GetCurrentTimeAsync()
115 | {
116 | return await JSReference.InvokeAsync("getCurrentTime");
117 | }
118 |
119 | public async ValueTask GetSimpleDurationAsync()
120 | {
121 | return await JSReference.InvokeAsync("getSimpleDuration");
122 | }
123 |
124 | public async ValueTask BeginElementAsync()
125 | {
126 | await JSReference.InvokeVoidAsync("beginElement");
127 | }
128 |
129 | public async ValueTask BeginElementAtAsync(float offset)
130 | {
131 | await JSReference.InvokeVoidAsync("beginElementAt", offset);
132 | }
133 |
134 | public async ValueTask EndElementAsync()
135 | {
136 | await JSReference.InvokeVoidAsync("endElement");
137 | }
138 |
139 | public async ValueTask EndElementAtAsync(float offset)
140 | {
141 | await JSReference.InvokeVoidAsync("endElementAt", offset);
142 | }
143 |
144 | public new async ValueTask DisposeAsync()
145 | {
146 | await base.DisposeAsync();
147 | if (svgAnimationHelperTask.IsValueCreated)
148 | {
149 | IJSObjectReference module = await svgAnimationHelperTask.Value;
150 | await module.DisposeAsync();
151 | }
152 | GC.SuppressFinalize(this);
153 | }
154 | }
155 |
--------------------------------------------------------------------------------
/src/KristofferStrube.Blazor.SVGAnimation/SVGAnimationService.cs:
--------------------------------------------------------------------------------
1 | using KristofferStrube.Blazor.SVGAnimation.Extensions;
2 | using Microsoft.AspNetCore.Components;
3 | using Microsoft.JSInterop;
4 |
5 | namespace KristofferStrube.Blazor.SVGAnimation;
6 |
7 | public class SVGAnimationService : IAsyncDisposable, ISVGAnimationService
8 | {
9 | protected readonly IJSRuntime jSRuntime;
10 | private readonly Lazy> helperTask;
11 |
12 | [Obsolete("This doesn't follow the pattern for creation of wrapper object that we wish to continue with so it will be removed in the next major release. Use one of the static SVGAnimationElement.CreateAsync methods instead.")]
13 | public SVGAnimationService(IJSRuntime jsRuntime)
14 | {
15 | this.jSRuntime = jsRuntime;
16 | helperTask = new(jSRuntime.GetInProcessHelperAsync);
17 | }
18 |
19 | [Obsolete("This doesn't follow the pattern for creation of wrapper object that we wish to continue with so it will be removed in the next major release. Use one of the static SVGAnimationElement.CreateAsync methods instead.")]
20 | public async ValueTask GreateSVGAnimationElement(ElementReference elementReference)
21 | {
22 | IJSInProcessObjectReference helper = await helperTask.Value;
23 | IJSObjectReference jsReference = await helper.InvokeAsync("getJSReference", elementReference);
24 | return new SVGAnimationElement(jSRuntime, jsReference, helper);
25 | }
26 |
27 | public async ValueTask DisposeAsync()
28 | {
29 | if (helperTask.IsValueCreated)
30 | {
31 | IJSInProcessObjectReference module = await helperTask.Value;
32 | await module.DisposeAsync();
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/KristofferStrube.Blazor.SVGAnimation/wwwroot/KristofferStrube.Blazor.SVGAnimation.js:
--------------------------------------------------------------------------------
1 | export function getAttribute(object, attribute) { return object[attribute]; }
2 |
3 | export function getJSReference(element) { return element; }
--------------------------------------------------------------------------------