├── .editorconfig ├── .github └── workflows │ ├── ci.yml │ └── publish.yml ├── .gitignore ├── Canvas.sln ├── Directory.Build.props ├── Directory.Build.targets ├── LICENSE ├── README.md ├── src ├── Blazor.Extensions.Canvas.JS │ ├── Blazor.Extensions.Canvas.JS.csproj │ ├── package-lock.json │ ├── package.json │ ├── src │ │ ├── CanvasContextManager.ts │ │ └── InitializeCanvas.ts │ ├── tsconfig.json │ ├── tsfmt.json │ ├── tslint.json │ └── webpack.config.js ├── Blazor.Extensions.Canvas │ ├── BECanvas.razor │ ├── BECanvasComponent.cs │ ├── Blazor.Extensions.Canvas.csproj │ ├── Canvas2D │ │ ├── Canvas2DContext.cs │ │ └── Canvas2DEnums.cs │ ├── CanvasContextExtensions.cs │ ├── Model │ │ └── TextMetrics.cs │ ├── RenderingContext.cs │ ├── WebGL │ │ ├── WebGLContext.cs │ │ ├── WebGLEnums.cs │ │ └── WebGLObjects.cs │ └── wwwroot │ │ └── blazor.extensions.canvas.js ├── Directory.Build.props └── Directory.Build.targets └── test ├── Blazor.Extensions.Canvas.Test.ClientSide ├── App.razor ├── Blazor.Extensions.Canvas.Test.ClientSide.csproj ├── Pages │ ├── Index.razor │ ├── IndexComponent.cs │ ├── WebGL.razor │ ├── WebGLComponent.cs │ └── _Imports.razor ├── Program.cs ├── Shared │ └── MainLayout.razor ├── _Imports.razor └── wwwroot │ ├── favicon.ico │ └── index.html ├── Blazor.Extensions.Canvas.Test.ServerSide ├── App.razor ├── Blazor.Extensions.Canvas.Test.ServerSide.csproj ├── Pages │ ├── Index.razor │ ├── IndexComponent.cs │ ├── WebGL.razor │ ├── WebGLComponent.cs │ ├── _Host.cshtml │ └── _Imports.razor ├── Program.cs ├── Shared │ └── MainLayout.razor ├── Startup.cs ├── _Imports.razor ├── appsettings.Development.json ├── appsettings.json └── wwwroot │ └── favicon.ico └── Directory.Build.props /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://EditorConfig.org 2 | 3 | # This file is the top-most EditorConfig file 4 | root = true 5 | 6 | # All Files 7 | [*] 8 | charset = utf-8 9 | end_of_line = crlf 10 | indent_style = space 11 | indent_size = 4 12 | insert_final_newline = false 13 | trim_trailing_whitespace = true 14 | 15 | # Solution Files 16 | [*.sln] 17 | indent_style = tab 18 | 19 | # XML Project Files 20 | [*.{csproj,vbproj,vcxproj,vcxproj.filters,proj,projitems,shproj}] 21 | indent_size = 2 22 | 23 | # Configuration Files 24 | [*.{json,xml,yml,config,props,targets,nuspec,resx,ruleset,vsixmanifest,vsct}] 25 | indent_size = 2 26 | 27 | # Markdown Files 28 | [*.md] 29 | trim_trailing_whitespace = false 30 | 31 | # Web Files 32 | [*.{htm,html,js,ts,css,scss,less}] 33 | indent_size = 2 34 | insert_final_newline = true 35 | 36 | # Bash Files 37 | [*.sh] 38 | end_of_line = lf 39 | 40 | # Dotnet Code Style Settings 41 | # See https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-code-style-settings-reference 42 | # See http://kent-boogaart.com/blog/editorconfig-reference-for-c-developers 43 | [*.{cs,csx,cake,vb}] 44 | dotnet_sort_system_directives_first = true:warning 45 | dotnet_style_coalesce_expression = true:warning 46 | dotnet_style_collection_initializer = true:warning 47 | dotnet_style_explicit_tuple_names = true:warning 48 | dotnet_style_null_propagation = true:warning 49 | dotnet_style_object_initializer = true:warning 50 | dotnet_style_predefined_type_for_locals_parameters_members = true:warning 51 | dotnet_style_predefined_type_for_member_access = true:warning 52 | dotnet_style_qualification_for_event = true:warning 53 | dotnet_style_qualification_for_field = true:warning 54 | dotnet_style_qualification_for_method = true:warning 55 | dotnet_style_qualification_for_property = true:warning 56 | 57 | # Naming Symbols 58 | # constant_fields - Define constant fields 59 | dotnet_naming_symbols.constant_fields.applicable_kinds = field 60 | dotnet_naming_symbols.constant_fields.required_modifiers = const 61 | # non_private_readonly_fields - Define public, internal and protected readonly fields 62 | dotnet_naming_symbols.non_private_readonly_fields.applicable_accessibilities = public, internal, protected 63 | dotnet_naming_symbols.non_private_readonly_fields.applicable_kinds = field 64 | dotnet_naming_symbols.non_private_readonly_fields.required_modifiers = readonly 65 | # static_readonly_fields - Define static and readonly fields 66 | dotnet_naming_symbols.static_readonly_fields.applicable_kinds = field 67 | dotnet_naming_symbols.static_readonly_fields.required_modifiers = static, readonly 68 | # private_readonly_fields - Define private readonly fields 69 | dotnet_naming_symbols.private_readonly_fields.applicable_accessibilities = private 70 | dotnet_naming_symbols.private_readonly_fields.applicable_kinds = field 71 | dotnet_naming_symbols.private_readonly_fields.required_modifiers = readonly 72 | # public_internal_fields - Define public and internal fields 73 | dotnet_naming_symbols.public_internal_fields.applicable_accessibilities = public, internal 74 | dotnet_naming_symbols.public_internal_fields.applicable_kinds = field 75 | # private_protected_fields - Define private and protected fields 76 | dotnet_naming_symbols.private_protected_fields.applicable_accessibilities = private, protected 77 | dotnet_naming_symbols.private_protected_fields.applicable_kinds = field 78 | # public_symbols - Define any public symbol 79 | dotnet_naming_symbols.public_symbols.applicable_accessibilities = public, internal, protected, protected_internal 80 | dotnet_naming_symbols.public_symbols.applicable_kinds = method, property, event, delegate 81 | # parameters - Defines any parameter 82 | dotnet_naming_symbols.parameters.applicable_kinds = parameter 83 | # non_interface_types - Defines class, struct, enum and delegate types 84 | dotnet_naming_symbols.non_interface_types.applicable_kinds = class, struct, enum, delegate 85 | # interface_types - Defines interfaces 86 | dotnet_naming_symbols.interface_types.applicable_kinds = interface 87 | 88 | # Naming Styles 89 | # camel_case - Define the camelCase style 90 | dotnet_naming_style.camel_case.capitalization = camel_case 91 | # pascal_case - Define the Pascal_case style 92 | dotnet_naming_style.pascal_case.capitalization = pascal_case 93 | # first_upper - The first character must start with an upper-case character 94 | dotnet_naming_style.first_upper.capitalization = first_word_upper 95 | # prefix_interface_interface_with_i - Interfaces must be PascalCase and the first character of an interface must be an 'I' 96 | dotnet_naming_style.prefix_interface_interface_with_i.capitalization = pascal_case 97 | dotnet_naming_style.prefix_interface_interface_with_i.required_prefix = I 98 | # underscore_camel_case - Define the _camelCase style 99 | dotnet_naming_style.underscore_camel_case.capitalization = camel_case 100 | dotnet_naming_style.underscore_camel_case.required_prefix = _ 101 | 102 | # Naming Rules 103 | # Constant fields must be PascalCase 104 | dotnet_naming_rule.constant_fields_must_be_pascal_case.severity = warning 105 | dotnet_naming_rule.constant_fields_must_be_pascal_case.symbols = constant_fields 106 | dotnet_naming_rule.constant_fields_must_be_pascal_case.style = pascal_case 107 | # Public, internal and protected readonly fields must be PascalCase 108 | dotnet_naming_rule.non_private_readonly_fields_must_be_pascal_case.severity = warning 109 | dotnet_naming_rule.non_private_readonly_fields_must_be_pascal_case.symbols = non_private_readonly_fields 110 | dotnet_naming_rule.non_private_readonly_fields_must_be_pascal_case.style = pascal_case 111 | # Static readonly fields must be PascalCase 112 | dotnet_naming_rule.static_readonly_fields_must_be_pascal_case.severity = warning 113 | dotnet_naming_rule.static_readonly_fields_must_be_pascal_case.symbols = static_readonly_fields 114 | dotnet_naming_rule.static_readonly_fields_must_be_pascal_case.style = pascal_case 115 | # Private readonly fields must be _camelCase 116 | dotnet_naming_rule.private_readonly_fields_must_be_camel_case.severity = warning 117 | dotnet_naming_rule.private_readonly_fields_must_be_camel_case.symbols = private_readonly_fields 118 | dotnet_naming_rule.private_readonly_fields_must_be_camel_case.style = underscore_camel_case 119 | # Public and internal fields must be PascalCase 120 | dotnet_naming_rule.public_internal_fields_must_be_pascal_case.severity = warning 121 | dotnet_naming_rule.public_internal_fields_must_be_pascal_case.symbols = public_internal_fields 122 | dotnet_naming_rule.public_internal_fields_must_be_pascal_case.style = pascal_case 123 | # Private and protected fields must be _camelCase 124 | dotnet_naming_rule.private_protected_fields_must_be_camel_case.severity = warning 125 | dotnet_naming_rule.private_protected_fields_must_be_camel_case.symbols = private_protected_fields 126 | dotnet_naming_rule.private_protected_fields_must_be_camel_case.style = underscore_camel_case 127 | # Public members must be capitalized 128 | dotnet_naming_rule.public_members_must_be_capitalized.severity = warning 129 | dotnet_naming_rule.public_members_must_be_capitalized.symbols = public_symbols 130 | dotnet_naming_rule.public_members_must_be_capitalized.style = first_upper 131 | # Parameters must be camelCase 132 | dotnet_naming_rule.parameters_must_be_camel_case.severity = warning 133 | dotnet_naming_rule.parameters_must_be_camel_case.symbols = parameters 134 | dotnet_naming_rule.parameters_must_be_camel_case.style = camel_case 135 | # Class, struct, enum and delegates must be PascalCase 136 | dotnet_naming_rule.non_interface_types_must_be_pascal_case.severity = warning 137 | dotnet_naming_rule.non_interface_types_must_be_pascal_case.symbols = non_interface_types 138 | dotnet_naming_rule.non_interface_types_must_be_pascal_case.style = pascal_case 139 | # Interfaces must be PascalCase and start with an 'I' 140 | dotnet_naming_rule.interface_types_must_be_prefixed_with_i.severity = warning 141 | dotnet_naming_rule.interface_types_must_be_prefixed_with_i.symbols = interface_types 142 | dotnet_naming_rule.interface_types_must_be_prefixed_with_i.style = prefix_interface_interface_with_i 143 | 144 | # C# Code Style Settings 145 | # See https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-code-style-settings-reference 146 | # See http://kent-boogaart.com/blog/editorconfig-reference-for-c-developers 147 | [*.cs,csx,cake] 148 | # Indentation Options 149 | csharp_indent_block_contents = true:warning 150 | csharp_indent_braces = false:warning 151 | csharp_indent_case_contents = true:warning 152 | csharp_indent_labels = no_change:warning 153 | csharp_indent_switch_labels = true:warning 154 | # Style Options 155 | csharp_style_conditional_delegate_call = true:warning 156 | csharp_style_expression_bodied_accessors = true:warning 157 | csharp_style_expression_bodied_constructors = true:warning 158 | csharp_style_expression_bodied_indexers = true:warning 159 | csharp_style_expression_bodied_methods = true:warning 160 | csharp_style_expression_bodied_operators = true:warning 161 | csharp_style_expression_bodied_properties = true:warning 162 | csharp_style_inlined_variable_declaration = true:warning 163 | csharp_style_pattern_matching_over_as_with_null_check = true:warning 164 | csharp_style_pattern_matching_over_is_with_cast_check = true:warning 165 | csharp_style_throw_expression = true:warning 166 | csharp_style_var_elsewhere = true:warning 167 | csharp_style_var_for_built_in_types = true:warning 168 | csharp_style_var_when_type_is_apparent = true:warning 169 | # New Line Options 170 | csharp_new_line_before_catch = true:warning 171 | csharp_new_line_before_else = true:warning 172 | csharp_new_line_before_finally = true:warning 173 | csharp_new_line_before_members_in_anonymous_types = true:warning 174 | csharp_new_line_before_members_in_object_initializers = true:warning 175 | # BUG: Warning level cannot be set https://github.com/dotnet/roslyn/issues/18010 176 | csharp_new_line_before_open_brace = all 177 | csharp_new_line_between_query_expression_clauses = true:warning 178 | # Spacing Options 179 | csharp_space_after_cast = false:warning 180 | csharp_space_after_colon_in_inheritance_clause = true:warning 181 | csharp_space_after_comma = true:warning 182 | csharp_space_after_dot = false:warning 183 | csharp_space_after_keywords_in_control_flow_statements = true:warning 184 | csharp_space_after_semicolon_in_for_statement = true:warning 185 | csharp_space_around_binary_operators = before_and_after:warning 186 | csharp_space_around_declaration_statements = do_not_ignore:warning 187 | csharp_space_before_colon_in_inheritance_clause = true:warning 188 | csharp_space_before_comma = false:warning 189 | csharp_space_before_dot = false:warning 190 | csharp_space_before_semicolon_in_for_statement = false:warning 191 | csharp_space_before_open_square_brackets = false:warning 192 | csharp_space_between_empty_square_brackets = false:warning 193 | csharp_space_between_method_declaration_name_and_open_parenthesis = false:warning 194 | csharp_space_between_method_declaration_parameter_list_parentheses = false:warning 195 | csharp_space_between_method_declaration_empty_parameter_list_parentheses = false:warning 196 | csharp_space_between_method_call_name_and_opening_parenthesis = false:warning 197 | csharp_space_between_method_call_parameter_list_parentheses = false:warning 198 | csharp_space_between_method_call_empty_parameter_list_parentheses = false:warning 199 | csharp_space_between_parentheses = expressions:warning 200 | csharp_space_between_square_brackets = false:warning 201 | # Wrapping Options 202 | csharp_preserve_single_line_blocks = true:warning 203 | csharp_preserve_single_line_statements = false:warning -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - master 7 | 8 | jobs: 9 | build: 10 | 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - uses: actions/checkout@v1 15 | - name: Setup .NET Core 16 | uses: actions/setup-dotnet@v1 17 | with: 18 | dotnet-version: 3.1.301 19 | - name: Build 20 | run: dotnet build --configuration Release -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish 2 | 3 | on: 4 | push: 5 | tags: 6 | - 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10 7 | 8 | jobs: 9 | build: 10 | 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - uses: actions/checkout@v1 15 | - name: Setup .NET Core 16 | uses: actions/setup-dotnet@v1 17 | with: 18 | dotnet-version: 3.1.301 19 | - name: Build 20 | run: dotnet build --configuration Release 21 | - name: Pack 22 | working-directory: src/Blazor.Extensions.Canvas 23 | run: dotnet pack --configuration Release 24 | - name: Push 25 | working-directory: src/Blazor.Extensions.Canvas/bin/Release 26 | run: | 27 | dotnet nuget push Blazor.Extensions.Canvas.*.nupkg -k ${{ secrets.NUGET_KEY }} -s https://api.nuget.org/v3/index.json 28 | - name: Create Release 29 | uses: actions/create-release@master 30 | env: 31 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 32 | with: 33 | tag_name: ${{ github.ref }} 34 | release_name: Release ${{ github.ref }} 35 | draft: false 36 | prerelease: false -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.suo 8 | *.user 9 | *.userosscache 10 | *.sln.docstates 11 | 12 | # User-specific files (MonoDevelop/Xamarin Studio) 13 | *.userprefs 14 | 15 | # Build results 16 | [Dd]ebug/ 17 | [Dd]ebugPublic/ 18 | [Rr]elease/ 19 | [Rr]eleases/ 20 | [Pp]ublish[Oo]utput/ 21 | x64/ 22 | x86/ 23 | bld/ 24 | [Bb]in/ 25 | [Oo]bj/ 26 | [Ll]og/ 27 | dist/ 28 | 29 | # Visual Studio 2015 cache/options directory 30 | .vs/ 31 | # Uncomment if you have tasks that create the project's static files in wwwroot 32 | #wwwroot/ 33 | 34 | # MSTest test Results 35 | [Tt]est[Rr]esult*/ 36 | [Bb]uild[Ll]og.* 37 | 38 | # NUNIT 39 | *.VisualState.xml 40 | TestResult.xml 41 | 42 | # Build Results of an ATL Project 43 | [Dd]ebugPS/ 44 | [Rr]eleasePS/ 45 | dlldata.c 46 | 47 | # .NET Core 48 | project.lock.json 49 | project.fragment.lock.json 50 | artifacts/ 51 | **/Properties/launchSettings.json 52 | 53 | *_i.c 54 | *_p.c 55 | *_i.h 56 | *.ilk 57 | *.meta 58 | *.obj 59 | *.pch 60 | *.pdb 61 | *.pgc 62 | *.pgd 63 | *.rsp 64 | *.sbr 65 | *.tlb 66 | *.tli 67 | *.tlh 68 | *.tmp 69 | *.tmp_proj 70 | *.log 71 | *.vspscc 72 | *.vssscc 73 | .builds 74 | *.pidb 75 | *.svclog 76 | *.scc 77 | 78 | # Chutzpah Test files 79 | _Chutzpah* 80 | 81 | # Visual C++ cache files 82 | ipch/ 83 | *.aps 84 | *.ncb 85 | *.opendb 86 | *.opensdf 87 | *.sdf 88 | *.cachefile 89 | *.VC.db 90 | *.VC.VC.opendb 91 | 92 | # Visual Studio profiler 93 | *.psess 94 | *.vsp 95 | *.vspx 96 | *.sap 97 | 98 | # TFS 2012 Local Workspace 99 | $tf/ 100 | 101 | # Guidance Automation Toolkit 102 | *.gpState 103 | 104 | # ReSharper is a .NET coding add-in 105 | _ReSharper*/ 106 | *.[Rr]e[Ss]harper 107 | *.DotSettings.user 108 | 109 | # JustCode is a .NET coding add-in 110 | .JustCode 111 | 112 | # TeamCity is a build add-in 113 | _TeamCity* 114 | 115 | # DotCover is a Code Coverage Tool 116 | *.dotCover 117 | 118 | # Visual Studio code coverage results 119 | *.coverage 120 | *.coveragexml 121 | 122 | # NCrunch 123 | _NCrunch_* 124 | .*crunch*.local.xml 125 | nCrunchTemp_* 126 | 127 | # MightyMoose 128 | *.mm.* 129 | AutoTest.Net/ 130 | 131 | # Web workbench (sass) 132 | .sass-cache/ 133 | 134 | # Installshield output folder 135 | [Ee]xpress/ 136 | 137 | # DocProject is a documentation generator add-in 138 | DocProject/buildhelp/ 139 | DocProject/Help/*.HxT 140 | DocProject/Help/*.HxC 141 | DocProject/Help/*.hhc 142 | DocProject/Help/*.hhk 143 | DocProject/Help/*.hhp 144 | DocProject/Help/Html2 145 | DocProject/Help/html 146 | 147 | # Click-Once directory 148 | publish/ 149 | 150 | # Publish Web Output 151 | *.[Pp]ublish.xml 152 | *.azurePubxml 153 | # TODO: Comment the next line if you want to checkin your web deploy settings 154 | # but database connection strings (with potential passwords) will be unencrypted 155 | *.pubxml 156 | *.publishproj 157 | 158 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 159 | # checkin your Azure Web App publish settings, but sensitive information contained 160 | # in these scripts will be unencrypted 161 | PublishScripts/ 162 | 163 | # NuGet Packages 164 | *.nupkg 165 | # The packages folder can be ignored because of Package Restore 166 | **/packages/* 167 | # except build/, which is used as an MSBuild target. 168 | !**/packages/build/ 169 | # Uncomment if necessary however generally it will be regenerated when needed 170 | #!**/packages/repositories.config 171 | # NuGet v3's project.json files produces more ignorable files 172 | *.nuget.props 173 | *.nuget.targets 174 | 175 | # Microsoft Azure Build Output 176 | csx/ 177 | *.build.csdef 178 | 179 | # Microsoft Azure Emulator 180 | ecf/ 181 | rcf/ 182 | 183 | # Windows Store app package directories and files 184 | AppPackages/ 185 | BundleArtifacts/ 186 | Package.StoreAssociation.xml 187 | _pkginfo.txt 188 | 189 | # Visual Studio cache files 190 | # files ending in .cache can be ignored 191 | *.[Cc]ache 192 | # but keep track of directories ending in .cache 193 | !*.[Cc]ache/ 194 | 195 | # Others 196 | ClientBin/ 197 | ~$* 198 | *~ 199 | *.dbmdl 200 | *.dbproj.schemaview 201 | *.jfm 202 | *.pfx 203 | *.publishsettings 204 | orleans.codegen.cs 205 | 206 | # Since there are multiple workflows, uncomment next line to ignore bower_components 207 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 208 | #bower_components/ 209 | 210 | # RIA/Silverlight projects 211 | Generated_Code/ 212 | 213 | # Backup & report files from converting an old project file 214 | # to a newer Visual Studio version. Backup files are not needed, 215 | # because we have git ;-) 216 | _UpgradeReport_Files/ 217 | Backup*/ 218 | UpgradeLog*.XML 219 | UpgradeLog*.htm 220 | 221 | # SQL Server files 222 | *.mdf 223 | *.ldf 224 | *.ndf 225 | 226 | # Business Intelligence projects 227 | *.rdl.data 228 | *.bim.layout 229 | *.bim_*.settings 230 | 231 | # Microsoft Fakes 232 | FakesAssemblies/ 233 | 234 | # GhostDoc plugin setting file 235 | *.GhostDoc.xml 236 | 237 | # Node.js Tools for Visual Studio 238 | .ntvs_analysis.dat 239 | node_modules/ 240 | 241 | # Typescript v1 declaration files 242 | typings/ 243 | 244 | # Visual Studio 6 build log 245 | *.plg 246 | 247 | # Visual Studio 6 workspace options file 248 | *.opt 249 | 250 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 251 | *.vbw 252 | 253 | # Visual Studio LightSwitch build output 254 | **/*.HTMLClient/GeneratedArtifacts 255 | **/*.DesktopClient/GeneratedArtifacts 256 | **/*.DesktopClient/ModelManifest.xml 257 | **/*.Server/GeneratedArtifacts 258 | **/*.Server/ModelManifest.xml 259 | _Pvt_Extensions 260 | 261 | # Paket dependency manager 262 | .paket/paket.exe 263 | paket-files/ 264 | 265 | # FAKE - F# Make 266 | .fake/ 267 | 268 | # JetBrains Rider 269 | .idea/ 270 | *.sln.iml 271 | 272 | # CodeRush 273 | .cr/ 274 | 275 | # Python Tools for Visual Studio (PTVS) 276 | __pycache__/ 277 | *.pyc 278 | 279 | # Cake - Uncomment if you are using it 280 | # tools/** 281 | # !tools/packages.config 282 | 283 | # Telerik's JustMock configuration file 284 | *.jmconfig 285 | 286 | # BizTalk build output 287 | *.btp.cs 288 | *.btm.cs 289 | *.odx.cs 290 | *.xsd.cs 291 | dist/ 292 | node_modules/ -------------------------------------------------------------------------------- /Canvas.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.28711.60 5 | MinimumVisualStudioVersion = 15.0.26124.0 6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{B286BCBD-DAD8-4DE7-9334-3DE18DF233AF}" 7 | EndProject 8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{20DAA632-F8AD-4C5F-9E5F-FC82B7CB56A7}" 9 | EndProject 10 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{8A34FDEB-D488-4AE3-887B-8254B33C7A13}" 11 | ProjectSection(SolutionItems) = preProject 12 | .editorconfig = .editorconfig 13 | README.md = README.md 14 | EndProjectSection 15 | EndProject 16 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Blazor.Extensions.Canvas.Test.ClientSide", "test\Blazor.Extensions.Canvas.Test.ClientSide\Blazor.Extensions.Canvas.Test.ClientSide.csproj", "{C4BB6A39-28E6-454D-8679-92562CEAD0A9}" 17 | EndProject 18 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Blazor.Extensions.Canvas.JS", "src\Blazor.Extensions.Canvas.JS\Blazor.Extensions.Canvas.JS.csproj", "{1C49147F-7C73-4962-A71C-6A193970D058}" 19 | EndProject 20 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Blazor.Extensions.Canvas", "src\Blazor.Extensions.Canvas\Blazor.Extensions.Canvas.csproj", "{CB6A1BDA-7768-4A0C-A802-D3AE0C19C120}" 21 | EndProject 22 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Blazor.Extensions.Canvas.Test.ServerSide", "test\Blazor.Extensions.Canvas.Test.ServerSide\Blazor.Extensions.Canvas.Test.ServerSide.csproj", "{D2242105-73D1-4F44-9A80-13D51B6B35FE}" 23 | EndProject 24 | Global 25 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 26 | Debug|Any CPU = Debug|Any CPU 27 | Debug|x64 = Debug|x64 28 | Debug|x86 = Debug|x86 29 | Release|Any CPU = Release|Any CPU 30 | Release|x64 = Release|x64 31 | Release|x86 = Release|x86 32 | EndGlobalSection 33 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 34 | {C4BB6A39-28E6-454D-8679-92562CEAD0A9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 35 | {C4BB6A39-28E6-454D-8679-92562CEAD0A9}.Debug|Any CPU.Build.0 = Debug|Any CPU 36 | {C4BB6A39-28E6-454D-8679-92562CEAD0A9}.Debug|x64.ActiveCfg = Debug|Any CPU 37 | {C4BB6A39-28E6-454D-8679-92562CEAD0A9}.Debug|x64.Build.0 = Debug|Any CPU 38 | {C4BB6A39-28E6-454D-8679-92562CEAD0A9}.Debug|x86.ActiveCfg = Debug|Any CPU 39 | {C4BB6A39-28E6-454D-8679-92562CEAD0A9}.Debug|x86.Build.0 = Debug|Any CPU 40 | {C4BB6A39-28E6-454D-8679-92562CEAD0A9}.Release|Any CPU.ActiveCfg = Release|Any CPU 41 | {C4BB6A39-28E6-454D-8679-92562CEAD0A9}.Release|Any CPU.Build.0 = Release|Any CPU 42 | {C4BB6A39-28E6-454D-8679-92562CEAD0A9}.Release|x64.ActiveCfg = Release|Any CPU 43 | {C4BB6A39-28E6-454D-8679-92562CEAD0A9}.Release|x64.Build.0 = Release|Any CPU 44 | {C4BB6A39-28E6-454D-8679-92562CEAD0A9}.Release|x86.ActiveCfg = Release|Any CPU 45 | {C4BB6A39-28E6-454D-8679-92562CEAD0A9}.Release|x86.Build.0 = Release|Any CPU 46 | {1C49147F-7C73-4962-A71C-6A193970D058}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 47 | {1C49147F-7C73-4962-A71C-6A193970D058}.Debug|Any CPU.Build.0 = Debug|Any CPU 48 | {1C49147F-7C73-4962-A71C-6A193970D058}.Debug|x64.ActiveCfg = Debug|Any CPU 49 | {1C49147F-7C73-4962-A71C-6A193970D058}.Debug|x64.Build.0 = Debug|Any CPU 50 | {1C49147F-7C73-4962-A71C-6A193970D058}.Debug|x86.ActiveCfg = Debug|Any CPU 51 | {1C49147F-7C73-4962-A71C-6A193970D058}.Debug|x86.Build.0 = Debug|Any CPU 52 | {1C49147F-7C73-4962-A71C-6A193970D058}.Release|Any CPU.ActiveCfg = Release|Any CPU 53 | {1C49147F-7C73-4962-A71C-6A193970D058}.Release|Any CPU.Build.0 = Release|Any CPU 54 | {1C49147F-7C73-4962-A71C-6A193970D058}.Release|x64.ActiveCfg = Release|Any CPU 55 | {1C49147F-7C73-4962-A71C-6A193970D058}.Release|x64.Build.0 = Release|Any CPU 56 | {1C49147F-7C73-4962-A71C-6A193970D058}.Release|x86.ActiveCfg = Release|Any CPU 57 | {1C49147F-7C73-4962-A71C-6A193970D058}.Release|x86.Build.0 = Release|Any CPU 58 | {CB6A1BDA-7768-4A0C-A802-D3AE0C19C120}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 59 | {CB6A1BDA-7768-4A0C-A802-D3AE0C19C120}.Debug|Any CPU.Build.0 = Debug|Any CPU 60 | {CB6A1BDA-7768-4A0C-A802-D3AE0C19C120}.Debug|x64.ActiveCfg = Debug|Any CPU 61 | {CB6A1BDA-7768-4A0C-A802-D3AE0C19C120}.Debug|x64.Build.0 = Debug|Any CPU 62 | {CB6A1BDA-7768-4A0C-A802-D3AE0C19C120}.Debug|x86.ActiveCfg = Debug|Any CPU 63 | {CB6A1BDA-7768-4A0C-A802-D3AE0C19C120}.Debug|x86.Build.0 = Debug|Any CPU 64 | {CB6A1BDA-7768-4A0C-A802-D3AE0C19C120}.Release|Any CPU.ActiveCfg = Release|Any CPU 65 | {CB6A1BDA-7768-4A0C-A802-D3AE0C19C120}.Release|Any CPU.Build.0 = Release|Any CPU 66 | {CB6A1BDA-7768-4A0C-A802-D3AE0C19C120}.Release|x64.ActiveCfg = Release|Any CPU 67 | {CB6A1BDA-7768-4A0C-A802-D3AE0C19C120}.Release|x64.Build.0 = Release|Any CPU 68 | {CB6A1BDA-7768-4A0C-A802-D3AE0C19C120}.Release|x86.ActiveCfg = Release|Any CPU 69 | {CB6A1BDA-7768-4A0C-A802-D3AE0C19C120}.Release|x86.Build.0 = Release|Any CPU 70 | {D2242105-73D1-4F44-9A80-13D51B6B35FE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 71 | {D2242105-73D1-4F44-9A80-13D51B6B35FE}.Debug|Any CPU.Build.0 = Debug|Any CPU 72 | {D2242105-73D1-4F44-9A80-13D51B6B35FE}.Debug|x64.ActiveCfg = Debug|Any CPU 73 | {D2242105-73D1-4F44-9A80-13D51B6B35FE}.Debug|x64.Build.0 = Debug|Any CPU 74 | {D2242105-73D1-4F44-9A80-13D51B6B35FE}.Debug|x86.ActiveCfg = Debug|Any CPU 75 | {D2242105-73D1-4F44-9A80-13D51B6B35FE}.Debug|x86.Build.0 = Debug|Any CPU 76 | {D2242105-73D1-4F44-9A80-13D51B6B35FE}.Release|Any CPU.ActiveCfg = Release|Any CPU 77 | {D2242105-73D1-4F44-9A80-13D51B6B35FE}.Release|Any CPU.Build.0 = Release|Any CPU 78 | {D2242105-73D1-4F44-9A80-13D51B6B35FE}.Release|x64.ActiveCfg = Release|Any CPU 79 | {D2242105-73D1-4F44-9A80-13D51B6B35FE}.Release|x64.Build.0 = Release|Any CPU 80 | {D2242105-73D1-4F44-9A80-13D51B6B35FE}.Release|x86.ActiveCfg = Release|Any CPU 81 | {D2242105-73D1-4F44-9A80-13D51B6B35FE}.Release|x86.Build.0 = Release|Any CPU 82 | EndGlobalSection 83 | GlobalSection(SolutionProperties) = preSolution 84 | HideSolutionNode = FALSE 85 | EndGlobalSection 86 | GlobalSection(NestedProjects) = preSolution 87 | {C4BB6A39-28E6-454D-8679-92562CEAD0A9} = {20DAA632-F8AD-4C5F-9E5F-FC82B7CB56A7} 88 | {1C49147F-7C73-4962-A71C-6A193970D058} = {B286BCBD-DAD8-4DE7-9334-3DE18DF233AF} 89 | {CB6A1BDA-7768-4A0C-A802-D3AE0C19C120} = {B286BCBD-DAD8-4DE7-9334-3DE18DF233AF} 90 | {D2242105-73D1-4F44-9A80-13D51B6B35FE} = {20DAA632-F8AD-4C5F-9E5F-FC82B7CB56A7} 91 | EndGlobalSection 92 | GlobalSection(ExtensibilityGlobals) = postSolution 93 | SolutionGuid = {A97C0A4B-E309-4485-BB76-898B37BFBFFF} 94 | EndGlobalSection 95 | EndGlobal 96 | -------------------------------------------------------------------------------- /Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Blazor Extensions Contributors 5 | Blazor Extensions 6 | (c) Blazor Extensions Contributors. All rights reserved. 7 | https://github.com/BlazorExtensions/Canvas#license 8 | https://github.com/BlazorExtensions/Canvas 9 | https://avatars2.githubusercontent.com/u/38994076?s%3D128%26v%3D4 10 | Microsoft ASP.NET Core Blazor Extensions Canvas 11 | 12 | https://github.com/BlazorExtensions/Canvas 13 | git 14 | true 15 | 16 | 17 | 18 | 19 | latest 20 | true 21 | true 22 | 23 | full 24 | 25 | 26 | 27 | netstandard2.0 28 | netstandard2.0 29 | 30 | 31 | 32 | 33 | 0.1.0 34 | dev 35 | 36 | 37 | 38 | 39 | 43 | 44 | <developer build> 45 | 46 | 47 | 48 | 49 | $(BUILD_SOURCEVERSION) 50 | 51 | 52 | 53 | 54 | $(GIT_COMMIT) 55 | 56 | 57 | 58 | 59 | Not found 60 | $([System.IO.Path]::GetFullPath('$(MSBuildThisFileDirectory).git')) 61 | $([System.IO.File]::ReadAllText('$(DotGitDir)/HEAD').Trim()) 62 | $(DotGitDir)/$(HeadFileContent.Substring(5)) 63 | $([System.IO.File]::ReadAllText('$(RefPath)').Trim()) 64 | $(HeadFileContent) 65 | 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /Directory.Build.targets: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | $(Version). Commit Hash: $(GitHeadSha) 5 | 6 | 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Blazor Extensions Contributors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Canvas 2 | HTML5 Canvas API implementation for Microsoft Blazor 3 | 4 | [![Build](https://github.com/BlazorExtensions/Canvas/workflows/CI/badge.svg)](https://github.com/BlazorExtensions/Canvas/actions) 5 | [![Package Version](https://img.shields.io/nuget/v/Blazor.Extensions.Canvas.svg)](https://www.nuget.org/packages/Blazor.Extensions.Canvas) 6 | [![NuGet Downloads](https://img.shields.io/nuget/dt/Blazor.Extensions.Canvas.svg)](https://www.nuget.org/packages/Blazor.Extensions.Canvas) 7 | [![License](https://img.shields.io/github/license/BlazorExtensions/Canvas.svg)](https://github.com/BlazorExtensions/Canvas/blob/master/LICENSE) 8 | 9 | # Blazor Extensions 10 | 11 | Blazor Extensions are a set of packages with the goal of adding useful things to [Blazor](https://blazor.net). 12 | 13 | # Blazor Extensions Canvas 14 | 15 | This package wraps [HTML5 Canvas](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/canvas) APIs. 16 | 17 | Both Canvas 2D and WebGL are supported. 18 | 19 | Both Blazor Server Apps and Blazor WebAssembly Apps are supported. 20 | 21 | **NOTE** Currently targets the v3.1.5 of Blazor with 3.2.0 of WebAssembly 22 | 23 | # Installation 24 | 25 | ``` 26 | Install-Package Blazor.Extensions.Canvas 27 | ``` 28 | 29 | # Sample 30 | 31 | ## Usage 32 | 33 | In your `index.html` file (WebAssembly Apps) or `_Host.cshtml` (Server Apps) file, place a reference to the library's script file: 34 | 35 | ```html 36 | 37 | ``` 38 | 39 | In your `_Imports.razor` add the following `using` entry: 40 | 41 | ```c# 42 | @using Blazor.Extensions.Canvas 43 | ``` 44 | 45 | In the component where you want to place a canvas element, add a `BECanvas`. Make sure to set the `ref` to a field on your component: 46 | 47 | ```c# 48 | 49 | ``` 50 | 51 | ### 2D 52 | 53 | In your component C# code (regardless if inline on .razor or in a .cs file), add a `BECanvasComponent` reference which matches the `ref` you set on your `BECanvas`. 54 | 55 | Create a `Canvas2DContext`, and then use the context methods to draw on the canvas: 56 | 57 | ```c# 58 | private Canvas2DContext _context; 59 | 60 | protected BECanvasComponent _canvasReference; 61 | 62 | protected override async Task OnAfterRenderAsync(bool firstRender) 63 | { 64 | this._context = await this._canvasReference.CreateCanvas2DAsync(); 65 | await this._context.SetFillStyleAsync("green"); 66 | 67 | await this._context.FillRectAsync(10, 100, 100, 100); 68 | 69 | await this._context.SetFontAsync("48px serif"); 70 | await this._context.StrokeTextAsync("Hello Blazor!!!", 10, 100); 71 | } 72 | ``` 73 | 74 | **NOTE** You cannot call `CreateCanvas2DAsync` in `OnInitAsync`, because the underlying `` element is not yet present in the generated markup. 75 | 76 | ### WebGL 77 | 78 | In your component C# code (regardless if inline on .razor or in a .cs file), add a `BECanvasComponent` reference which matches the `ref` you set on your `BECanvas`. 79 | 80 | Create a `WebGLContext`, and then use the context methods to draw on the canvas: 81 | 82 | ```c# 83 | private WebGLContext _context; 84 | 85 | protected BECanvasComponent _canvasReference; 86 | 87 | protected override async Task OnAfterRenderAsync(bool firstRender) 88 | { 89 | this._context = await this._canvasReference.CreateWebGLAsync(); 90 | 91 | await this._context.ClearColorAsync(0, 0, 0, 1); 92 | await this._context.ClearAsync(BufferBits.COLOR_BUFFER_BIT); 93 | } 94 | ``` 95 | 96 | **NOTE** You cannot call `CreateWebGLAsync` in `OnInitAsync`, because the underlying `` element is not yet present in the generated markup. 97 | 98 | ### Call Batching 99 | 100 | All javascript interop are batched as needed to improve performance. In high-performance scenarios this behavior will not have any effect: each call will execute immediately. In low-performance scenarios, consective calls to canvas APIs will be queued. JavaScript interop calls will be made with each batch of queued commands sequentially, to avoid the performance impact of multiple concurrent interop calls. 101 | 102 | When using server-side Razor Components, because of the server-side rendering mechanism, only the last drawing operation executed will appear to render on the client, overwriting all previous operations. In the example code above, for example, drawing the triangles would appear to "erase" the black background drawn immediately before, leaving the canvas transparent. 103 | 104 | To avoid this issue, all WebGL **drawing** operations should be explicitly preceded and followed by `BeginBatchAsync` and `EndBatchAsync` calls. 105 | 106 | For example: 107 | 108 | ```c# 109 | await this._context.ClearColorAsync(0, 0, 0, 1); // this call does not draw anything, so it does not need to be included in the explicit batch 110 | 111 | await this._context.BeginBatchAsync(); // begin the explicit batch 112 | 113 | await this._context.ClearAsync(BufferBits.COLOR_BUFFER_BIT); 114 | await this._context.DrawArraysAsync(Primitive.TRIANGLES, 0, 3); 115 | 116 | await this._context.EndBatchAsync(); // execute all currently batched calls 117 | ``` 118 | 119 | It is best to structure your code so that `BeginBatchAsync` and `EndBatchAsync` surround as few calls as possible. That will allow the automatic batching behavior to send calls in the most efficient manner possible, and avoid unnecessary performance impacts. 120 | 121 | Methods which return values are never batched. Such methods may be called at any time, *even after calling `BeginBatchAsync`*, without interrupting the batching of other calls. 122 | 123 | ***NOTE*** The "overwriting" behavior of server-side code is unpredictable, and shouldn't be relied on as a feature. In low-performance situations calls can be batched automatically, even when you don't explicitly use `BeginBatchAsync` and `EndBatchAsync`. 124 | 125 | # Contributions and feedback 126 | 127 | Please feel free to use the component, open issues, fix bugs or provide feedback. 128 | 129 | # Contributors 130 | 131 | The following people are the maintainers of the Blazor Extensions projects: 132 | 133 | - [Attila Hajdrik](https://github.com/attilah) 134 | - [Gutemberg Ribiero](https://github.com/galvesribeiro) 135 | 136 | -------------------------------------------------------------------------------- /src/Blazor.Extensions.Canvas.JS/Blazor.Extensions.Canvas.JS.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Library 5 | false 6 | false 7 | true 8 | Latest 9 | ${DefaultItemExcludes};dist\**;node_modules\** 10 | CS2008 11 | 12 | 13 | true 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /src/Blazor.Extensions.Canvas.JS/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "blazor.extensions.canvas", 3 | "version": "0.0.1", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "build": "webpack-cli", 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "dependencies": { 11 | "lodash": "^4.17.20" 12 | }, 13 | "devDependencies": { 14 | "@types/emscripten": "0.0.31", 15 | "ts-loader": "^4.4.2", 16 | "typescript": "^2.9.2", 17 | "webpack": "^4.16.3", 18 | "webpack-cli": "^3.1.0", 19 | "uuid": "8.3.1" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/Blazor.Extensions.Canvas.JS/src/CanvasContextManager.ts: -------------------------------------------------------------------------------- 1 | import { v4 as uuidv4 } from 'uuid'; 2 | 3 | export class ContextManager { 4 | private readonly contexts = new Map(); 5 | private readonly webGLObject = new Array(); 6 | private readonly contextName: string; 7 | private webGLContext = false; 8 | private readonly prototypes: any; 9 | 10 | private readonly patterns = new Map(); 11 | 12 | private readonly webGLTypes = [ 13 | WebGLBuffer, WebGLShader, WebGLProgram, WebGLFramebuffer, WebGLRenderbuffer, WebGLTexture, WebGLUniformLocation 14 | ]; 15 | 16 | public constructor(contextName: string) { 17 | this.contextName = contextName; 18 | if (contextName === "2d") 19 | this.prototypes = CanvasRenderingContext2D.prototype; 20 | else if (contextName === "webgl" || contextName === "experimental-webgl") { 21 | this.prototypes = WebGLRenderingContext.prototype; 22 | this.webGLContext = true; 23 | } else 24 | throw new Error(`Invalid context name: ${contextName}`); 25 | } 26 | 27 | public add = (canvas: HTMLCanvasElement, parameters: any) => { 28 | if (!canvas) throw new Error('Invalid canvas.'); 29 | if (this.contexts.get(canvas.id)) return; 30 | 31 | var context; 32 | if (parameters) 33 | context = canvas.getContext(this.contextName, parameters); 34 | else 35 | context = canvas.getContext(this.contextName); 36 | 37 | if (!context) throw new Error('Invalid context.'); 38 | 39 | this.contexts.set(canvas.id, context); 40 | } 41 | 42 | public remove = (canvas: HTMLCanvasElement) => { 43 | this.contexts.delete(canvas.id); 44 | } 45 | 46 | public setProperty = (canvas: HTMLCanvasElement, property: string, value: any) => { 47 | const context = this.getContext(canvas); 48 | this.setPropertyWithContext(context, property, value); 49 | } 50 | 51 | public getProperty = (canvas: HTMLCanvasElement, property: string) => { 52 | const context = this.getContext(canvas); 53 | return this.serialize(context[property]); 54 | } 55 | 56 | public call = (canvas: HTMLCanvasElement, method: string, args: any) => { 57 | const context = this.getContext(canvas); 58 | return this.callWithContext(context, method, args); 59 | } 60 | 61 | public callBatch = (canvas: HTMLCanvasElement, batchedCalls: any[][]) => { 62 | const context = this.getContext(canvas); 63 | for (let i = 0; i < batchedCalls.length; i++) { 64 | let params = batchedCalls[i].slice(2); 65 | if (batchedCalls[i][1]) { 66 | this.callWithContext(context, batchedCalls[i][0], params); 67 | } else { 68 | this.setPropertyWithContext( 69 | context, 70 | batchedCalls[i][0], 71 | Array.isArray(params) && params.length > 0 ? params[0] : null); 72 | } 73 | } 74 | } 75 | 76 | private callWithContext = (context: any, method: string, args: any) => { 77 | const result = this.prototypes[method].apply(context, args != undefined ? args.map((value) => this.deserialize(method, value)) : []); 78 | 79 | if (method == 'createPattern') { 80 | const key = uuidv4(); 81 | this.patterns.set(key, result); 82 | return key; 83 | } 84 | 85 | return this.serialize(result); 86 | } 87 | 88 | private setPropertyWithContext = (context: any, property: string, value: any) => { 89 | 90 | if (property == 'fillStyle') { 91 | value = this.patterns.get(value) || value; 92 | } 93 | 94 | context[property] = this.deserialize(property, value); 95 | } 96 | 97 | private getContext = (canvas: HTMLCanvasElement) => { 98 | if (!canvas) throw new Error('Invalid canvas.'); 99 | 100 | const context = this.contexts.get(canvas.id); 101 | if (!context) throw new Error('Invalid context.'); 102 | 103 | return context; 104 | } 105 | 106 | private deserialize = (method: string, object: any) => { 107 | if (!this.webGLContext || object == undefined) return object; //deserialization only needs to happen for webGL 108 | 109 | if (object.hasOwnProperty("webGLType") && object.hasOwnProperty("id")) { 110 | return (this.webGLObject[object["id"]]); 111 | } else if (Array.isArray(object) && !method.endsWith("v")) { 112 | return Int8Array.of(...(object as number[])); 113 | } else if (typeof(object) === "string" && (method === "bufferData" || method === "bufferSubData")) { 114 | let binStr = window.atob(object); 115 | let length = binStr.length; 116 | let bytes = new Uint8Array(length); 117 | for (var i = 0; i < length; i++) { 118 | bytes[i] = binStr.charCodeAt(i); 119 | } 120 | return bytes; 121 | } else 122 | return object; 123 | } 124 | 125 | private serialize = (object: any) => { 126 | if (object instanceof TextMetrics) { 127 | return { width: object.width }; 128 | } 129 | 130 | if (!this.webGLContext || object == undefined) return object; //serialization only needs to happen for webGL 131 | 132 | const type = this.webGLTypes.find((type) => object instanceof type); 133 | if (type != undefined) { 134 | const id = this.webGLObject.length; 135 | this.webGLObject.push(object); 136 | 137 | return { 138 | webGLType: type.name, 139 | id: id 140 | }; 141 | } else 142 | return object; 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /src/Blazor.Extensions.Canvas.JS/src/InitializeCanvas.ts: -------------------------------------------------------------------------------- 1 | import { ContextManager } from './CanvasContextManager'; 2 | 3 | namespace Canvas { 4 | const blazorExtensions: string = 'BlazorExtensions'; 5 | // define what this extension adds to the window object inside BlazorExtensions 6 | const extensionObject = { 7 | Canvas2d: new ContextManager("2d"), 8 | WebGL: new ContextManager("webgl") 9 | }; 10 | 11 | export function initialize(): void { 12 | if (typeof window !== 'undefined' && !window[blazorExtensions]) { 13 | // when the library is loaded in a browser via a 9 | 10 | 11 | Loading... 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /test/Blazor.Extensions.Canvas.Test.ServerSide/App.razor: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |

