├── .github ├── CODE_OF_CONDUCT.md ├── ISSUE_TEMPLATE.md ├── PULL_REQUEST_TEMPLATE.md └── workflows │ ├── node.js.yml │ └── npm-publish.yml ├── .gitignore ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE.md ├── README.md ├── SECURITY.md ├── azure-pipelines.yml ├── build └── build.js ├── dist ├── azure-maps-animations.js └── azure-maps-animations.min.js ├── docs ├── API Reference.md └── README.md ├── examples ├── Animate a GPS trace.html ├── Animate a choropleth map.html ├── Animate a snakeline.html ├── Animate along a path.html ├── Animate along a route path.html ├── Animate marker along path.html ├── Animate multiple points.html ├── Animate point along path.html ├── Animate to new position of marker.html ├── Animate to new position of point.html ├── Animated tile layer.html ├── Animated traffic flow.html ├── Animation easings.html ├── Bouncing marker animation.html ├── Drop markers on interval.html ├── Drop multiple markers animation.html ├── Drop multiple symbols animation.html ├── Drop symbol animation.html ├── Drop symbols on interval.html ├── Fade shapes in sequentially.html ├── Morph shape animation.html └── Moving dashed line.html ├── package-lock.json ├── package.json ├── src ├── animations │ ├── FrameBasedAnimationTimer.ts │ ├── GroupAnimation.ts │ ├── PlayableAnimation.ts │ ├── index.ts │ ├── interfaces │ │ ├── IPlayableAnimation.ts │ │ ├── PointPairValueInterpolation.ts │ │ └── TimeSpan.ts │ ├── internal │ │ ├── AnimationManager.ts │ │ ├── DropAnimation.ts │ │ ├── Easings.ts │ │ ├── MapPathPlayableAnimation.ts │ │ ├── OpacityAnimation.ts │ │ ├── PathAnimation.ts │ │ ├── PointTranslateAnimation.ts │ │ ├── RoutePathAnimation.ts │ │ ├── SimpleIntervalAnimation.ts │ │ └── morph │ │ │ ├── GeometryInterpolator.ts │ │ │ ├── MorphShapeAnimation.ts │ │ │ ├── RingInterpolator.ts │ │ │ └── SimpleGeometryInterpolator.ts │ ├── options │ │ ├── GroupAnimationOptions.ts │ │ ├── MapPathAnimationOptions.ts │ │ ├── MovingDashLineOptions.ts │ │ ├── PathAnimationOptions.ts │ │ ├── PlayableAnimationOptions.ts │ │ └── RoutePathAnimationOptions.ts │ └── static.ts ├── extensions │ └── EventManager.ts ├── helpers │ ├── Namespace.ts │ └── Utils.ts ├── index.ts └── layer │ ├── AnimatedTileLayer.ts │ ├── AnimatedTileLayerOptions.ts │ └── index.ts ├── tsconfig.json ├── types └── flubber │ └── index.d.ts └── typings └── index.d.ts /.github/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Microsoft Open Source Code of Conduct 2 | 3 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 4 | 5 | Resources: 6 | 7 | - [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/) 8 | - [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) 9 | - Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns 10 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 4 | > Please provide us with the following information: 5 | > --------------------------------------------------------------- 6 | 7 | ### This issue is for a: (mark with an `x`) 8 | ``` 9 | - [ ] bug report -> please search issues before submitting 10 | - [ ] feature request 11 | - [ ] documentation issue or request 12 | - [ ] regression (a behavior that used to work and stopped in a new release) 13 | ``` 14 | 15 | ### Minimal steps to reproduce 16 | > 17 | 18 | ### Any log messages given by the failure 19 | > 20 | 21 | ### Expected/desired behavior 22 | > 23 | 24 | ### OS and Version? 25 | > Windows 7, 8 or 10. Linux (which distribution). macOS (Yosemite? El Capitan? Sierra?) 26 | 27 | ### Versions 28 | > 29 | 30 | ### Mention any other details that might be useful 31 | 32 | > --------------------------------------------------------------- 33 | > Thanks! We'll be in touch soon. 34 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## Purpose 2 | 3 | * ... 4 | 5 | ## Does this introduce a breaking change? 6 | 7 | ``` 8 | [ ] Yes 9 | [ ] No 10 | ``` 11 | 12 | ## Pull Request Type 13 | What kind of change does this Pull Request introduce? 14 | 15 | 16 | ``` 17 | [ ] Bugfix 18 | [ ] Feature 19 | [ ] Code style update (formatting, local variables) 20 | [ ] Refactoring (no functional changes, no api changes) 21 | [ ] Documentation content changes 22 | [ ] Other... Please describe: 23 | ``` 24 | 25 | ## How to Test 26 | * Get the code 27 | 28 | ``` 29 | git clone [repo-address] 30 | cd [repo-name] 31 | git checkout [branch-name] 32 | npm install 33 | ``` 34 | 35 | * Test the code 36 | 37 | ``` 38 | ``` 39 | 40 | ## What to Check 41 | Verify that the following are valid 42 | * ... 43 | 44 | ## Other Information 45 | -------------------------------------------------------------------------------- /.github/workflows/node.js.yml: -------------------------------------------------------------------------------- 1 | # This workflow will do a clean installation of node dependencies, cache/restore them, build the source code and run tests across different versions of node 2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-nodejs 3 | 4 | name: Node.js CI 5 | 6 | on: 7 | push: 8 | branches: 9 | - '**' 10 | pull_request: 11 | branches: 12 | - '**' 13 | 14 | jobs: 15 | build: 16 | 17 | runs-on: ubuntu-latest 18 | 19 | strategy: 20 | matrix: 21 | node-version: [16.x, 18.x, 20.x, 22.x] 22 | # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ 23 | 24 | steps: 25 | - uses: actions/checkout@v4 26 | - name: Use Node.js ${{ matrix.node-version }} 27 | uses: actions/setup-node@v4 28 | with: 29 | node-version: ${{ matrix.node-version }} 30 | cache: 'npm' 31 | - run: npm ci --force 32 | - run: npm run build --if-present -- --isNpmBuild 33 | - run: npm run build --if-present 34 | - run: python -m pip install linkcheckmd 35 | - run: python -m linkcheckmd README.md 36 | -------------------------------------------------------------------------------- /.github/workflows/npm-publish.yml: -------------------------------------------------------------------------------- 1 | # This workflow will run tests using node and then publish a package to GitHub Packages when a release is created 2 | # For more information see: https://docs.github.com/en/actions/publishing-packages/publishing-nodejs-packages 3 | 4 | name: Publish Node.js Package 5 | 6 | on: 7 | release: 8 | types: [published] 9 | workflow_dispatch: 10 | 11 | jobs: 12 | build: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/checkout@v4 16 | - uses: actions/setup-node@v4 17 | with: 18 | node-version: 20 19 | - run: npm ci --force 20 | - run: npm run build --if-present 21 | 22 | publish-npm: 23 | needs: build 24 | runs-on: ubuntu-latest 25 | steps: 26 | - uses: actions/checkout@v4 27 | - uses: actions/setup-node@v4 28 | with: 29 | node-version: 20 30 | registry-url: https://registry.npmjs.org/ 31 | - run: npm ci --force 32 | - run: npm run build --if-present -- --isNpmBuild 33 | - run: npm publish --access=public 34 | env: 35 | NODE_AUTH_TOKEN: ${{secrets.npm_token}} 36 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Directories to ignore 2 | js/ 3 | node_modules/ 4 | test/lib/ -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## azure-maps-animations Changelog 2 | 3 | 4 | # 0.0.2 (Unreleased) 5 | 6 | - Update dependencies versions. 7 | - Update readme links. 8 | 9 | 10 | # 0.0.1 (2020-08-21) 11 | 12 | **Animations** 13 | 14 | - Frame based animation timer. 15 | - Drop animations for Point data and markers. 16 | - Animate data along a path. 17 | - Animate the map camera along a path. 18 | - Create a snakeline animation effect. 19 | - Animate the changing of coordinates in a shape or marker. 20 | - Morph a shapes from one geometry to another. 21 | - Group animations and play them together, sequentially, or incrementally. 22 | - Create a flowing stroke dash array in a line layer. 23 | 24 | **Tools** 25 | 26 | - Improved setTimeout and setInterval functions that leverage requestAnimationFrame. 27 | - Leverage over 30 different built in easing functions. 28 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to azure-maps-animations 2 | 3 | This project welcomes contributions and suggestions. Most contributions require you to agree to a 4 | Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us 5 | the rights to use your contribution. For details, visit https://cla.opensource.microsoft.com. 6 | 7 | When you submit a pull request, a CLA bot will automatically determine whether you need to provide 8 | a CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions 9 | provided by the bot. You will only need to do this once across all repos using our CLA. 10 | 11 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 12 | For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or 13 | contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. 14 | 15 | - [Code of Conduct](#coc) 16 | - [Issues and Bugs](#issue) 17 | - [Feature Requests](#feature) 18 | - [Submission Guidelines](#submit) 19 | 20 | ## Code of Conduct 21 | Help us keep this project open and inclusive. Please read and follow our [Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 22 | 23 | ## Found an Issue? 24 | If you find a bug in the source code or a mistake in the documentation, you can help us by 25 | [submitting an issue](#submit-issue) to the GitHub Repository. Even better, you can 26 | [submit a Pull Request](#submit-pr) with a fix. 27 | 28 | ## Want a Feature? 29 | You can *request* a new feature by [submitting an issue](#submit-issue) to the GitHub 30 | Repository. If you would like to *implement* a new feature, please submit an issue with 31 | a proposal for your work first, to be sure that we can use it. 32 | 33 | * **Small Features** can be crafted and directly [submitted as a Pull Request](#submit-pr). 34 | 35 | ## Submission Guidelines 36 | 37 | ### Submitting an Issue 38 | Before you submit an issue, search the archive, maybe your question was already answered. 39 | 40 | If your issue appears to be a bug, and hasn't been reported, open a new issue. 41 | Help us to maximize the effort we can spend fixing issues and adding new 42 | features, by not reporting duplicate issues. Providing the following information will increase the 43 | chances of your issue being dealt with quickly: 44 | 45 | * **Overview of the Issue** - if an error is being thrown a non-minified stack trace helps 46 | * **Version** - what version is affected (e.g. 0.1.2) 47 | * **Motivation for or Use Case** - explain what are you trying to do and why the current behavior is a bug for you 48 | * **Browsers and Operating System** - is this a problem with all browsers? 49 | * **Reproduce the Error** - provide a live example or a unambiguous set of steps 50 | * **Related Issues** - has a similar issue been reported before? 51 | * **Suggest a Fix** - if you can't fix the bug yourself, perhaps you can point to what might be 52 | causing the problem (line of code or commit) 53 | 54 | You can file new issues by providing the above information at the corresponding repository's issues link: https://github.com/Azure-Samples/azure-maps-animations/issues/new]. 55 | 56 | ### Submitting a Pull Request (PR) 57 | Before you submit your Pull Request (PR) consider the following guidelines: 58 | 59 | * Search the repository (https://github.com/Azure-Samples/azure-maps-animations/pulls) for an open or closed PR 60 | that relates to your submission. You don't want to duplicate effort. 61 | 62 | * Make your changes in a new git fork: 63 | 64 | * Commit your changes using a descriptive commit message 65 | * Push your fork to GitHub: 66 | * In GitHub, create a pull request 67 | * If we suggest changes then: 68 | * Make the required updates. 69 | * Rebase your fork and force push to your GitHub repository (this will update your Pull Request): 70 | 71 | ```shell 72 | git rebase master -i 73 | git push -f 74 | ``` 75 | 76 | That's it! Thank you for your contribution! 77 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Microsoft Corporation. 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 -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Azure Maps Animation module 2 | 3 | A rich library of animations for use with the Azure Maps Web SDK. 4 | 5 | ## Features 6 | 7 | ### Animations 8 | 9 | - Frame based animation timer. 10 | - Drop animations for Point data and markers. 11 | - Animate data along a path. 12 | - Animate the map camera along a path. 13 | - Create a snakeline animation effect. 14 | - Animate the changing of coordinates in a shape or marker. 15 | - Morph a shapes from one geometry to another. 16 | - Group animations and play them together, sequentially, or incrementally. 17 | - Create a flowing stroke dash array in a line layer. 18 | 19 | ### Tools 20 | 21 | - Improved setTimeout and setInterval functions that leverage requestAnimationFrame. 22 | - Leverage over 30 different built in easing functions. 23 | 24 | ## Getting started 25 | 26 | Download the project and copy the `azure-maps-animations` JavaScript file from the `dist` folder into your project. 27 | 28 | See the [documentation](https://github.com/Azure-Samples/azure-maps-animations/tree/main/docs) for more details on a specific feature or take a look at one of the samples below. 29 | 30 | ## Samples 31 | 32 | [Animate a choropleth map](https://samples.azuremaps.com/?sample=animate-a-choropleth-map) 33 |
[](https://samples.azuremaps.com/?sample=animate-a-choropleth-map) 34 | 35 | [Animate a GPS trace](https://samples.azuremaps.com/?sample=animate-a-GPS-trace) 36 |
[](https://samples.azuremaps.com/?sample=animate-a-GPS-trace) 37 | 38 | [Animate a snakeline](https://samples.azuremaps.com/?sample=animate-a-snakeline) 39 |
[](https://samples.azuremaps.com/?sample=animate-a-snakeline) 40 | 41 | [Animate along a path](https://samples.azuremaps.com/?sample=animate-along-a-path) 42 |
[](https://samples.azuremaps.com/?sample=animate-along-a-path) 43 | 44 | [Animate along a route path](https://samples.azuremaps.com/?sample=animate-along-a-route-path) 45 |
[](https://samples.azuremaps.com/?sample=animate-along-a-route-path) 46 | 47 | [Animate marker along path](https://samples.azuremaps.com/?sample=animate-marker-along-path) 48 |
[](https://samples.azuremaps.com/?sample=animate-marker-along-path) 49 | 50 | [Animate multiple points](https://samples.azuremaps.com/?sample=animate-multiple-points) 51 |
[](https://samples.azuremaps.com/?sample=animate-multiple-points) 52 | 53 | [Animate point along path](https://samples.azuremaps.com/?sample=animate-point-along-path) 54 |
[](https://samples.azuremaps.com/?sample=animate-point-along-path) 55 | 56 | [Animate to new position of marker](https://samples.azuremaps.com/?sample=animate-to-new-position-of-marker) 57 |
[](https://samples.azuremaps.com/?sample=animate-to-new-position-of-marker) 58 | 59 | [Animate to new position of point](https://samples.azuremaps.com/?sample=animate-to-new-position-of-point) 60 |
[](https://samples.azuremaps.com/?sample=animate-to-new-position-of-point) 61 | 62 | [Animated tile layer](https://samples.azuremaps.com/?sample=animated-tile-layer) 63 |
[](https://samples.azuremaps.com/?sample=animated-tile-layer) 64 | 65 | [Animated traffic flow](https://samples.azuremaps.com/?sample=animated-traffic-flow) 66 |
[](https://samples.azuremaps.com/?sample=animated-traffic-flow) 67 | 68 | [Animation easings](https://samples.azuremaps.com/?sample=animation-easings) 69 |
[](https://samples.azuremaps.com/?sample=animation-easings) 70 | 71 | [Bouncing marker animation](https://samples.azuremaps.com/?sample=bouncing-marker-animation) 72 |
[](https://samples.azuremaps.com/?sample=bouncing-marker-animation) 73 | 74 | [Drop markers on interval](https://samples.azuremaps.com/?sample=drop-markers-on-interval) 75 |
[](https://samples.azuremaps.com/?sample=drop-markers-on-interval) 76 | 77 | [Drop multiple markers animation](https://samples.azuremaps.com/?sample=drop-multiple-markers-animation) 78 |
[](https://samples.azuremaps.com/?sample=drop-multiple-markers-animation) 79 | 80 | [Drop multiple symbols animation](https://samples.azuremaps.com/?sample=drop-multiple-symbols-on-interval) 81 |
[](https://samples.azuremaps.com/?sample=drop-multiple-symbols-on-interval) 82 | 83 | [Drop symbol animation](https://samples.azuremaps.com/?sample=drop-symbol-animation) 84 |
[](https://samples.azuremaps.com/?sample=drop-symbol-animation) 85 | 86 | [Drop symbols on interval](https://samples.azuremaps.com/?sample=drop-symbols-on-interval) 87 |
[](https://samples.azuremaps.com/?sample=drop-symbols-on-interval) 88 | 89 | [Morph shape animation](https://samples.azuremaps.com/?sample=morph-shape-animation) 90 |
[](https://samples.azuremaps.com/?sample=morph-shape-animation) 91 | 92 | [Moving dashed line](https://samples.azuremaps.com/?sample=moving-dashed-line) 93 |
[](https://samples.azuremaps.com/?sample=moving-dashed-line) 94 | 95 | ## Roadmap 96 | 97 | The following are some ideas to take this project further. 98 | 99 | - New examples 100 | - Cluster explode 101 | - Traffic flow lines 102 | - New animations 103 | - Line path with trail 104 | - Playable camera animations (rotate around, fly, jump) 105 | - UI controls 106 | - Animation control UI 107 | - Time series animation control UI 108 | - Investigate 109 | - Add option to reduce noise of `heading` values in route path animation. 110 | - JSON schema for saving and replaying animations. 111 | 112 | ## Related Projects 113 | 114 | * [Azure Maps Web SDK Open modules](https://github.com/microsoft/Maps/blob/master/AzureMaps.md#open-web-sdk-modules) - A collection of open source modules that extend the Azure Maps Web SDK. 115 | * [Azure Maps Web SDK Samples](https://github.com/Azure-Samples/AzureMapsCodeSamples) 116 | * [Azure Maps & Azure Active Directory Samples](https://github.com/Azure-Samples/Azure-Maps-AzureAD-Samples) 117 | * [List of open-source Azure Maps projects](https://github.com/microsoft/Maps/blob/master/AzureMaps.md) 118 | 119 | ## Additional Resources 120 | 121 | * [Azure Maps (main site)](https://azure.microsoft.com/en-us/products/azure-maps/) 122 | * [Azure Maps Documentation](https://docs.microsoft.com/azure/azure-maps/index) 123 | * [Azure Maps Blog](https://azure.microsoft.com/en-us/blog/product/azure-maps/) 124 | * [Microsoft Q&A](https://docs.microsoft.com/answers/topics/azure-maps.html) 125 | * [Azure Maps feedback](https://feedback.azure.com/forums/909172-azure-maps) 126 | 127 | ## Contributing 128 | 129 | We welcome contributions. Feel free to submit code samples, file issues and pull requests on the repo and we'll address them as we can. 130 | Learn more about how you can help on our [Contribution Rules & Guidelines](https://github.com/Azure-Samples/azure-maps-animations/blob/main/CONTRIBUTING.md). 131 | 132 | You can reach out to us anytime with questions and suggestions using our communities below: 133 | * [Microsoft Q&A](https://docs.microsoft.com/answers/topics/azure-maps.html) 134 | * [Azure Maps feedback](https://feedback.azure.com/forums/909172-azure-maps) 135 | 136 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 137 | For more information, see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or 138 | contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. 139 | 140 | ## License 141 | 142 | MIT 143 | 144 | See [License](https://github.com/Azure-Samples/azure-maps-animations/blob/main/LICENSE.md) for full license text. 145 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Security 4 | 5 | Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/). 6 | 7 | If you believe you have found a security vulnerability in any Microsoft-owned repository that meets Microsoft's [Microsoft's definition of a security vulnerability](https://docs.microsoft.com/en-us/previous-versions/tn-archive/cc751383(v=technet.10)) of a security vulnerability, please report it to us as described below. 8 | 9 | ## Reporting Security Issues 10 | 11 | **Please do not report security vulnerabilities through public GitHub issues.** 12 | 13 | Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://msrc.microsoft.com/create-report). 14 | 15 | If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the the [Microsoft Security Response Center PGP Key page](https://www.microsoft.com/en-us/msrc/pgp-key-msrc). 16 | 17 | You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://www.microsoft.com/msrc). 18 | 19 | Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: 20 | 21 | * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) 22 | * Full paths of source file(s) related to the manifestation of the issue 23 | * The location of the affected source code (tag/branch/commit or direct URL) 24 | * Any special configuration required to reproduce the issue 25 | * Step-by-step instructions to reproduce the issue 26 | * Proof-of-concept or exploit code (if possible) 27 | * Impact of the issue, including how an attacker might exploit the issue 28 | 29 | This information will help us triage your report more quickly. 30 | 31 | If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://microsoft.com/msrc/bounty) page for more details about our active programs. 32 | 33 | ## Preferred Languages 34 | 35 | We prefer all communications to be in English. 36 | 37 | ## Policy 38 | 39 | Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://www.microsoft.com/en-us/msrc/cvd). 40 | 41 | 42 | -------------------------------------------------------------------------------- /azure-pipelines.yml: -------------------------------------------------------------------------------- 1 | # HTML 2 | # Archive your static HTML project and save it with the build record. 3 | # Add steps that build, run tests, deploy, and more: 4 | # https://aka.ms/yaml 5 | 6 | trigger: 7 | - master 8 | 9 | pool: 10 | vmImage: 'ubuntu-latest' 11 | 12 | steps: 13 | - task: ArchiveFiles@2 14 | inputs: 15 | rootFolderOrFile: '$(build.sourcesDirectory)' 16 | includeRootFolder: false 17 | - task: PublishBuildArtifacts@1 18 | -------------------------------------------------------------------------------- /build/build.js: -------------------------------------------------------------------------------- 1 | const pkg = require("../package.json"); 2 | const fs = require("fs-extra"); 3 | const path = require("path"); 4 | const { rollup } = require("rollup"); 5 | const commonjs = require("rollup-plugin-commonjs"); 6 | const nodeResolve = require("rollup-plugin-node-resolve"); 7 | const { uglify } = require("rollup-plugin-uglify"); 8 | const ts = require("typescript"); 9 | 10 | // Configure any functions/properties used by the drawing tools and 11 | // exported by a dependency that rollup can't automatically detect. 12 | const namedExports = { 13 | }; 14 | 15 | // Parse the command line. 16 | const args = require("yargs").options({ 17 | "isNpmBuild": { 18 | describe: "Whether the build is for NPM.", 19 | type: "boolean" 20 | } 21 | }).help().argv; 22 | 23 | // Host for formating typescript diagnostics. 24 | const formatDiagnosticHost = { 25 | getCanonicalFileName: path.normalize, 26 | getCurrentDirectory: ts.sys.getCurrentDirectory, 27 | getNewLine: () => ts.sys.newLine 28 | }; 29 | 30 | // Host for parsing the config file host. 31 | const parseConfigFileHost = { 32 | useCaseSensitiveFileNames: false, 33 | fileExists: ts.sys.fileExists, 34 | getCurrentDirectory: ts.sys.getCurrentDirectory, 35 | readDirectory: ts.sys.readDirectory, 36 | readFile: ts.sys.readFile, 37 | onUnRecoverableConfigFileDiagnostic: (diag) => 38 | console.error(ts.formatDiagnostic(diag, formatDiagnosticHost)) 39 | }; 40 | 41 | // Define and immediately execute the main build function. 42 | let rollupError = false; 43 | (async function build() { 44 | // Cleanup the dist folder where the js packages will be output 45 | const distDirPath = "./dist"; 46 | fs.emptyDirSync(distDirPath); 47 | 48 | // Get the major and minor version for the output folder name 49 | //const [majorVersion, minorVersion] = pkg.version.split("."); 50 | 51 | // File name and path for non-minified browser js 52 | const outFilePath = `${distDirPath}/azure-maps-animations.js`; 53 | const outMinFilePath = `${distDirPath}/azure-maps-animations.min.js`; 54 | 55 | const inputPath = "./js/index.js"; 56 | 57 | // Ensure that all necessary output folders are created. 58 | await fs.ensureDir(path.dirname(outFilePath)); 59 | await fs.ensureDir(path.dirname(outMinFilePath)); 60 | 61 | // Parse the typescript config file. 62 | console.log("Parsing tsconfig.json"); 63 | const tsConfig = ts.getParsedCommandLineOfConfigFile("./tsconfig.json", {}, parseConfigFileHost); 64 | if (tsConfig.errors.length > 0) { 65 | for (const error of tsConfig.errors) { 66 | console.error(ts.formatDiagnostic(error, formatDiagnosticHost)); 67 | } 68 | 69 | process.exit(-1); 70 | } 71 | 72 | // Empty the directory for storing the compiled typescript. 73 | console.log("Clearing the typescript output folder"); 74 | await fs.emptyDir(tsConfig.options.outDir); 75 | 76 | // Compile the typescript source. 77 | console.log("Compiling typescript to javascript"); 78 | const tsProgram = ts.createProgram(tsConfig.fileNames, tsConfig.options); 79 | const tsResult = tsProgram.emit(); 80 | const tsDiag = ts.getPreEmitDiagnostics(tsProgram).concat(tsResult.diagnostics); 81 | if (tsDiag.length > 0) { 82 | for (const error of tsDiag) { 83 | console.error(ts.formatDiagnostic(error, formatDiagnosticHost)); 84 | } 85 | 86 | process.exit(-1); 87 | } 88 | 89 | // Read license.txt to define the banner for the packages. 90 | let banner = "/*\n"; 91 | banner += (await fs.readFile("./LICENSE.md", "utf8")).trim(); 92 | banner += "\n*/\n"; 93 | 94 | let rollupInputOps, rollupOutputOps; 95 | if (!args.isNpmBuild) { 96 | // Set rollup options for browser builds. 97 | console.log("Building IIFE version"); 98 | rollupInputOps = { 99 | external: ["azure-maps-control"], 100 | onwarn: rollupWarn, 101 | input: inputPath, 102 | plugins: [ 103 | nodeResolve({ 104 | browser: true, 105 | preferBuiltins: false 106 | }), 107 | commonjs({ 108 | namedExports: namedExports 109 | }) 110 | ] 111 | }; 112 | 113 | rollupOutputOps = { 114 | exports: "named", 115 | file: outFilePath, 116 | format: "iife", 117 | name: "atlas", 118 | extend: true, 119 | globals: { 120 | "azure-maps-control": "atlas" 121 | } 122 | }; 123 | } else { 124 | console.log("Building CommonJS version"); 125 | rollupInputOps = { 126 | external: ["azure-maps-control"], 127 | onwarn: rollupWarn, 128 | input: inputPath, 129 | plugins: [ 130 | nodeResolve({ 131 | browser: true, 132 | }), 133 | commonjs({ 134 | namedExports: namedExports 135 | }) 136 | ] 137 | }; 138 | 139 | rollupOutputOps = { 140 | file: outFilePath, 141 | format: "cjs" 142 | }; 143 | } 144 | 145 | // Rollup non-minified version. 146 | console.log("Bundling non-minified javascript package"); 147 | await bundle(rollupInputOps, rollupOutputOps, banner); 148 | 149 | // Add uglify to the rollup input plugins. 150 | // Update the output file path for the minified version. 151 | rollupOutputOps.file = outMinFilePath; 152 | rollupInputOps.plugins.push(uglify()); 153 | 154 | // Rollup minified version. 155 | console.log("Bundling minified javascript package"); 156 | 157 | const minifiedLicense = "/* MIT License - Copyright (c) Microsoft Corporation. */\n\n" 158 | 159 | await bundle(rollupInputOps, rollupOutputOps, minifiedLicense); 160 | 161 | //Remove js folder. 162 | await fs.remove("./js"); 163 | 164 | // Build is done! 165 | console.log(rollupError ? "Build failed" : "Build completed successfully!"); 166 | process.exit(rollupError ? -1 : 0); 167 | })() 168 | 169 | async function bundle(inputOptions, outputOptions) { 170 | const bundle = await rollup(inputOptions); 171 | await bundle.write(outputOptions); 172 | } 173 | 174 | function rollupWarn(warning) { 175 | // Print the warning to the console. 176 | console.warn(warning.toString()); 177 | 178 | // If the warning is about missing export provide more info. 179 | if (warning.code === "MISSING_EXPORT") { 180 | console.warn( 181 | ` if '${warning.missing}' is exported by '${warning.exporter}' then try adding\n` + 182 | ` "${warning.exporter}": [${warning.missing}] to namedExports in ${__filename}` 183 | ); 184 | } 185 | } 186 | 187 | async function bundle(rollupInputOps, rollupOutputOps, banner) { 188 | try { 189 | const bundle = await rollup(rollupInputOps); 190 | const { output } = await bundle.generate(rollupOutputOps); 191 | 192 | const chunk = output.find((chunk) => 193 | chunk.fileName === path.basename(rollupOutputOps.file) 194 | ); 195 | 196 | await fs.writeFile(rollupOutputOps.file, banner + "\n" + chunk.code, "utf8"); 197 | } catch (error) { 198 | throw new Error(`Failed to bundle the javascript package:\n${error.message}\n` + 199 | JSON.stringify(error, null, 2)); 200 | } 201 | } 202 | 203 | function rollupWarn(warning) { 204 | // If the warning is about missing export provide more info. 205 | if (warning.code === "MISSING_EXPORT") { 206 | rollupError = true; 207 | console.error("ERROR: " + warning.toString() + "\n" + 208 | ` if '${warning.missing}' is exported by '${warning.exporter}' then try adding\n` + 209 | ` "${warning.exporter}": [${warning.missing}] to namedExports in ${__filename}` 210 | ); 211 | } else { 212 | // Print the warning to the console. 213 | console.warn("WARNING: " + warning.toString()); 214 | } 215 | } -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # Animation Module 2 | 3 | A rich library of animations for use with the Azure Maps Web SDK. 4 | 5 | Most animations are based on the `IPlayableAnimation` interface and accept `PlayableAnimationOptions` which makes it easy customize how these animations function. 6 | 7 | Check out the [API Reference](API%20Reference.md) for indepth details. 8 | 9 | ## Easings 10 | 11 | The following easing names can be used with the animation library. Check out the [Animation easings](https://azuremapscodesamples.azurewebsites.net/index.html?sample=Animation%20easings) example to see these in action. 12 | 13 | | Name | Description | 14 | |------|-------------| 15 | | `linear` | linear easing function. | 16 | | `easeInSine` | Slight acceleration from zero to full speed. | 17 | | `easeOutSine` | Slight deceleration at the end. | 18 | | `easeInOutSine` | Slight acceleration at beginning and slight deceleration at end. | 19 | | `easeInQuad` | Accelerating from zero velocity. | 20 | | `easeOutQuad` | Decelerating to zero velocity. | 21 | | `easeInOutQuad` | Acceleration until halfway, then deceleration. | 22 | | `easeInCubic` | Accelerating from zero velocity. | 23 | | `easeOutCubic` | Decelerating to zero velocity. | 24 | | `easeInOutCubic` | Acceleration until halfway, then deceleration. | 25 | | `easeInQuart` | Accelerating from zero velocity. | 26 | | `easeOutQuart` | Decelerating to zero velocity. | 27 | | `easeInOutQuart` | Acceleration until halfway, then deceleration. | 28 | | `easeInQuint` | Accelerating from zero velocity. | 29 | | `easeOutQuint` | Decelerating to zero velocity. | 30 | | `easeInOutQuint` | Acceleration until halfway, then deceleration. | 31 | | `easeInExpo` | Accelerate exponentially until finish. | 32 | | `easeOutExpo` | Initial exponential acceleration slowing to stop. | 33 | | `easeInOutExpo` | Exponential acceleration and deceleration. | 34 | | `easeInCirc` | Increasing velocity until stop. | 35 | | `easeOutCirc` | Start fast, decreasing velocity until stop. | 36 | | `easeInOutCirc` | Fast increase in velocity, fast decrease in velocity. | 37 | | `easeInBack` | Slow movement backwards then fast snap to finish. | 38 | | `easeOutBack` | Fast snap to backwards point then slow resolve to finish. | 39 | | `easeInOutBack` | Slow movement backwards, fast snap to past finish, slow resolve to finish. | 40 | | `easeInElastic` | Bounces slowly then quickly to finish. | 41 | | `easeOutElastic` | Fast acceleration, bounces to zero. | 42 | | `easeInOutElastic` | Slow start and end, two bounces sandwich a fast motion. | 43 | | `easeOutBounce` | Bounce to completion. | 44 | | `easeInBounce` | Bounce increasing in velocity until completion. | 45 | | `easeInOutBounce` | Bounce in and bounce out. | 46 | -------------------------------------------------------------------------------- /examples/Animate a snakeline.html: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Animate a snakeline - Azure Maps Web SDK Samples 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 91 | 92 | 93 |
94 | 95 |
Click the map to animate line.
96 | 97 |
98 |

Animate a snakeline

99 | This sample shows how to animate a LineString such that its path is drawn out smoothly over time on top of the map using what is called a snakeline animation. 100 | This sample uses the open source Azure Maps Animation module 101 |
102 | 103 | -------------------------------------------------------------------------------- /examples/Animate along a path.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Animate along a Path - Azure Maps Web SDK Samples 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 148 | 149 | 150 |
151 | 152 |
153 | 154 | 155 | 156 | 157 |

158 | Follow:
159 | Follow offset:
160 | Loop:
161 | Reverse: 162 |
163 | 164 |
165 |

Animate along a path

166 | This sample shows how to animate a symbol along a path on the map smoothly. This sample also includes controls and options for the animation. 167 | This sample uses the open source Azure Maps Animation module 168 |
169 | 170 | -------------------------------------------------------------------------------- /examples/Animate along a route path.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Animate along a route path - Azure Maps Web SDK Samples 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 163 | 164 | 165 |
166 | 167 |
168 | 169 | 170 | 171 | 172 |

173 | Follow:
174 | Follow offset:
175 | Loop:
176 | Reverse: 177 |
178 | 179 |
180 |

Animate along a route path

181 | This sample shows how to smoothly animate a symbol along a route path taking into consideration timestamps for each point in the route path. 182 | This sample also includes controls and options for the animation. 183 | This sample uses the open source Azure Maps Animation module 184 |
185 | 186 | -------------------------------------------------------------------------------- /examples/Animate marker along path.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Animate marker along path - Azure Maps Web SDK Samples 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 75 | 76 | 77 |
78 | 79 |
Click the map to animate marker.
80 | 81 |
82 |

Animate marker along path

83 | This sample shows how to easily animate a HTML marker along a path on the map. 84 | This sample uses the open source Azure Maps Animation module 85 |
86 | 87 | -------------------------------------------------------------------------------- /examples/Animate multiple points.html: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Animate multiple points - Azure Maps Web SDK Samples 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 94 | 95 | 96 |
97 | 98 |
99 | 100 | 101 | 102 | 103 |
104 | 105 |
106 |

Animate multiple points

107 | This sample shows how to animate multiple points on the map. 108 | This sample uses the open source Azure Maps Animation module 109 |
110 | 111 | -------------------------------------------------------------------------------- /examples/Animate point along path.html: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Animate point along path - Azure Maps Web SDK Samples 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 93 | 94 | 95 |
96 | 97 |
Click the map to animate point.
98 | 99 |
100 |

Animate point along path

101 | This sample shows how to easily animate a point along a path on the map. 102 | This sample uses the open source Azure Maps Animation module 103 |
104 | 105 | -------------------------------------------------------------------------------- /examples/Animate to new position of marker.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Animate to new position of marker - Azure Maps Web SDK Samples 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 52 | 53 | 54 |
55 | 56 |
Click the map to animate marker.
57 | 58 |
59 |

Animate to new position of marker

60 | This sample shows how to animate a marker on the map to a new coordinate. 61 | This sample uses the open source Azure Maps Animation module 62 |
63 | 64 | -------------------------------------------------------------------------------- /examples/Animate to new position of point.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Animate to new position of point - Azure Maps Web SDK Samples 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 65 | 66 | 67 |
68 | 69 |
Click the map to animate point.
70 | 71 |
72 |

Animate to new position of point

73 | This sample shows how to animate a point on the map to a new coordinate. 74 | This sample uses the open source Azure Maps Animation module 75 |
76 | 77 | -------------------------------------------------------------------------------- /examples/Animated traffic flow.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Animated traffic flow - Azure Maps Web SDK Samples 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 167 | 168 | 169 |
170 | 171 |
172 |

Animated traffic flow

173 | This sample shows how to animate the flow of traffic relative to the congestion level using the flowing dashed line animation. 174 | This sample uses the open source Azure Maps Animation module 175 |
176 | 177 | -------------------------------------------------------------------------------- /examples/Animation easings.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Animation easings - Azure Maps Web SDK Samples 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 160 | 161 | 162 |
163 | 164 |
165 | Easing: 166 | 167 | 168 | 169 | 170 |

171 | 172 |
173 | 174 |
175 |
176 | 177 |
178 |

Animation easings

179 | This sample demonstrates the different built in easing functions in the Azure Maps animation module. 180 | This sample uses the open source Azure Maps Animation module 181 |
182 | 183 | -------------------------------------------------------------------------------- /examples/Bouncing marker animation.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Bouncing marker animation - Azure Maps Web SDK Samples 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 67 | 68 | 69 |
70 | 71 |
Click the marker to toggle the animation.
72 | 73 |
74 |

Bouncing marker animation

75 | This sample shows how to animate an HTML marker to make it appear to be bouncing on the map. 76 | This sample uses the open source Azure Maps Animation module 77 |
78 | 79 | -------------------------------------------------------------------------------- /examples/Drop markers on interval.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Drop markers on interval - Azure Maps Web SDK Samples 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 67 | 68 | 69 |
70 | 71 |
Click the map to animate markers.
72 | 73 |
74 |

Drop markers on interval

75 | This sample shows how to animate the dropping of multiple HTML markers on an interval to the map. 76 | This sample uses the open source Azure Maps Animation module 77 |
78 | 79 | -------------------------------------------------------------------------------- /examples/Drop multiple markers animation.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Drop multiple markers animation - Azure Maps Web SDK Samples 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 61 | 62 | 63 |
64 | 65 |
Click the map to animate markers.
66 | 67 |
68 |

Drop multiple markers animation

69 | This sample shows how to animate the dropping of multiple HTML markers on the map. 70 | This sample uses the open source Azure Maps Animation module 71 |
72 | 73 | -------------------------------------------------------------------------------- /examples/Drop multiple symbols animation.html: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Drop multiple symbols animation - Azure Maps Web SDK Samples 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 79 | 80 | 81 |
82 | 83 |
Click the map to animate points.
84 | 85 |
86 |

Drop multiple symbols animation

87 | This sample shows how to animate multiple points on the map as dropping symbols. 88 | This sample uses the open source Azure Maps Animation module 89 |
90 | 91 | -------------------------------------------------------------------------------- /examples/Drop symbol animation.html: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Drop symbol animation - Azure Maps Web SDK Samples 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 71 | 72 | 73 |
74 | 75 |
Click the map to animate point.
76 | 77 |
78 |

Drop symbol animation

79 | This sample shows how to animate a point on the map as a dropping symbol. 80 | This sample uses the open source Azure Maps Animation module 81 |
82 | 83 | -------------------------------------------------------------------------------- /examples/Drop symbols on interval.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Drop multiple symbols on interval - Azure Maps Web SDK Samples 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 87 | 88 | 89 |
90 | 91 |
Click the map to animate points.
92 | 93 |
94 |

Drop multiple symbols on interval

95 | This sample shows how to animate the dropping of multiple points on an interval to the map using a symbol layer. 96 | This sample uses the open source Azure Maps Animation module 97 |
98 | 99 | -------------------------------------------------------------------------------- /examples/Fade shapes in sequentially.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Fade shapes in sequentially - Azure Maps Web SDK Samples 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 146 | 147 | 148 |
149 | 150 |
Click the map to animate markers.
151 | 152 |
153 |

Fade shapes in sequentially

154 | This sample shows how to animate the opacity of shapes to fade them into appearance. 155 |
156 | 157 | -------------------------------------------------------------------------------- /examples/Morph shape animation.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Morph shape animation - Azure Maps Web SDK Samples 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 131 | 132 | 133 |
134 | 135 |
136 | 137 | 138 | 139 | 140 |
141 | 142 |
143 |

Morph shape animation

144 | This sample shows how to animate the morphing of a shape from one geometry to another. 145 | This sample uses the open source Azure Maps Animation module 146 |
147 | 148 | -------------------------------------------------------------------------------- /examples/Moving dashed line.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Moving dashed line - Azure Maps Web SDK Samples 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 62 | 63 | 64 |
65 | 66 |
67 |

Moving dashed line

68 | This sample shows how to easily animate the dashes of a line to making it look like it is flowing. 69 | This sample uses the open source Azure Maps Animation module 70 |
71 | 72 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@azure-maps/animations", 3 | "version": "0.0.3", 4 | "description": "A module for the Azure Maps Web SDK that provides tools for animating data on the map.", 5 | "keywords": [ 6 | "azure", 7 | "maps", 8 | "animations", 9 | "animate", 10 | "lbs", 11 | "microsoft" 12 | ], 13 | "bugs": { 14 | "url": "https://github.com/Azure-Samples/azure-maps-animations/issues" 15 | }, 16 | "homepage": "https://github.com/Azure-Samples/azure-maps-animations#readme", 17 | "scripts": { 18 | "build": "node ./build/build.js" 19 | }, 20 | "author": "Microsoft Corporation", 21 | "license": "MIT", 22 | "dependencies": { 23 | "azure-maps-control": "^2.0.25 || ^3.0.1" 24 | }, 25 | "devDependencies": { 26 | "@types/lodash": "^4.14.136", 27 | "@types/mocha": "^5.2.6", 28 | "@types/puppeteer": "^1.20.4", 29 | "@types/geojson": "^7946.0.14", 30 | "flubber": "^0.4.2", 31 | "fs-extra": "^7.0.1", 32 | "lodash": "^4.17.15", 33 | "mocha": "^7.1.1", 34 | "mocha-parallel-tests": "^2.3.0", 35 | "puppeteer": "^2.1.1", 36 | "rollup": "^1.32.1", 37 | "rollup-plugin-commonjs": "^10.0.1", 38 | "rollup-plugin-node-resolve": "^5.2.0", 39 | "rollup-plugin-uglify": "^6.0.4", 40 | "tslint": "^5.15.0", 41 | "typescript": "^5.5.4", 42 | "yargs": "^13.3.2" 43 | }, 44 | "files": [ 45 | "typings/**/*", 46 | "dist/**/*", 47 | "LICENSE.md" 48 | ], 49 | "types": "typings/index.d.ts", 50 | "main": "dist/azure-maps-animations.min.js" 51 | } 52 | -------------------------------------------------------------------------------- /src/animations/FrameBasedAnimationTimer.ts: -------------------------------------------------------------------------------- 1 | import { PlayableAnimation } from './PlayableAnimation'; 2 | import { PlayableAnimationOptions } from './options/PlayableAnimationOptions'; 3 | 4 | /** Event arguments for a frame based animation. */ 5 | export interface FrameBasedAnimationEvent { 6 | /** The event type. */ 7 | type: string; 8 | 9 | /** The animation the event occurered on. */ 10 | animation: FrameBasedAnimationTimer; 11 | 12 | /** The index of the frame if using the frame based animation timer. */ 13 | frameIdx?: number; 14 | 15 | /** The number of frames in the animation. */ 16 | numFrames?: number; 17 | } 18 | 19 | /** A class for frame based animations. */ 20 | export class FrameBasedAnimationTimer extends PlayableAnimation { 21 | 22 | private _numFrames: number = 0; 23 | private _onFrame: (frameIdx: number) => void; 24 | private _curFrameIdx = -1; 25 | 26 | /** 27 | * An class for frame based animations. 28 | * @param numberOfFrames The number of frames in the animation. 29 | * @param onFrame A callback function to trigger when the frame index changes. 30 | * @param options Animation options. 31 | */ 32 | constructor(numberOfFrames: number, onFrame: (frameIdx: number) => void, options?: PlayableAnimationOptions){ 33 | super(options); 34 | const self = this; 35 | 36 | self._numFrames = numberOfFrames; 37 | self._onFrame = onFrame; 38 | 39 | if(options && options.autoPlay){ 40 | self.play(); 41 | } 42 | } 43 | 44 | /** Gets the current frame index of the animation. Returns -1 if animation hasn't started, or if there is 0 frames. */ 45 | public getCurrentFrameIdx(): number { 46 | if(this._numFrames <= 0){ 47 | return -1; 48 | } 49 | 50 | return this._curFrameIdx; 51 | } 52 | 53 | /** Gets the number of frames in the animation. */ 54 | public getNumberOfFrames(): number { 55 | return this._numFrames; 56 | } 57 | 58 | /** 59 | * Sets the frame index of the animation. 60 | * @param frameIdx The frame index to advance to. 61 | */ 62 | public setFrameIdx(frameIdx: number): void { 63 | const self = this; 64 | if(frameIdx >= 0 || frameIdx < self._numFrames){ 65 | self.seek(self._numFrames / frameIdx) 66 | } 67 | } 68 | 69 | /** 70 | * Sets the number of frames in the animation. 71 | * @param numberOfFrames The number of frames in the animation. 72 | */ 73 | public setNumberOfFrames(numberOfFrames: number): void { 74 | const self = this; 75 | if(typeof numberOfFrames === 'number' && self._numFrames !== numberOfFrames){ 76 | self._numFrames = Math.max(numberOfFrames, 0); 77 | self._curFrameIdx = (numberOfFrames < self._curFrameIdx )? self._curFrameIdx : 0; 78 | 79 | if(numberOfFrames <= 0){ 80 | self._curFrameIdx = -1; 81 | } 82 | 83 | self._triggerFrame(self._curFrameIdx); 84 | } 85 | } 86 | 87 | ///////////////////////////// 88 | // Abstract method override 89 | //////////////////////////// 90 | 91 | public onAnimationProgress(progress: number): { frameIdx: number } { 92 | const self = this; 93 | let nf = self._numFrames; 94 | 95 | if(nf > 0){ 96 | //Need to get even spaced frame periods. 97 | let frameIdx = Math.round(progress * nf - 0.49999999999999999999999); 98 | 99 | if(frameIdx !== self._curFrameIdx) { 100 | //When progress exactly 1, the frameIdx will be equal to the number of frames, but we want one less. This means that the last frame will be slightly longer (a couple of ms in a most cases). 101 | if(frameIdx === nf){ 102 | frameIdx--; 103 | } else if(frameIdx < 0){ 104 | //Unlikely to happen, but an extra check to be safe. Ignore any frames that are negative. 105 | frameIdx = -1; 106 | } 107 | 108 | self._triggerFrame(frameIdx); 109 | 110 | return { frameIdx: frameIdx }; 111 | } 112 | } 113 | 114 | return null; 115 | } 116 | 117 | private _triggerFrame(frameIdx): void { 118 | const self = this; 119 | if(self._onFrame && frameIdx !== -1){ 120 | self._onFrame(frameIdx); 121 | } 122 | 123 | self._curFrameIdx = frameIdx; 124 | 125 | if(frameIdx !== -1){ 126 | self._invokeEvent('onframe', { 127 | type: 'onframe', 128 | frameIdx: frameIdx, 129 | animation: self, 130 | numFrames: self._numFrames 131 | }); 132 | } 133 | } 134 | } -------------------------------------------------------------------------------- /src/animations/index.ts: -------------------------------------------------------------------------------- 1 | /* Export static functions */ 2 | export * from "./static"; 3 | 4 | export { FrameBasedAnimationTimer } from "./FrameBasedAnimationTimer" 5 | export { GroupAnimation } from "./GroupAnimation"; 6 | export { PlayableAnimation } from "./PlayableAnimation"; 7 | -------------------------------------------------------------------------------- /src/animations/interfaces/IPlayableAnimation.ts: -------------------------------------------------------------------------------- 1 | 2 | /** An interface that all playable animations adhere to. */ 3 | export interface IPlayableAnimation { 4 | /** A unique internal ID for the animation. Need to ensure that two animations that do the same thing have a different signature for the Animation Manager. */ 5 | _id: number; 6 | 7 | /** Internal callback function that is called when the animation progress changes. */ 8 | _onAnimationProgress(timestamp: number): void; 9 | 10 | /** Internal callback function that is called when the animation is complete to notify dependancies. */ 11 | _onComplete: () => void; 12 | 13 | /** Disposes the animation. */ 14 | dispose(): void; 15 | 16 | /** Gets the duration of the animation. Returns Infinity if the animations loops forever. */ 17 | getDuration(): number; 18 | 19 | /** Checks to see if the animaiton is playing. */ 20 | isPlaying(): boolean; 21 | 22 | /** Pauses the animation. */ 23 | pause(): void; 24 | 25 | /** Plays the animation. */ 26 | play(): void; 27 | 28 | /** Reset the animation. */ 29 | reset(): void; 30 | 31 | /** Stops the animation. */ 32 | stop(): void; 33 | } 34 | -------------------------------------------------------------------------------- /src/animations/interfaces/PointPairValueInterpolation.ts: -------------------------------------------------------------------------------- 1 | 2 | /** Defines how the value of property in two points is extrapolated. */ 3 | export interface PointPairValueInterpolation { 4 | /** 5 | * How the interpolation is performed. Certain interpolations require the data to be a certain value. 6 | * - `linear`,`min`, `max`, `avg`: `number` or `Date` 7 | * - `nearest`: `any` 8 | * Default: `linear` 9 | */ 10 | interpolation: 'linear' | 'nearest' | 'min' | 'max' | 'avg'; 11 | 12 | /** 13 | * The path to the property with each sub-property separated with a forward slash "/", for example "property/subproperty1/subproperty2". 14 | * Array indices can be added as subproperties as well, for example "property/0". 15 | */ 16 | propertyPath: string; 17 | } -------------------------------------------------------------------------------- /src/animations/interfaces/TimeSpan.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Details about a period time. 3 | */ 4 | export interface TimeSpan { 5 | /** The start of the time span. Can be a number representing a date/time, or a number representing an order. */ 6 | begin: number; 7 | 8 | /** The end of the time span. Can be a number representing a date/time, or a number representing an order. */ 9 | end: number; 10 | } -------------------------------------------------------------------------------- /src/animations/internal/AnimationManager.ts: -------------------------------------------------------------------------------- 1 | import { IPlayableAnimation } from '../interfaces/IPlayableAnimation'; 2 | 3 | /** An animation manager for classes that extend from the AnimatedShape class. */ 4 | export class AnimationManager { 5 | 6 | /**************************** 7 | * Private Properties 8 | ***************************/ 9 | 10 | private _animation: number = null; 11 | private _queue: IPlayableAnimation[] = []; 12 | private _lastTime: number; 13 | //Min frame rate 14 | private _minFR = 33; //roughly 30 frames per second is the fastest that the animation loop will update. 15 | private _stopped = true; 16 | private _idCounter = 1234567890; 17 | private _idTable: { [key: number]: IPlayableAnimation } = {}; 18 | 19 | /**************************** 20 | * Constructor 21 | ***************************/ 22 | 23 | constructor() { 24 | this._lastTime = performance.now(); 25 | this.enable(); 26 | } 27 | 28 | /**************************** 29 | * Public functions 30 | ***************************/ 31 | 32 | /** Stops all animations. */ 33 | public disable(): void { 34 | const self = this; 35 | if (!self._stopped) { 36 | self._stopped = true; 37 | cancelAnimationFrame(self._animation); 38 | } 39 | } 40 | 41 | /** Renables animations. Many will likely snap to the end of their animation. */ 42 | public enable(): void { 43 | const self = this; 44 | 45 | if (self._stopped) { 46 | self._stopped = false; 47 | self._animation = requestAnimationFrame(self._animate.bind(self)); 48 | } 49 | } 50 | 51 | /** 52 | * Adds an animated object to the animation queue. 53 | * @param animatable The object to animate. 54 | */ 55 | public add(animatable: IPlayableAnimation): number { 56 | const self = this; 57 | 58 | if (!animatable._id) { 59 | animatable._id = self._getUuid(); 60 | } 61 | 62 | const animation = self._idTable[animatable._id]; 63 | 64 | //Only add the animation to the queue if it isn't already in it. 65 | if (!animation) { 66 | self._queue.push(animatable); 67 | self._idTable[animatable._id] = animatable; 68 | } 69 | 70 | return animatable._id; 71 | } 72 | 73 | /** 74 | * Gets an animation by ID. 75 | * @param id The ID of the animation to get. 76 | */ 77 | public getById(id: number): IPlayableAnimation { 78 | return this._idTable[id]; 79 | } 80 | 81 | /** 82 | * Removes a object from the animation queue. 83 | * @param animatable The object to remove from the queue. 84 | */ 85 | public remove(animatable: IPlayableAnimation): void { 86 | const self = this; 87 | 88 | //Verify animation is in queue. 89 | if (animatable) { 90 | const id = animatable._id; 91 | 92 | if (self._idTable[id]) { 93 | const q = self._queue; 94 | 95 | //Loop through and find the index of the animation in the array. 96 | for (let i = q.length - 1; i >= 0; i--) { 97 | if (id === q[i]._id) { 98 | //Remove it from the queue. 99 | self._queue = q.splice(i, 1); 100 | //Remove it from the lookup table. 101 | self._idTable[id] = undefined; 102 | break; 103 | } 104 | } 105 | } 106 | } 107 | } 108 | 109 | /** 110 | * Removes an animation from the queue by ID. 111 | * @param id The ID of the animation to remove. 112 | */ 113 | public removeById(id: number): void { 114 | this.remove(this._idTable[id]); 115 | } 116 | 117 | /**************************** 118 | * Public static properties 119 | ***************************/ 120 | 121 | /** A blobal static instance of the AnimationManager. */ 122 | public static instance = new AnimationManager(); 123 | 124 | /**************************** 125 | * Private functions 126 | ***************************/ 127 | 128 | /** Loops through the queue and animates a frame for each animatable object. */ 129 | private _animate(): void { 130 | const self = this; 131 | if (!self._stopped) { 132 | let t = performance.now(); 133 | 134 | if (t - self._lastTime >= self._minFR) { 135 | const q = self._queue; 136 | //Iterate backwards over queue incase the _onTriggerFrameAnimation asks to remove the animation. 137 | for (let i = q.length - 1; i >= 0; i--) { 138 | try { 139 | q[i]._onAnimationProgress(t); 140 | } catch{ } 141 | } 142 | 143 | //Request the next frame of the animation. 144 | self._lastTime = t; 145 | } 146 | 147 | self._animation = requestAnimationFrame(self._animate.bind(self)); 148 | } 149 | } 150 | 151 | /** Retrieves a unique ID from the animation manager. */ 152 | private _getUuid(): number { 153 | return this._idCounter++; 154 | } 155 | } -------------------------------------------------------------------------------- /src/animations/internal/DropAnimation.ts: -------------------------------------------------------------------------------- 1 | import * as azmaps from "azure-maps-control"; 2 | import { PlayableAnimationOptions } from '../options/PlayableAnimationOptions'; 3 | import { PlayableAnimation } from '../PlayableAnimation'; 4 | 5 | /** Animates the dropping of a point geometries. */ 6 | export class DropAnimation extends PlayableAnimation { 7 | /**************************** 8 | * Private properties 9 | ***************************/ 10 | 11 | private _height = 200; 12 | private _shapes: (azmaps.Shape | azmaps.HtmlMarker)[]; 13 | private _x0: number[]; 14 | private _y0: number[]; 15 | 16 | /************************** 17 | * Constructor 18 | ***************************/ 19 | 20 | /** 21 | * Animates the dropping of point geometries or HtmlMarkers. 22 | * @param shapes An array point geometry shapes or HtmlMarkers to animatie dropping. 23 | * @param dataSourceOrMap The map or data source to drop the shapes into. 24 | * @param height The height at which to drop the shape from. Default: 200 pixels 25 | * @param options Options for the animation. 26 | */ 27 | constructor(shapes: azmaps.Shape[] | azmaps.HtmlMarker[], dataSourceOrMap?: azmaps.source.DataSource | azmaps.Map, height?: number, options?: PlayableAnimationOptions) { 28 | super(options); 29 | 30 | if (shapes && shapes.length > 0) { 31 | const self = this; 32 | self._shapes = shapes; 33 | 34 | const x0 = []; 35 | self._x0 = x0; 36 | 37 | const y0 = []; 38 | self._y0 = y0; 39 | 40 | self._height = (typeof height === 'number' && height > 0) ? height : self._height; 41 | 42 | const needsAdding = []; 43 | let offset: number[]; 44 | 45 | let ds:azmaps.source.DataSource; 46 | let map: azmaps.Map; 47 | let markers: azmaps.HtmlMarker[] = []; 48 | 49 | if(dataSourceOrMap instanceof azmaps.source.DataSource){ 50 | ds = dataSourceOrMap; 51 | } 52 | 53 | if(dataSourceOrMap instanceof azmaps.Map){ 54 | map = dataSourceOrMap; 55 | markers = map.markers.getMarkers(); 56 | } 57 | 58 | //Extract the offsets for each shape. 59 | for (let i = 0, len = shapes.length; i < len; i++) { 60 | offset = null; 61 | 62 | if(shapes[i] instanceof azmaps.Shape){ 63 | let prop = (shapes[i]).getProperties(); 64 | 65 | offset = prop['offset']; 66 | } else { 67 | offset = (shapes[i]).getOptions().pixelOffset; 68 | } 69 | 70 | if (offset && Array.isArray(offset) && offset.length >= 2) { 71 | x0.push(offset[0]); 72 | y0.push(offset[1]); 73 | } else { 74 | x0.push(0); 75 | y0.push(0); 76 | 77 | offset = [0, 0]; 78 | } 79 | 80 | offset[1] -= self._height; 81 | 82 | if(shapes[i] instanceof azmaps.Shape){ 83 | const s = (shapes[i]); 84 | s.setProperties(Object.assign(s.getProperties(), { 85 | offset: offset, 86 | opacity: 0 87 | })); 88 | 89 | //Add the shape to the data source if it isn't already added. 90 | if (ds && ds.getShapeById((shapes[i]).getId()) === null) { 91 | needsAdding.push(shapes[i]); 92 | } 93 | } else { 94 | const m = shapes[i]; 95 | (m).setOptions({ pixelOffset: offset, visible: false }); 96 | 97 | if(map && markers && markers.indexOf(m) === -1){ 98 | map.markers.add(m); 99 | } 100 | } 101 | } 102 | 103 | if(ds && needsAdding.length > 0){ 104 | ds.add(needsAdding); 105 | } 106 | 107 | if (options && options.autoPlay) { 108 | self.play(); 109 | } 110 | } else { 111 | throw 'No shape specified for animation.'; 112 | } 113 | } 114 | 115 | /************************** 116 | * Public Methods 117 | ***************************/ 118 | 119 | /** 120 | * The function that contains the animation frame logic. 121 | * @param timestamp Timestamp from `performance.now()` that for the animation frame relative to the start time. 122 | */ 123 | public onAnimationProgress(progress: number): any { 124 | const self = this; 125 | const shapes = self._shapes; 126 | let offset: number[]; 127 | let y1: number; 128 | 129 | for (let i = 0, len = shapes.length; i < len; i++) { 130 | y1 = self._y0[i] - self._height * (1 - progress); 131 | 132 | offset = [self._x0[i], y1]; 133 | 134 | if(shapes[i] instanceof azmaps.Shape){ 135 | let s = (shapes[i]); 136 | s.setProperties(Object.assign(s.getProperties(), { 137 | offset: offset, 138 | opacity: (progress !== 0)? 1 : 0 139 | })); 140 | } else { 141 | (shapes[i]).setOptions({ pixelOffset: offset, visible: progress !== 0 }); 142 | } 143 | } 144 | 145 | return null; 146 | } 147 | } -------------------------------------------------------------------------------- /src/animations/internal/MapPathPlayableAnimation.ts: -------------------------------------------------------------------------------- 1 | import * as azmaps from "azure-maps-control"; 2 | import { PlayableAnimation } from '../PlayableAnimation'; 3 | import { MapPathAnimationOptions } from '../options/MapPathAnimationOptions'; 4 | 5 | /** An abstract class which defines an animation that will animate the maps camera on each frame as part of a larger animation. */ 6 | export abstract class MapPathPlayableAnaimation extends PlayableAnimation { 7 | 8 | /************************** 9 | * Private Properties 10 | ***************************/ 11 | 12 | protected _pathOptions: MapPathAnimationOptions = { 13 | duration: 1000 14 | }; 15 | 16 | /************************** 17 | * Constructor 18 | ***************************/ 19 | 20 | constructor(options?: MapPathAnimationOptions) { 21 | super(options); 22 | 23 | this.setOptions(options); 24 | } 25 | 26 | /************************** 27 | * Public functions 28 | ***************************/ 29 | 30 | /** Disposes the animation. */ 31 | public dispose(): void { 32 | this._pathOptions = null; 33 | super.dispose(); 34 | } 35 | 36 | /** Gets the animation options. */ 37 | public getOptions(): MapPathAnimationOptions { 38 | return Object.assign({}, super.getOptions(), this._pathOptions); 39 | } 40 | 41 | /** Sets the options of the animation. */ 42 | public setOptions(options: MapPathAnimationOptions): void { 43 | if (options) { 44 | const self = this; 45 | const opt = self._pathOptions; 46 | 47 | if (typeof options.duration === 'number' && options.duration > 0) { 48 | opt.duration = options.duration || opt.duration; 49 | } 50 | 51 | if (typeof options.captureMetadata === 'boolean') { 52 | opt.captureMetadata = options.captureMetadata; 53 | } 54 | 55 | if (typeof options.geodesic === 'boolean') { 56 | opt.geodesic = options.geodesic; 57 | } 58 | 59 | if (typeof options.reverse === 'boolean') { 60 | opt.reverse = options.reverse; 61 | } 62 | 63 | if (typeof options.pitch === 'number') { 64 | opt.pitch = options.pitch; 65 | } 66 | 67 | if (typeof options.zoom === 'number') { 68 | opt.zoom = options.zoom; 69 | } 70 | 71 | if (typeof options.rotate === 'boolean') { 72 | opt.rotate = options.rotate; 73 | } 74 | 75 | if (typeof options.rotationOffset === 'number') { 76 | opt.rotationOffset = options.rotationOffset; 77 | } 78 | 79 | if (options.map || options.map === null) { 80 | opt.map = options.map; 81 | } 82 | 83 | super.setOptions(options); 84 | } 85 | } 86 | 87 | /************************** 88 | * Protected functions 89 | ***************************/ 90 | 91 | protected _setMapCamera(position: azmaps.data.Position, heading: number, animate: boolean): void { 92 | const opt = this._pathOptions; 93 | 94 | if (opt.map && position) { 95 | let cam = { 96 | center: position 97 | }; 98 | 99 | if (typeof opt.pitch === 'number') { 100 | cam.pitch = opt.pitch; 101 | } 102 | 103 | if (typeof opt.zoom === 'number') { 104 | cam.zoom = opt.zoom; 105 | } 106 | 107 | if (opt.rotate && typeof heading === 'number') { 108 | cam.bearing = (opt.reverse)? heading + 180: heading; 109 | 110 | if (typeof opt.rotationOffset === 'number') { 111 | cam.bearing += opt.rotationOffset; 112 | } 113 | } 114 | 115 | if (animate) { 116 | cam.type = 'fly'; 117 | cam.duration = Math.min(60, opt.duration); 118 | } else { 119 | cam.type = 'jump'; 120 | } 121 | 122 | //Set the initial view of the map. 123 | opt.map.setCamera(cam); 124 | } 125 | } 126 | 127 | public onAnimationProgress(progress: number): void { 128 | return null; 129 | } 130 | } -------------------------------------------------------------------------------- /src/animations/internal/OpacityAnimation.ts: -------------------------------------------------------------------------------- 1 | import * as azmaps from "azure-maps-control"; 2 | import { PlayableAnimationOptions } from '../options/PlayableAnimationOptions'; 3 | import { PlayableAnimation } from '../PlayableAnimation'; 4 | 5 | /** Animates the opacity of a feature. */ 6 | export class OpacityAnimation extends PlayableAnimation { 7 | /**************************** 8 | * Private properties 9 | ***************************/ 10 | 11 | private _shapes: azmaps.Shape[]; 12 | private _minOpacity: number; 13 | private _opacityWidth: number; 14 | 15 | /************************** 16 | * Constructor 17 | ***************************/ 18 | 19 | /** 20 | * Animates the opacity of a feature. 21 | * @param shapes An array shapes or HtmlMarkers to animatie opacity. 22 | * @param initialOpacity The initial opacity of the shape. Default: `0` 23 | * @param finalOpacity The final opacity of the shape. Default: `1` 24 | * @param options Options for the animation. 25 | */ 26 | constructor(shapes: azmaps.Shape[], initialOpacity?: number, finalOpacity?: number, options?: PlayableAnimationOptions) { 27 | super(options); 28 | 29 | initialOpacity = initialOpacity || 0; 30 | finalOpacity = finalOpacity || 1 31 | 32 | if(initialOpacity > finalOpacity) { 33 | var t = finalOpacity; 34 | finalOpacity = initialOpacity; 35 | initialOpacity = t; 36 | } 37 | 38 | this._minOpacity = initialOpacity; 39 | this._opacityWidth = finalOpacity - initialOpacity; 40 | 41 | if (shapes && shapes.length > 0) { 42 | const self = this; 43 | self._shapes = shapes; 44 | 45 | //Extract the offsets for each shape. 46 | shapes.forEach(s => { 47 | s.setProperties(Object.assign(s.getProperties(), { 48 | opacity: initialOpacity 49 | })); 50 | }); 51 | 52 | if (options && options.autoPlay) { 53 | self.play(); 54 | } 55 | } else { 56 | throw 'No shape specified for animation.'; 57 | } 58 | } 59 | 60 | /************************** 61 | * Public Methods 62 | ***************************/ 63 | 64 | public onAnimationProgress(progress: number): any { 65 | const self = this; 66 | self._shapes.forEach(s => { 67 | s.setProperties(Object.assign(s.getProperties(), { 68 | opacity: self._minOpacity + self._opacityWidth * progress 69 | })); 70 | }); 71 | 72 | return null; 73 | } 74 | } -------------------------------------------------------------------------------- /src/animations/internal/PathAnimation.ts: -------------------------------------------------------------------------------- 1 | import * as azmaps from "azure-maps-control"; 2 | import { MapPathAnimationOptions } from '../options/MapPathAnimationOptions'; 3 | import { Utils } from '../../helpers/Utils'; 4 | import { MapPathPlayableAnaimation } from './MapPathPlayableAnimation'; 5 | 6 | /** Translates a Point object along a path or animates a LineString as a snakeline. */ 7 | export class PathAnimation extends MapPathPlayableAnaimation { 8 | 9 | /************************** 10 | * Private Properties 11 | ***************************/ 12 | 13 | private _totalLength: number; 14 | private _positions: azmaps.data.Position[]; 15 | private _pixels: azmaps.Pixel[]; 16 | private _distances: number[]; 17 | private _headings: number[]; 18 | private _shape: azmaps.Shape | azmaps.HtmlMarker; 19 | 20 | /************************** 21 | * Constructor 22 | ***************************/ 23 | 24 | constructor(path: azmaps.data.Position[], shape?: azmaps.Shape | azmaps.HtmlMarker, options?: MapPathAnimationOptions) { 25 | super(); 26 | const self = this; 27 | 28 | self._shape = shape; 29 | self._positions = path; 30 | 31 | self.setOptions(Object.assign({ 32 | rotate: true, 33 | rotationOffset: 0 34 | }, options || {})); 35 | 36 | if (options && options.autoPlay) { 37 | self.play(); 38 | } 39 | } 40 | 41 | /************************** 42 | * Public Methods 43 | ***************************/ 44 | 45 | /** Gets the animation options. */ 46 | public dispose(): void { 47 | Object.keys(this).forEach(k => { 48 | this[k] = undefined; 49 | }); 50 | 51 | super.dispose(); 52 | } 53 | 54 | /** Sets the options of the animation. */ 55 | public setOptions(options: MapPathAnimationOptions): void { 56 | const self = this; 57 | 58 | if(options){ 59 | super.setOptions(options); 60 | } 61 | 62 | let isPlaying = self.isPlaying(); 63 | 64 | if(isPlaying){ 65 | self.pause(); 66 | } 67 | 68 | if(self._positions){ 69 | 70 | let tl = 0; 71 | let distances = []; 72 | let heading = []; 73 | const pos = self._positions; 74 | const mapMath = azmaps.math; 75 | 76 | //Calculate the distances and headings between the positions. 77 | if (self._pathOptions.geodesic) { 78 | 79 | for (let i = 1, len = pos.length; i < len; i++) { 80 | let d = mapMath.getDistanceTo(pos[i - 1], pos[i]); 81 | tl += d; 82 | distances.push(d); 83 | 84 | let h = mapMath.getHeading(pos[i - 1], pos[i]); 85 | heading.push(h); 86 | } 87 | } else { 88 | //Calculate the mercator pixels of the coordinates at zoom level 21. 89 | let pixels = mapMath.mercatorPositionsToPixels(pos, 21); 90 | self._pixels = pixels; 91 | 92 | for (let i = 1, len = pixels.length; i < len; i++) { 93 | let d = azmaps.Pixel.getDistance(pixels[i - 1], pixels[i]); 94 | tl += d; 95 | distances.push(d); 96 | 97 | let h = Utils.getPixelHeading(pixels[i - 1], pixels[i]); 98 | heading.push(h); 99 | } 100 | } 101 | 102 | self._totalLength = tl; 103 | self._distances = distances; 104 | self._headings = heading; 105 | 106 | if (self._pathOptions.captureMetadata) { 107 | Utils.setMetadata(self._shape, { heading: self._headings[0] }); 108 | } 109 | } 110 | 111 | if(isPlaying){ 112 | self.play(); 113 | } 114 | } 115 | 116 | /** 117 | * Callback function that contains the animation frame logic. 118 | * @param progress The progress of the animation where 0 is start and 1 is the end. 119 | */ 120 | public onAnimationProgress(progress: number): { position: azmaps.data.Position, heading: number} { 121 | const self = this; 122 | let pos: azmaps.data.Position; 123 | let heading: number; 124 | const shape = self._shape; 125 | 126 | const sourcePos = self._positions; 127 | const headings = self._headings; 128 | const distances = self._distances; 129 | const pathOptions = self._pathOptions; 130 | const totalLength = self._totalLength; 131 | const mapMath = azmaps.math; 132 | 133 | if (progress === 1) { 134 | //Animation is done. 135 | pos = sourcePos[sourcePos.length - 1]; 136 | heading = (headings.length > 0) ? headings[headings.length - 1] : undefined; 137 | 138 | if (pathOptions.map) { 139 | self._setMapCamera(pos, heading, false); 140 | } 141 | 142 | Utils.setCoordinates(shape, pos, sourcePos); 143 | } else if (progress === 0) { 144 | pos = sourcePos[0]; 145 | heading = (headings.length > 0)? headings[0] : undefined; 146 | 147 | if (pathOptions.map) { 148 | self._setMapCamera(pos, heading, false); 149 | } 150 | 151 | Utils.setCoordinates(shape, pos, [pos, pos]); 152 | } else { 153 | const dx = totalLength * progress; 154 | let positions: azmaps.data.Position[] = null; 155 | 156 | //Calculate the coordinate part way between the origin and destination. 157 | if (pathOptions.geodesic) { 158 | 159 | if (dx > totalLength) { 160 | heading = headings[headings.length - 1]; 161 | positions = sourcePos.slice(0); 162 | } else if (dx < 0) { 163 | heading = headings[0]; 164 | positions = sourcePos.slice(0, 1); 165 | } else { 166 | let travelled = 0; 167 | 168 | for (let i = 0; i < distances.length; i++) { 169 | if (travelled + distances[i] >= dx) { 170 | heading = headings[i]; 171 | positions = sourcePos.slice(0, i + 1); 172 | positions.push(mapMath.getDestination(sourcePos[i], heading, dx - travelled)); 173 | break; 174 | } else { 175 | travelled += distances[i]; 176 | } 177 | } 178 | } 179 | } else { 180 | let px = null; 181 | const pixels = self._pixels; 182 | 183 | if (dx > totalLength) { 184 | heading = headings[headings.length - 1]; 185 | px = Utils.getPixelDestination(pixels[pixels.length - 1], heading, dx - totalLength); 186 | positions = sourcePos.slice(0); 187 | positions.push(mapMath.mercatorPixelsToPositions([px], 21)[0]); 188 | } else if (dx < 0) { 189 | heading = headings[0]; 190 | px = Utils.getPixelDestination(pixels[0], heading, dx); 191 | positions = sourcePos.slice(0, 1); 192 | positions.push(mapMath.mercatorPixelsToPositions([px], 21)[0]); 193 | } else { 194 | let travelled = 0; 195 | 196 | for (let i = 0; i < distances.length; i++) { 197 | if (travelled + distances[i] >= dx) { 198 | heading = headings[i]; 199 | px = Utils.getPixelDestination(pixels[i], heading, dx - travelled); 200 | positions = sourcePos.slice(0, i + 1); 201 | positions.push(mapMath.mercatorPixelsToPositions([px], 21)[0]); 202 | break; 203 | } else { 204 | travelled += distances[i]; 205 | } 206 | } 207 | } 208 | } 209 | 210 | if (positions && positions.length > 0) { 211 | pos = positions[positions.length - 1]; 212 | 213 | if (pathOptions.map) { 214 | //Animate to the next view. 215 | self._setMapCamera(pos, heading, positions.length > 2); 216 | } 217 | 218 | Utils.setCoordinates(shape, pos, positions); 219 | } 220 | } 221 | 222 | if (pathOptions.captureMetadata) { 223 | Utils.setMetadata(shape, { heading: heading }); 224 | } 225 | 226 | return { 227 | position: pos, 228 | heading: heading 229 | }; 230 | } 231 | } -------------------------------------------------------------------------------- /src/animations/internal/PointTranslateAnimation.ts: -------------------------------------------------------------------------------- 1 | import * as azmaps from "azure-maps-control"; 2 | import { MapPathAnimationOptions } from '../options/MapPathAnimationOptions'; 3 | import { MapPathPlayableAnaimation } from './MapPathPlayableAnimation'; 4 | import { Utils } from '../../helpers/Utils'; 5 | 6 | /** Translates a Point object from one coordinate to another. */ 7 | export class PointTranslateAnimation extends MapPathPlayableAnaimation { 8 | /**************************** 9 | * Private properties 10 | ***************************/ 11 | 12 | private _shape: azmaps.Shape | azmaps.HtmlMarker; 13 | private _originPosition: azmaps.data.Position; 14 | private _destinationPosition: azmaps.data.Position; 15 | private _dx: number; 16 | private _heading: number; 17 | private _originPixel: azmaps.Pixel; 18 | 19 | /************************** 20 | * Constructor 21 | ***************************/ 22 | 23 | /** 24 | * Animates the dropping of a point geometries. 25 | * @param shapes An array point geometry shapes to animatie dropping. 26 | * @param options Options for the animation. 27 | */ 28 | constructor(shape: azmaps.Shape | azmaps.HtmlMarker, newPosition?: azmaps.data.Position, options?: MapPathAnimationOptions) { 29 | super(); 30 | const self = this; 31 | 32 | let pos: azmaps.data.Position; 33 | if(shape instanceof azmaps.Shape){ 34 | pos = shape.getCoordinates() as azmaps.data.Position; 35 | } else { 36 | pos = shape.getOptions().position; 37 | } 38 | 39 | self._originPosition = pos; 40 | self._shape = shape; 41 | self._destinationPosition = newPosition; 42 | 43 | self.setOptions(options); 44 | 45 | if (options && options.autoPlay) { 46 | self.play(); 47 | } 48 | } 49 | 50 | /************************** 51 | * Public Methods 52 | ***************************/ 53 | 54 | /** Sets the options of the animation. */ 55 | public setOptions(options: MapPathAnimationOptions): void { 56 | if(options){ 57 | super.setOptions(options); 58 | } 59 | 60 | const self = this; 61 | const oPos = self._originPosition; 62 | const destPos = self._destinationPosition; 63 | const mapMath = azmaps.math; 64 | const azPixel = azmaps.Pixel; 65 | 66 | if(oPos && destPos){ 67 | if (self._pathOptions.geodesic) { 68 | //Calculate the distance and heading between the points. 69 | self._dx = mapMath.getDistanceTo(oPos, destPos); 70 | self._heading = mapMath.getHeading(oPos, destPos); 71 | } else { 72 | //Calculate the mercator pixels of the coordinates at zoom level 21. 73 | let pixels = mapMath.mercatorPositionsToPixels([oPos, destPos], 21); 74 | self._originPixel = pixels[0]; 75 | 76 | //Ensure that the shortest path is taken between coordinates. 77 | if (Math.abs(oPos[0] - destPos[0]) > 180) { 78 | let mapWidth = Math.pow(2, 21) * 512; 79 | 80 | if (pixels[0][0] > pixels[1][0]) { 81 | pixels[1][0] += mapWidth; 82 | } else { 83 | pixels[0][0] += mapWidth; 84 | } 85 | } 86 | 87 | //Calculate the distance and heading between the pixels. 88 | self._dx = azPixel.getDistance(pixels[0], pixels[1]); 89 | self._heading = azPixel.getHeading(pixels[0], pixels[1]); 90 | } 91 | 92 | if (self._pathOptions.captureMetadata) { 93 | Utils.setMetadata(self._shape, { 94 | heading: self._heading 95 | }); 96 | } 97 | } 98 | } 99 | 100 | /** 101 | * Callback function that contains the animation frame logic. 102 | * @param progress The progress of the animation where 0 is start and 1 is the end. 103 | */ 104 | public onAnimationProgress(progress: number): { 105 | position: azmaps.data.Position, 106 | heading: number 107 | } { 108 | const self = this; 109 | const oPos = self._originPosition; 110 | const heading = self._heading; 111 | const destPos = self._destinationPosition; 112 | const mapMath = azmaps.math; 113 | const opt = self._pathOptions; 114 | 115 | if(oPos && destPos && opt){ 116 | let pos: azmaps.data.Position; 117 | let animateCamera = false; 118 | 119 | if (progress === 1) { 120 | //Animation is done. 121 | pos = destPos; 122 | } else if (progress === 0) { 123 | //Restart animation. 124 | pos = oPos; 125 | } else { 126 | let dx = self._dx * progress; 127 | 128 | //Calculate the coordinate part way between the origin and destination. 129 | if (opt.geodesic) { 130 | pos = mapMath.getDestination(oPos, heading, dx); 131 | } else { 132 | pos = mapMath.mercatorPixelsToPositions([azmaps.Pixel.getDestination(self._originPixel, heading, dx)], 21)[0]; 133 | } 134 | 135 | animateCamera = true; 136 | } 137 | 138 | Utils.setCoordinates(self._shape, pos); 139 | 140 | if (opt.map) { 141 | self._setMapCamera(pos, heading, animateCamera); 142 | } 143 | 144 | return { 145 | position: pos, 146 | heading: heading 147 | }; 148 | } 149 | 150 | return null; 151 | } 152 | } -------------------------------------------------------------------------------- /src/animations/internal/SimpleIntervalAnimation.ts: -------------------------------------------------------------------------------- 1 | import { IPlayableAnimation } from '../interfaces/IPlayableAnimation'; 2 | import { AnimationManager } from './AnimationManager'; 3 | 4 | /** A simple animation class that can replace the logic of setTimeout and setInterval. */ 5 | export class SimpleIntervalAnimation implements IPlayableAnimation { 6 | 7 | public _id: number; 8 | public _onComplete: () => void; 9 | 10 | private _start: number; 11 | 12 | private _intervalCb: string | Function; 13 | private _delay: number = 1; 14 | private _numberOfInv: number = Infinity; 15 | private _currentInterval: number = 0; 16 | private _arguments: any[]; 17 | 18 | /** 19 | * A simple animation class that can replace the logic of setTimeout and setInterval. 20 | * @param intervalCallback The callback function for each interval. 21 | * @param delay The interval time in ms. 22 | * @param numberOfIOntervals The number of intervals. 23 | * @param arguments Any additional arguments to pass to the callback function. 24 | */ 25 | constructor(intervalCallback: string | Function, delay: number, numberOfIOntervals?: number, ...args: any[]) { 26 | const self = this; 27 | self._id = AnimationManager.instance.add(self); 28 | self._intervalCb = intervalCallback; 29 | self._arguments = args; 30 | 31 | if(delay >= 0){ 32 | self._delay = delay; 33 | } 34 | 35 | if(numberOfIOntervals > 0){ 36 | self._numberOfInv = numberOfIOntervals; 37 | } 38 | } 39 | 40 | /** Disposes the animation. */ 41 | public dispose(): void { 42 | const self = this; 43 | AnimationManager.instance.remove(self); 44 | 45 | Object.keys(self).forEach(k => { 46 | self[k] = undefined; 47 | }); 48 | } 49 | 50 | /** Gets the duration of the animation. Returns Infinity if the animations loops forever. */ 51 | public getDuration(): number { 52 | return this._numberOfInv * this._delay; 53 | } 54 | 55 | /** Checks to see if the animaiton is playing. */ 56 | public isPlaying(): boolean { 57 | return this._start != null; 58 | } 59 | 60 | /** Pauses the animation. */ 61 | public pause(): void { 62 | this._start = null; 63 | } 64 | 65 | /** Plays the animation. */ 66 | public play(): void { 67 | this._start = performance.now(); 68 | } 69 | 70 | /** Stops the animation and resets the interval back to 0. */ 71 | public reset(): void { 72 | this._start = null; 73 | this._currentInterval = 0; 74 | } 75 | 76 | /** Stops the animation and jumps to the last interval. */ 77 | public stop(): void { 78 | const self = this; 79 | self._start = null; 80 | self._currentInterval = self._numberOfInv; 81 | } 82 | 83 | public _onAnimationProgress(timestamp: number): void { 84 | const self = this; 85 | 86 | if (self._start) { 87 | let intervalIdx = Math.round((timestamp - self._start) / self._delay); 88 | 89 | if(intervalIdx !== self._currentInterval){ 90 | self._currentInterval = intervalIdx; 91 | 92 | if(self._intervalCb){ 93 | //Call setTimeout without any time, so that it calls the callback function asynchronously. 94 | setTimeout(self._intervalCb, 0, self._arguments); 95 | } 96 | 97 | if(intervalIdx >= self._numberOfInv){ 98 | self._start = null; 99 | 100 | if(self._onComplete){ 101 | self._onComplete(); 102 | self.dispose(); 103 | } 104 | } 105 | } 106 | } 107 | } 108 | } -------------------------------------------------------------------------------- /src/animations/internal/morph/GeometryInterpolator.ts: -------------------------------------------------------------------------------- 1 | import * as azmaps from "azure-maps-control"; 2 | import { SimpleGeometryInterpolator } from './SimpleGeometryInterpolator'; 3 | 4 | export class GeometryInterpolator { 5 | private _interps: SimpleGeometryInterpolator[] = []; 6 | private _fromGeom: azmaps.data.Geometry; 7 | private _toGeom: azmaps.data.Geometry; 8 | 9 | constructor(fromGeometry: azmaps.data.Geometry, toGeometry: azmaps.data.Geometry) { 10 | const self = this; 11 | self._fromGeom = fromGeometry; 12 | self._toGeom = toGeometry; 13 | 14 | self._initInterps(); 15 | } 16 | 17 | public interpolate(progress: number): azmaps.data.Geometry { 18 | const self = this; 19 | const toGeom = self._toGeom; 20 | const interps = self._interps; 21 | 22 | if(progress === 0){ 23 | return self._fromGeom; 24 | } else if (progress === 1){ 25 | return toGeom; 26 | } 27 | 28 | if(toGeom.type === 'MultiPolygon'){ 29 | const c: azmaps.data.Position[][][] = []; 30 | 31 | interps.forEach(interpolator => { 32 | c.push(interpolator.interpolate(progress).coordinates); 33 | }); 34 | 35 | return { 36 | type: 'MultiPolygon', 37 | coordinates: c 38 | }; 39 | } else if (toGeom.type === 'GeometryCollection'){ 40 | const geoms: azmaps.data.Geometry[] = []; 41 | 42 | interps.forEach(interpolator => { 43 | geoms.push(interpolator.interpolate(progress)); 44 | }); 45 | 46 | //@ts-ignore. 47 | return { 48 | type: 'GeometryCollection', 49 | geometries: geoms 50 | }; 51 | } 52 | 53 | return interps[0].interpolate(progress); 54 | } 55 | 56 | private _initInterps() { 57 | const self = this; 58 | 59 | const fromGeoms: azmaps.data.Geometry[] = []; 60 | const toGeoms: azmaps.data.Geometry[] = []; 61 | 62 | self._extractGeoms(self._fromGeom, fromGeoms); 63 | self._extractGeoms(self._toGeom, toGeoms); 64 | 65 | //Fill gap of geometries transitioning from. 66 | if(fromGeoms.length < toGeoms.length){ 67 | 68 | if(fromGeoms.length > 0){ 69 | const c = azmaps.data.BoundingBox.getCenter(azmaps.data.BoundingBox.fromData(fromGeoms[0])); 70 | const fillGeom: azmaps.data.Geometry = { 71 | type: fromGeoms[0].type, 72 | coordinates: [] 73 | }; 74 | 75 | switch(fromGeoms[0].type){ 76 | case 'Point': 77 | fillGeom.coordinates = c; 78 | break; 79 | case 'LineString': 80 | case 'MultiPoint': 81 | fillGeom.coordinates = [c, c]; 82 | break; 83 | case 'MultiLineString': 84 | case 'Polygon': 85 | fillGeom.coordinates = [[c, c, c]]; 86 | break; 87 | } 88 | 89 | //Transitioning "from" less geometries to more, expand from a point near the center of the "from" geometry and expand to the "to" geometry. 90 | for(let i = fromGeoms.length, len = toGeoms.length; i < len; i++){ 91 | fromGeoms.push(fillGeom); 92 | } 93 | } 94 | } 95 | 96 | //Crate interpolators 97 | for(let i = 0; i < toGeoms.length; i++){ 98 | self._interps.push(new SimpleGeometryInterpolator(fromGeoms[i], toGeoms[i])); 99 | } 100 | } 101 | 102 | private _extractGeoms(geom: azmaps.data.Geometry, targetArray: azmaps.data.Geometry[]){ 103 | switch(geom.type){ 104 | case 'MultiPolygon': 105 | geom.coordinates.forEach(p => { 106 | targetArray.push({ 107 | type: 'Polygon', 108 | coordinates: p 109 | }); 110 | }); 111 | break; 112 | case 'GeometryCollection': 113 | //@ts-ignore 114 | (geom).geometries.forEach(g => { 115 | this._extractGeoms(g, targetArray); 116 | }); 117 | break; 118 | default: 119 | targetArray.push(geom); 120 | break; 121 | } 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /src/animations/internal/morph/MorphShapeAnimation.ts: -------------------------------------------------------------------------------- 1 | import * as azmaps from "azure-maps-control"; 2 | import { MapPathAnimationOptions } from '../../options/MapPathAnimationOptions'; 3 | import { AnimationManager } from '../AnimationManager'; 4 | import { Utils } from '../../../helpers/Utils'; 5 | import { GeometryInterpolator } from './GeometryInterpolator'; 6 | import { MapPathPlayableAnaimation } from '../MapPathPlayableAnimation'; 7 | 8 | /** Animates the morphing of a shape from one geometry type or set of coordinates to another. */ 9 | export class MorphShapeAnimation extends MapPathPlayableAnaimation { 10 | 11 | /************************** 12 | * Private Properties 13 | ***************************/ 14 | 15 | private _shape: azmaps.Shape; 16 | private _interp: GeometryInterpolator; 17 | private _heading: number; 18 | private _pixelHeading: number; 19 | 20 | /************************** 21 | * Constructor 22 | ***************************/ 23 | 24 | constructor(shape: azmaps.Shape, newGeometry: azmaps.data.Geometry, options?: MapPathAnimationOptions) { 25 | super(); 26 | const self = this; 27 | const bbox = azmaps.data.BoundingBox; 28 | 29 | self._shape = shape; 30 | 31 | var g = shape.toJson().geometry; 32 | 33 | //For circles, if the new geometry is not a point, then pass in a polygon of the circle. 34 | if(shape.isCircle() && newGeometry.type !== 'Point') { 35 | g = new azmaps.data.Polygon(shape.getCircleCoordinates()); 36 | } 37 | 38 | self._interp = new GeometryInterpolator(shape.toJson().geometry, newGeometry); 39 | 40 | var lastCenter = bbox.getCenter(self._shape.getBounds()); 41 | var newCenter = bbox.getCenter(bbox.fromData(newGeometry)); 42 | 43 | self._heading = azmaps.math.getHeading(lastCenter, newCenter); 44 | 45 | var pixels = azmaps.math.mercatorPositionsToPixels([lastCenter, newCenter], 21); 46 | self._pixelHeading = Utils.getPixelHeading(pixels[0], pixels[1]) 47 | 48 | if (options){ 49 | self.setOptions(options); 50 | 51 | if(options.autoPlay) { 52 | self.play(); 53 | } 54 | } 55 | 56 | AnimationManager.instance.add(self); 57 | } 58 | 59 | /************************** 60 | * Public Methods 61 | ***************************/ 62 | 63 | public onAnimationProgress(progress: number): { 64 | position: azmaps.data.Position, 65 | heading: number 66 | } { 67 | const self = this; 68 | const bbox = azmaps.data.BoundingBox; 69 | const g = self._interp.interpolate(progress); 70 | 71 | const newCenter = bbox.getCenter(bbox.fromData(g)); 72 | 73 | let heading: number = 0; 74 | 75 | if(self._pathOptions.geodesic){ 76 | heading = self._heading; 77 | } else { 78 | heading = self._pixelHeading; 79 | } 80 | 81 | if(self._pathOptions.map){ 82 | self._setMapCamera(newCenter, heading, true); 83 | } 84 | 85 | let s = self._shape; 86 | 87 | if(self._pathOptions.captureMetadata){ 88 | s.addProperty('heading', heading); 89 | } 90 | 91 | //If shape is a circle and geometry is a Point, just set coordinates. 92 | if(s.isCircle() && g.type === 'Point') { 93 | s.setCoordinates(g.coordinates); 94 | } else { 95 | //TODO: Update with supported function in future. 96 | s['data'].geometry.type = g.type; 97 | s.setCoordinates(g.coordinates); 98 | } 99 | 100 | return { 101 | position: newCenter, 102 | heading: heading 103 | }; 104 | } 105 | } -------------------------------------------------------------------------------- /src/animations/internal/morph/RingInterpolator.ts: -------------------------------------------------------------------------------- 1 | import * as azmaps from "azure-maps-control"; 2 | import flubber from "flubber"; 3 | 4 | export class RingInterpolator { 5 | private _interp: any; 6 | private _constPos: azmaps.data.Position[]; 7 | 8 | constructor(fromRing: azmaps.data.Position[], toRing: azmaps.data.Position[]) { 9 | //If positions arrays are identical, don't use interpolate progress as it may add artifacts. 10 | let areEqual = true; 11 | 12 | if(fromRing.length !== toRing.length){ 13 | areEqual = false; 14 | } 15 | 16 | if(areEqual){ 17 | fromRing.forEach((val, idx) => { 18 | areEqual = areEqual && azmaps.data.Position.areEqual(val, toRing[idx]); 19 | }); 20 | } 21 | 22 | if(areEqual){ 23 | this._constPos = toRing; 24 | } else { 25 | this._interp = flubber.interpolate(<[number, number][]>fromRing, <[number, number][]>toRing, { 26 | string: false 27 | }); 28 | } 29 | } 30 | 31 | public interpolate(progress: number): azmaps.data.Position[] { 32 | const self = this; 33 | if(self._constPos){ 34 | return self._constPos; 35 | } 36 | 37 | return self._interp(progress); 38 | } 39 | } -------------------------------------------------------------------------------- /src/animations/internal/morph/SimpleGeometryInterpolator.ts: -------------------------------------------------------------------------------- 1 | import * as azmaps from "azure-maps-control"; 2 | import { RingInterpolator } from './RingInterpolator'; 3 | 4 | export class SimpleGeometryInterpolator { 5 | private _interps: RingInterpolator[] = []; 6 | private _fromGeom: azmaps.data.Geometry; 7 | private _toGeom: azmaps.data.Geometry; 8 | 9 | private _areSame = false; 10 | 11 | constructor(fromGeometry: azmaps.data.Geometry, toGeometry: azmaps.data.Geometry) { 12 | const self = this; 13 | if(fromGeometry.type === 'MultiPolygon'){ 14 | throw 'Only simple geometries supported.'; 15 | } 16 | 17 | self._fromGeom = fromGeometry; 18 | self._toGeom = toGeometry; 19 | 20 | self._areSame = (fromGeometry.type === toGeometry.type && JSON.parse(JSON.stringify(fromGeometry.coordinates)) === JSON.parse(JSON.stringify(toGeometry.coordinates))); 21 | 22 | self._initInterps(); 23 | } 24 | 25 | public interpolate(progress: number): azmaps.data.Geometry { 26 | const self = this; 27 | 28 | let fg = self._fromGeom; 29 | let tg = self._toGeom; 30 | 31 | if(self._areSame){ 32 | return tg; 33 | } 34 | 35 | if(progress === 0){ 36 | return fg; 37 | } else if (progress === 1){ 38 | return tg; 39 | } 40 | 41 | const fgPos = fg.coordinates; 42 | const tgPos = tg.coordinates; 43 | 44 | const c = self._runInterps(progress); 45 | let g:any = { type: tg.type }; 46 | 47 | switch(tg.type){ 48 | case 'Point': 49 | //If morphing to a point, keep the from shape for as long a possible. 50 | if(fg.type === 'LineString' || 51 | fg.type === 'MultiPoint'){ 52 | //Grab sample points. 53 | g = { 54 | type: fg.type, 55 | coordinates: self._sampleMultiPoint(c, fgPos.length) 56 | }; 57 | } else if(fg.type === 'Polygon' || 58 | fg.type === 'MultiLineString') { 59 | g = { 60 | type: fg.type, 61 | coordinates: c 62 | }; 63 | } else { 64 | g.coordinates = c[0][0]; 65 | } 66 | break; 67 | case 'LineString': 68 | //Remove extra points when transitioning from a polygon. 69 | if(fg.type === 'Polygon' && fgPos.length > tgPos.length){ 70 | let numRemove = Math.floor(c[0].length/(tgPos.length - 1)); 71 | 72 | for(let i=numRemove;i>= 0;i--){ 73 | c[0].pop(); 74 | } 75 | } 76 | 77 | g.coordinates = c[0]; 78 | break; 79 | case 'MultiPoint': 80 | //If morphing to a MultiPoint, keep the from shape for as long a possible. 81 | if(fg.type === 'Point'){ 82 | //Grab sample points. 83 | g.coordinates = self._sampleMultiPoint(c, tgPos.length); 84 | } else if(fg.type !== 'MultiPoint') { 85 | g = { 86 | type: fg.type, 87 | coordinates: (fg.type === 'LineString')? c[0]: c 88 | }; 89 | } else { 90 | g.coordinates = c[0]; 91 | } 92 | break; 93 | case 'Polygon': 94 | case 'MultiLineString': 95 | g.coordinates = c; 96 | break; 97 | } 98 | 99 | return g; 100 | } 101 | 102 | private _initInterps(): void { 103 | const self = this; 104 | let fg = self._fromGeom; 105 | let tg = self._toGeom; 106 | 107 | if(!self._areSame && fg && fg.coordinates.length > 0 && 108 | tg && tg.coordinates.length > 0){ 109 | 110 | const fgPos = fg.coordinates; 111 | const tgPos = tg.coordinates; 112 | 113 | let fromCoords: azmaps.data.Position[][] = []; 114 | let toCoords: azmaps.data.Position[][] = []; 115 | 116 | switch(fg.type) { 117 | case 'Point': 118 | const fc = fgPos; 119 | fromCoords = [[fc, fc, fc]]; 120 | break; 121 | case 'LineString': 122 | case 'MultiPoint': 123 | const fc2 = fgPos; 124 | fromCoords = [fc2]; 125 | break; 126 | case 'Polygon': 127 | case 'MultiLineString': 128 | if(typeof fgPos[0] === 'number'){ 129 | fromCoords = [fgPos]; 130 | } else { 131 | fromCoords = fgPos; 132 | } 133 | break; 134 | } 135 | 136 | switch(tg.type) { 137 | case 'Point': 138 | const tc = tgPos; 139 | toCoords = [[tc, tc, tc]]; 140 | break; 141 | case 'LineString': 142 | case 'MultiPoint': 143 | const tc2 = tgPos; 144 | toCoords = [tc2]; 145 | break; 146 | case 'Polygon': 147 | case 'MultiLineString': 148 | if(typeof tgPos[0] === 'number'){ 149 | toCoords = [tgPos]; 150 | } else { 151 | toCoords = tgPos; 152 | } 153 | break; 154 | } 155 | 156 | let i: number; 157 | const len = toCoords.length; 158 | 159 | //Fill gap of geometries transitioning from. 160 | if(fromCoords.length < toCoords.length){ 161 | for(i = fromCoords.length; i < len; i++){ 162 | fromCoords.push([fromCoords[0][0],fromCoords[0][0],fromCoords[0][0]]); 163 | } 164 | } 165 | 166 | for(i = 0; i < len; i++){ 167 | self._interps.push(new RingInterpolator(fromCoords[i], toCoords[i])); 168 | } 169 | } 170 | } 171 | 172 | private _runInterps(progress: number): azmaps.data.Position[][] { 173 | const c: azmaps.data.Position[][] = []; 174 | 175 | const interps = this._interps; 176 | for(let i = 0; i < interps.length; i++){ 177 | c.push(interps[i].interpolate(progress)); 178 | } 179 | 180 | return c; 181 | } 182 | 183 | private _sampleMultiPoint(c: azmaps.data.Position[][], targetSize: number): azmaps.data.Position[] { 184 | const step = Math.min(Math.ceil(c[0].length/targetSize), targetSize); 185 | const p = []; 186 | for(let i = 0; i < targetSize; i++){ 187 | p.push(c[0][i* step]); 188 | } 189 | 190 | return p; 191 | } 192 | } 193 | -------------------------------------------------------------------------------- /src/animations/options/GroupAnimationOptions.ts: -------------------------------------------------------------------------------- 1 | 2 | /** Options for a group of animations. */ 3 | export interface GroupAnimationOptions { 4 | /** How to play the animations. Default: 'together' */ 5 | playType: 'together' | 'sequential' | 'interval'; 6 | 7 | /** If the `playType` is set to `interval`, this option specifies the time interval to start each animation in milliseconds. Default: `100` */ 8 | interval?: number; 9 | 10 | /** Specifies if the animation should start automatically or wait for the play function to be called. Default: false */ 11 | autoPlay?: boolean; 12 | } -------------------------------------------------------------------------------- /src/animations/options/MapPathAnimationOptions.ts: -------------------------------------------------------------------------------- 1 | import * as azmaps from "azure-maps-control"; 2 | import { PathAnimationOptions } from './PathAnimationOptions'; 3 | 4 | /** Options for animating the map along a path. */ 5 | export interface MapPathAnimationOptions extends PathAnimationOptions { 6 | /** Map to animation along path. */ 7 | map?: azmaps.Map; 8 | 9 | /** A fixed zoom level to snap the map to on each animation frame. By default the maps current zoom level is used. */ 10 | zoom?: number; 11 | 12 | /** A pitch value to set on the map. By default this is not set. */ 13 | pitch?: number; 14 | 15 | /** Specifies if the map should rotate such that the bearing of the map faces the direction the map is moving. Default: true */ 16 | rotate?: boolean; 17 | 18 | /** When rotate is set to true, the animation will follow the animation. An offset of 180 will cause the camera to lead the animation and look back. Default: 0 */ 19 | rotationOffset?: number; 20 | } -------------------------------------------------------------------------------- /src/animations/options/MovingDashLineOptions.ts: -------------------------------------------------------------------------------- 1 | import { PlayableAnimationOptions } from './PlayableAnimationOptions'; 2 | 3 | export interface MovingDashLineOptions extends PlayableAnimationOptions{ 4 | /** The length of the dashed part of the line. Default: 4 */ 5 | dashLength: number; 6 | 7 | /** The length of the gap part of the line. Default: 4 */ 8 | gapLength: number; 9 | } -------------------------------------------------------------------------------- /src/animations/options/PathAnimationOptions.ts: -------------------------------------------------------------------------------- 1 | import { PlayableAnimationOptions } from './PlayableAnimationOptions'; 2 | 3 | /** Options for animations that involve coordiates following a path. */ 4 | export interface PathAnimationOptions extends PlayableAnimationOptions { 5 | /** Specifies if a curved geodesic path should be used between points rather than a straight pixel path. Default: false */ 6 | geodesic?: boolean; 7 | 8 | /** Specifies if metadata should be captured as properties of the shape. Potential metadata properties that may be captured: heading */ 9 | captureMetadata?: boolean; 10 | } -------------------------------------------------------------------------------- /src/animations/options/PlayableAnimationOptions.ts: -------------------------------------------------------------------------------- 1 | 2 | /** Base animation options. */ 3 | export interface PlayableAnimationOptions { 4 | /** The duration of the animation in ms. Default: 1000 ms */ 5 | duration?: number; 6 | 7 | /** Specifies if the animation should start automatically or wait for the play function to be called. Default: false */ 8 | autoPlay?: boolean; 9 | 10 | /** The easing of the animaiton. Default: linear */ 11 | easing?: string | ((progress: number) => number); 12 | 13 | /** Specifies if the animation should loop infinitely. Default: false */ 14 | loop?: boolean; 15 | 16 | /** Specifies if the animation should play backwards. Default: false */ 17 | reverse?: boolean; 18 | 19 | /** A multiplier of the duration to speed up or down the animation. Default: 1 */ 20 | speedMultiplier?: number; 21 | 22 | /** Specifies if the animation should dispose itself once it has completed. Default: false */ 23 | disposeOnComplete?: boolean; 24 | } 25 | -------------------------------------------------------------------------------- /src/animations/options/RoutePathAnimationOptions.ts: -------------------------------------------------------------------------------- 1 | import { MapPathAnimationOptions } from './MapPathAnimationOptions'; 2 | import { PointPairValueInterpolation } from '../interfaces/PointPairValueInterpolation'; 3 | 4 | /** Options for animating the map along a path. */ 5 | export interface RoutePathAnimationOptions extends MapPathAnimationOptions { 6 | /** Interpolation calculations to perform on property values between points during the animation. Requires `captureMetadata` to be enabled. */ 7 | valueInterpolations?: PointPairValueInterpolation[]; 8 | } -------------------------------------------------------------------------------- /src/extensions/EventManager.ts: -------------------------------------------------------------------------------- 1 | import { PlayableAnimationEvent, PlayableAnimation } from '../animations/PlayableAnimation'; 2 | import { FrameBasedAnimationTimer } from '../animations'; 3 | import { FrameBasedAnimationEvent } from '../animations/FrameBasedAnimationTimer'; 4 | 5 | /** 6 | * This module partially defines the map control. 7 | * This definition only includes the features added by using the drawing tools. 8 | * For the base definition see: 9 | * https://docs.microsoft.com/javascript/api/azure-maps-control/?view=azure-maps-typescript-latest 10 | */ 11 | declare module "azure-maps-control" { 12 | /** 13 | * This interface partially defines the map control's `EventManager`. 14 | * This definition only includes the method added by using the drawing tools. 15 | * For the base definition see: 16 | * https://docs.microsoft.com/javascript/api/azure-maps-control/atlas.eventmanager?view=azure-maps-typescript-latest 17 | */ 18 | export interface EventManager { 19 | /** 20 | * Adds an event to the `PlayableAnimation`. 21 | * @param eventType The event name. 22 | * @param target The `PlayableAnimation` to add the event for. 23 | * @param callback The event handler callback. 24 | */ 25 | add(eventType: "onprogress", target: PlayableAnimation, callback: (e: PlayableAnimationEvent) => void): void; 26 | 27 | /** 28 | * Adds an event to the `PlayableAnimation`. 29 | * @param eventType The event name. 30 | * @param target The `PlayableAnimation` to add the event for. 31 | * @param callback The event handler callback. 32 | */ 33 | add(eventType: "oncomplete", target: PlayableAnimation, callback: (e: PlayableAnimationEvent) => void): void; 34 | 35 | /** 36 | * Adds an event to the `FrameBasedAnimationTimer`. 37 | * @param eventType The event name. 38 | * @param target The `FrameBasedAnimationTimer` to add the event for. 39 | * @param callback The event handler callback. 40 | */ 41 | add(eventType: "onframe", target: FrameBasedAnimationTimer, callback: (e: FrameBasedAnimationEvent) => void): void; 42 | 43 | /** 44 | * Adds an event to the `PlayableAnimation` once. 45 | * @param eventType The event name. 46 | * @param target The `PlayableAnimation` to add the event for. 47 | * @param callback The event handler callback. 48 | */ 49 | addOnce(eventType: "onprogress", target: PlayableAnimation, callback: (e: PlayableAnimationEvent) => void): void; 50 | 51 | /** 52 | * Adds an event to the `PlayableAnimation` once. 53 | * @param eventType The event name. 54 | * @param target The `PlayableAnimation` to add the event for. 55 | * @param callback The event handler callback. 56 | */ 57 | addOnce(eventType: "oncomplete", target: PlayableAnimation, callback: (e: PlayableAnimationEvent) => void): void; 58 | 59 | /** 60 | * Adds an event to the `FrameBasedAnimationTimer` once. 61 | * @param eventType The event name. 62 | * @param target The `FrameBasedAnimationTimer` to add the event for. 63 | * @param callback The event handler callback. 64 | */ 65 | addOnce(eventType: "onframe", target: FrameBasedAnimationTimer, callback: (e: FrameBasedAnimationEvent) => void): void; 66 | 67 | /** 68 | * Removes an event listener from the `PlayableAnimation`. 69 | * @param eventType The event name. 70 | * @param target The `PlayableAnimation` to remove the event for. 71 | * @param callback The event handler callback. 72 | */ 73 | remove(eventType: string, target: PlayableAnimation, callback: (e?: any) => void): void; 74 | 75 | /** 76 | * Removes an event listener from the `FrameBasedAnimationTimer`. 77 | * @param eventType The event name. 78 | * @param target The `FrameBasedAnimationTimer` to remove the event for. 79 | * @param callback The event handler callback. 80 | */ 81 | remove(eventType: string, target: FrameBasedAnimationTimer, callback: (e?: any) => void): void; 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/helpers/Namespace.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Helper class for merging namespaces. 3 | */ 4 | export class Namespace { 5 | public static merge(namespace: string, base: object) { 6 | let context = window || global; 7 | const parts = namespace.split("."); 8 | 9 | for (const part of parts) { 10 | if (context[part]) { 11 | context = context[part]; 12 | } else { 13 | return base; 14 | } 15 | } 16 | 17 | return { ...context, ...base }; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import { Namespace } from "./helpers/Namespace"; 2 | 3 | /* Build the structure of the SDK */ 4 | 5 | import * as baseLayer from "./layer"; 6 | const layer = Namespace.merge("atlas.layer", baseLayer); 7 | 8 | //Merge the local controls into the 'atlas.animations' namespace. 9 | import * as baseAnimations from "./animations"; 10 | const animations = Namespace.merge("atlas.animations", baseAnimations); 11 | export { animations, layer }; 12 | -------------------------------------------------------------------------------- /src/layer/AnimatedTileLayerOptions.ts: -------------------------------------------------------------------------------- 1 | import * as azmaps from "azure-maps-control"; 2 | import { PlayableAnimationOptions } from '../animations/options/PlayableAnimationOptions'; 3 | 4 | /** An object that defines the options for an AnimatedTileLayer. */ 5 | export interface AnimatedTileLayerOptions extends PlayableAnimationOptions { 6 | /** The array of tile layers options to animate through. Note that fadeDuration and visible options are ignored. */ 7 | tileLayerOptions?: azmaps.TileLayerOptions[]; 8 | 9 | /** A boolean specifying if the animated tile layer is visible or not. Default: true */ 10 | visible?: boolean; 11 | } -------------------------------------------------------------------------------- /src/layer/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./AnimatedTileLayer"; 2 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "js", 4 | "module": "esnext", 5 | "moduleResolution": "node", 6 | "resolveJsonModule": true, 7 | "esModuleInterop": true, 8 | "importHelpers": true, 9 | "alwaysStrict": true, 10 | "target": "es5", 11 | "baseUrl": "./", 12 | "lib": [ 13 | "dom", 14 | "es5", 15 | "es2015", 16 | "es2017.object" 17 | ], 18 | "declaration": true, 19 | "declarationDir": "./js/typings", 20 | "typeRoots": [ "./types", "./node_modules/@types"] 21 | }, 22 | "include": [ 23 | "src/**/*" 24 | ], 25 | "exclude": [ 26 | "node_modules", 27 | "typings", 28 | "types" 29 | ] 30 | } -------------------------------------------------------------------------------- /types/flubber/index.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'flubber' { 2 | export type Point = [number, number]; 3 | export type Shape = string | Point[]; 4 | 5 | export interface InterpolateOptions { 6 | /** 7 | * The lower this number is, the smoother the resulting animation will be, at the expense of performance. Represents a number in pixels (if no transforms are involved). Set it to false or Infinity for no smoothing. (default: `10`) 8 | */ 9 | maxSegmentLength?: number | false 10 | /** 11 | * Whether to output results as an SVG path string or an array of points. (default: `true`) 12 | */ 13 | string?: boolean 14 | } 15 | 16 | export interface CombinatoryInterpolationOptions extends InterpolateOptions { 17 | /** 18 | * If single is false, this returns an array of n interpolator functions, where n is the length of toShapeList. If single is set to true this returns one interpolator that combines things into one giant path string or one big array of rings. (default: `false`) 19 | */ 20 | single?: boolean 21 | } 22 | 23 | interface InterpolateToStringOptions extends InterpolateOptions { 24 | string?: true 25 | } 26 | 27 | interface InterpolateToPointsOptions extends InterpolateOptions { 28 | string: false 29 | } 30 | 31 | interface MultipleInterpolateToStringOptions extends InterpolateOptions { 32 | single?: false 33 | string?: true 34 | } 35 | 36 | interface SingleInterpolateToStringOptions extends InterpolateOptions { 37 | single: true 38 | string?: true 39 | } 40 | 41 | interface MultipleInterpolateToPointsOptions extends InterpolateOptions { 42 | single?: false 43 | string: false 44 | } 45 | 46 | interface SingleInterpolateToPointsOptions extends InterpolateOptions { 47 | single: true 48 | string: false 49 | } 50 | 51 | /** 52 | * This returns a function that takes a value t from 0 to 1 and returns the in-between shape 53 | */ 54 | export function interpolate(fromShape: Shape, toShape: Shape, options?: InterpolateToStringOptions): (t: number) => string 55 | export function interpolate(fromShape: Shape, toShape: Shape, options: InterpolateToPointsOptions): (t: number) => Point[] 56 | 57 | /** 58 | * Like `interpolate()`, but for the specific case of transforming the shape to a circle centered at `[x, y]` with radius `r`. 59 | */ 60 | export function toCircle(fromShape: Shape, x: number, y: number, r: number, options?: InterpolateToStringOptions): (t: number) => string 61 | export function toCircle(fromShape: Shape, x: number, y: number, r: number, options: InterpolateToPointsOptions): (t: number) => Point[] 62 | 63 | /** 64 | * Like `interpolate()`, but for the specific case of transforming the shape to a rectangle with the upper-left corner `[x, y]` and the dimensions `width` x `height`. 65 | */ 66 | export function toRect(fromShape: Shape, x: number, y: number, width: number, height: number, options?: InterpolateToStringOptions): (t: number) => string 67 | export function toRect(fromShape: Shape, x: number, y: number, width: number, height: number, options: InterpolateToPointsOptions): (t: number) => Point[] 68 | 69 | /** 70 | * Like `toCircle()` but reversed. 71 | */ 72 | export function fromCircle(x: number, y: number, r: number, toShape: Shape, options?: InterpolateToStringOptions): (t: number) => string 73 | export function fromCircle(x: number, y: number, r: number, toShape: Shape, options: InterpolateToPointsOptions): (t: number) => Point[] 74 | 75 | /** 76 | * Like `toRect()` but reversed. 77 | */ 78 | export function fromRect(x: number, y: number, width: number, height: number, toShape: Shape, options?: InterpolateToStringOptions): (t: number) => string 79 | export function fromRect(x: number, y: number, width: number, height: number, toShape: Shape, options: InterpolateToPointsOptions): (t: number) => Point[] 80 | 81 | /** 82 | * If you're trying to interpolate between a single shape and multiple shapes (for example, a group of three circles turning into a single big circle), this method will break your shapes into pieces so you can animate between the two sets. This isn't terribly performant and has some quirks but it tends to get the job done. 83 | */ 84 | export function separate(fromShape: Shape, toShapeList: Shape[], options?: MultipleInterpolateToStringOptions): ((t: number) => string)[] 85 | export function separate(fromShape: Shape, toShapeList: Shape[], options: MultipleInterpolateToPointsOptions): ((t: number) => Point[])[] 86 | export function separate(fromShape: Shape, toShapeList: Shape[], options: SingleInterpolateToStringOptions): (t: number) => string 87 | export function separate(fromShape: Shape, toShapeList: Shape[], options: SingleInterpolateToPointsOptions): (t: number) => Point[] 88 | 89 | /** 90 | * Like `separate()` but reversed. 91 | */ 92 | export function combine(fromShapeList: Shape[], toShape: Shape, options?: MultipleInterpolateToStringOptions): ((t: number) => string)[] 93 | export function combine(fromShapeList: Shape[], toShape: Shape, options: MultipleInterpolateToPointsOptions): ((t: number) => Point[])[] 94 | export function combine(fromShapeList: Shape[], toShape: Shape, options: SingleInterpolateToStringOptions): (t: number) => string 95 | export function combine(fromShapeList: Shape[], toShape: Shape, options: SingleInterpolateToPointsOptions): (t: number) => Point[] 96 | 97 | /** 98 | * Like `separate()` or `combine()` but instead expects two arrays of shapes the same length (e.g. an array of three triangles turning into an array of three squares). The shapes will be matched up in the order of the arrays (the first `fromShapeList` item will turn into the first `toShapeList` item, and so on). 99 | */ 100 | export function interpolateAll(fromShapeList: Shape[], toShapeList: Shape[], options?: MultipleInterpolateToStringOptions): ((t: number) => string)[] 101 | export function interpolateAll(fromShapeList: Shape[], toShapeList: Shape[], options: MultipleInterpolateToPointsOptions): ((t: number) => Point[])[] 102 | export function interpolateAll(fromShapeList: Shape[], toShapeList: Shape[], options: SingleInterpolateToStringOptions): (t: number) => string 103 | export function interpolateAll(fromShapeList: Shape[], toShapeList: Shape[], options: SingleInterpolateToPointsOptions): (t: number) => Point[] 104 | 105 | /** 106 | * A helper function for converting an array of points to an SVG path string. 107 | */ 108 | export function toPathString(ring: Point[]): string 109 | 110 | /** 111 | * A helper function for splitting an SVG path string that might contain multiple shapes into an array of one-shape path strings. 112 | */ 113 | export function splitPathString(pathString: string): string[] 114 | } --------------------------------------------------------------------------------