Page not found

7 |

Sorry, but there's nothing here!

8 |
9 |
10 | -------------------------------------------------------------------------------- /test/Blazor.Extensions.Canvas.Test.ServerSide/Blazor.Extensions.Canvas.Test.ServerSide.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.1 5 | 7.3 6 | true 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /test/Blazor.Extensions.Canvas.Test.ServerSide/Pages/Index.razor: -------------------------------------------------------------------------------- 1 | @page "/" 2 | @inherits IndexComponent 3 | 4 |

Canvas demo!!!

5 | 6 | View WebGL page
7 | 8 | Welcome to your new app. 9 | 10 | 11 | -------------------------------------------------------------------------------- /test/Blazor.Extensions.Canvas.Test.ServerSide/Pages/IndexComponent.cs: -------------------------------------------------------------------------------- 1 | using Blazor.Extensions.Canvas.Canvas2D; 2 | using Microsoft.AspNetCore.Components; 3 | using System.Threading.Tasks; 4 | 5 | namespace Blazor.Extensions.Canvas.Test.ServerSide.Pages 6 | { 7 | public class IndexComponent : ComponentBase 8 | { 9 | private Canvas2DContext _context; 10 | 11 | protected BECanvasComponent _canvasReference; 12 | 13 | protected override async Task OnAfterRenderAsync(bool firstRender) 14 | { 15 | this._context = await this._canvasReference.CreateCanvas2DAsync(); 16 | await this._context.SetFillStyleAsync("green"); 17 | 18 | await this._context.FillRectAsync(10, 100, 100, 100); 19 | 20 | await this._context.SetFontAsync("48px serif"); 21 | await this._context.StrokeTextAsync("Hello Blazor!!!", 10, 100); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /test/Blazor.Extensions.Canvas.Test.ServerSide/Pages/WebGL.razor: -------------------------------------------------------------------------------- 1 | @page "/webgl" 2 | @inherits WebGLComponent 3 | 4 |

Canvas demo!!!

5 | 6 | View Canvas2d page
7 | 8 | Welcome to your new app. 9 | 10 | 11 | -------------------------------------------------------------------------------- /test/Blazor.Extensions.Canvas.Test.ServerSide/Pages/WebGLComponent.cs: -------------------------------------------------------------------------------- 1 | using Blazor.Extensions.Canvas.WebGL; 2 | using Microsoft.AspNetCore.Components; 3 | using System; 4 | using System.Threading.Tasks; 5 | 6 | namespace Blazor.Extensions.Canvas.Test.ServerSide.Pages 7 | { 8 | public class WebGLComponent : ComponentBase 9 | { 10 | private WebGLContext _context; 11 | 12 | protected BECanvasComponent _canvasReference; 13 | 14 | private const string VS_SOURCE = "attribute vec3 aPos;" + 15 | "attribute vec3 aColor;" + 16 | "varying vec3 vColor;" + 17 | 18 | "void main() {" + 19 | "gl_Position = vec4(aPos, 1.0);" + 20 | "vColor = aColor;" + 21 | "}"; 22 | 23 | private const string FS_SOURCE = "precision mediump float;" + 24 | "varying vec3 vColor;" + 25 | 26 | "void main() {" + 27 | "gl_FragColor = vec4(vColor, 1.0);" + 28 | "}"; 29 | 30 | protected override async Task OnAfterRenderAsync(bool firstRender) 31 | { 32 | this._context = await this._canvasReference.CreateWebGLAsync(new WebGLContextAttributes 33 | { 34 | PowerPreference = WebGLContextAttributes.POWER_PREFERENCE_HIGH_PERFORMANCE 35 | }); 36 | 37 | await this._context.ClearColorAsync(0, 0, 0, 1); 38 | 39 | var program = await this.InitProgramAsync(this._context, VS_SOURCE, FS_SOURCE); 40 | 41 | var vertexBuffer = await this._context.CreateBufferAsync(); 42 | await this._context.BindBufferAsync(BufferType.ARRAY_BUFFER, vertexBuffer); 43 | 44 | var vertices = new[] 45 | { 46 | -0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 47 | 0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 48 | 0.0f, 0.5f, 0.0f, 0.0f, 0.0f, 1.0f 49 | }; 50 | await this._context.BufferDataAsync(BufferType.ARRAY_BUFFER, vertices, BufferUsageHint.STATIC_DRAW); 51 | 52 | await this._context.VertexAttribPointerAsync(0, 3, DataType.FLOAT, false, 6 * sizeof(float), 0); 53 | await this._context.VertexAttribPointerAsync(1, 3, DataType.FLOAT, false, 6 * sizeof(float), 3 * sizeof(float)); 54 | await this._context.EnableVertexAttribArrayAsync(0); 55 | await this._context.EnableVertexAttribArrayAsync(1); 56 | 57 | await this._context.UseProgramAsync(program); 58 | 59 | await this._context.BeginBatchAsync(); 60 | await this._context.ClearAsync(BufferBits.COLOR_BUFFER_BIT); 61 | await this._context.DrawArraysAsync(Primitive.TRIANGLES, 0, 3); 62 | await this._context.EndBatchAsync(); 63 | } 64 | 65 | private async Task InitProgramAsync(WebGLContext gl, string vsSource, string fsSource) 66 | { 67 | var vertexShader = await this.LoadShaderAsync(gl, ShaderType.VERTEX_SHADER, vsSource); 68 | var fragmentShader = await this.LoadShaderAsync(gl, ShaderType.FRAGMENT_SHADER, fsSource); 69 | 70 | var program = await gl.CreateProgramAsync(); 71 | await gl.AttachShaderAsync(program, vertexShader); 72 | await gl.AttachShaderAsync(program, fragmentShader); 73 | await gl.LinkProgramAsync(program); 74 | 75 | await gl.DeleteShaderAsync(vertexShader); 76 | await gl.DeleteShaderAsync(fragmentShader); 77 | 78 | if (!await gl.GetProgramParameterAsync(program, ProgramParameter.LINK_STATUS)) 79 | { 80 | string info = await gl.GetProgramInfoLogAsync(program); 81 | throw new Exception("An error occured while linking the program: " + info); 82 | } 83 | 84 | return program; 85 | } 86 | 87 | private async Task LoadShaderAsync(WebGLContext gl, ShaderType type, string source) 88 | { 89 | var shader = await gl.CreateShaderAsync(type); 90 | 91 | await gl.ShaderSourceAsync(shader, source); 92 | await gl.CompileShaderAsync(shader); 93 | 94 | if (!await gl.GetShaderParameterAsync(shader, ShaderParameter.COMPILE_STATUS)) 95 | { 96 | string info = await gl.GetShaderInfoLogAsync(shader); 97 | await gl.DeleteShaderAsync(shader); 98 | throw new Exception("An error occured while compiling the shader: " + info); 99 | } 100 | 101 | return shader; 102 | } 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /test/Blazor.Extensions.Canvas.Test.ServerSide/Pages/_Host.cshtml: -------------------------------------------------------------------------------- 1 | @page "/" 2 | @namespace Blazor.Extensions.Canvas.Test.ServerSide.Pages 3 | @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers 4 | 5 | 6 | 7 | 8 | 9 | 10 | Blazor.Extensions.Canvas.Test.ServerSide 11 | 12 | 13 | 14 | 15 | @(await Html.RenderComponentAsync(RenderMode.Server)) 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /test/Blazor.Extensions.Canvas.Test.ServerSide/Pages/_Imports.razor: -------------------------------------------------------------------------------- 1 | @layout MainLayout 2 | -------------------------------------------------------------------------------- /test/Blazor.Extensions.Canvas.Test.ServerSide/Program.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Hosting; 2 | using Microsoft.Extensions.Hosting; 3 | 4 | namespace Blazor.Extensions.Canvas.Test.ServerSide 5 | { 6 | public class Program 7 | { 8 | public static void Main(string[] args) 9 | { 10 | CreateHostBuilder(args).Build().Run(); 11 | } 12 | 13 | public static IHostBuilder CreateHostBuilder(string[] args) => 14 | Host.CreateDefaultBuilder(args) 15 | .ConfigureWebHostDefaults(webBuilder => 16 | { 17 | webBuilder.UseStartup(); 18 | }); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /test/Blazor.Extensions.Canvas.Test.ServerSide/Shared/MainLayout.razor: -------------------------------------------------------------------------------- 1 | @inherits LayoutComponentBase 2 | 3 |
4 | @Body 5 |
6 | -------------------------------------------------------------------------------- /test/Blazor.Extensions.Canvas.Test.ServerSide/Startup.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Builder; 2 | using Microsoft.AspNetCore.Hosting; 3 | using Microsoft.Extensions.DependencyInjection; 4 | using Microsoft.Extensions.Hosting; 5 | 6 | namespace Blazor.Extensions.Canvas.Test.ServerSide 7 | { 8 | public class Startup 9 | { 10 | public void ConfigureServices(IServiceCollection services) 11 | { 12 | services.AddRazorPages(); 13 | services.AddServerSideBlazor(); 14 | } 15 | 16 | public void Configure(IApplicationBuilder app, IWebHostEnvironment env) 17 | { 18 | if (env.IsDevelopment()) 19 | { 20 | app.UseDeveloperExceptionPage(); 21 | } 22 | 23 | app.UseStaticFiles(); 24 | 25 | app.UseRouting(); 26 | 27 | app.UseEndpoints(endpoints => 28 | { 29 | endpoints.MapBlazorHub(); 30 | endpoints.MapFallbackToPage("/_Host"); 31 | }); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /test/Blazor.Extensions.Canvas.Test.ServerSide/_Imports.razor: -------------------------------------------------------------------------------- 1 | @using System.Net.Http 2 | @using Microsoft.AspNetCore.Components.Forms 3 | @using Microsoft.AspNetCore.Components.Routing 4 | @using Microsoft.JSInterop 5 | @using Blazor.Extensions.Canvas.Test.ServerSide 6 | @using Blazor.Extensions.Canvas.Test.ServerSide.Shared 7 | @using Blazor.Extensions.Canvas 8 | -------------------------------------------------------------------------------- /test/Blazor.Extensions.Canvas.Test.ServerSide/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Debug", 5 | "System": "Information", 6 | "Microsoft": "Information" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /test/Blazor.Extensions.Canvas.Test.ServerSide/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Warning", 5 | "Microsoft.Hosting.Lifetime": "Information" 6 | } 7 | }, 8 | "AllowedHosts": "*" 9 | } 10 | -------------------------------------------------------------------------------- /test/Blazor.Extensions.Canvas.Test.ServerSide/wwwroot/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlazorExtensions/Canvas/ebc3eefc7f6c7544975b3d434cc98c01e4d99106/test/Blazor.Extensions.Canvas.Test.ServerSide/wwwroot/favicon.ico -------------------------------------------------------------------------------- /test/Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | <_ParentDirectoryBuildPropsPath Condition="'$(_DirectoryBuildPropsFile)' != ''">$([System.IO.Path]::Combine('..', '$(_DirectoryBuildPropsFile)')) 4 | 5 | 6 | 7 | 8 | 9 | false 10 | false 11 | $(NoWarn);1591 12 | 13 | 14 | 15 | 16 | $(TestProjectTargetFramework) 17 | 18 | 19 | 20 | 21 | full 22 | 23 | 24 | --------------------------------------------------------------------------------