├── .all-contributorsrc ├── .eslintignore ├── .eslintrc.js ├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── feature_request.md │ └── question.md ├── PULL_REQUEST_TEMPLATE.md ├── dependabot.yml └── workflows │ ├── ci.yml │ ├── codeql-analysis.yml │ └── size.yml ├── .gitignore ├── .husky ├── pre-commit └── pre-push ├── .nvmrc ├── .prettierignore ├── .vscode └── settings.json ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── SECURITY.md ├── app ├── .eslintignore ├── .eslintrc.js ├── .gitignore ├── .stylelintrc.js ├── README.md ├── package.json ├── public │ ├── favicon.ico │ ├── index.html │ ├── logo192.png │ ├── logo512.png │ ├── manifest.json │ ├── og_image.png │ └── robots.txt ├── src │ ├── Animations │ │ ├── index.tsx │ │ └── styles.module.scss │ ├── App │ │ ├── index.tsx │ │ └── styles.scss │ ├── GitHubCorner │ │ ├── index.tsx │ │ └── styles.module.scss │ ├── Main │ │ ├── index.tsx │ │ └── styles.module.scss │ ├── index.tsx │ ├── mixins │ │ ├── common.scss │ │ └── mq.scss │ ├── react-app-env.d.ts │ └── types │ │ └── index.d.ts ├── tsconfig.json └── yarn.lock ├── babel.config.json ├── jest.config.js ├── package.json ├── rollup.config.js ├── src ├── __tests__ │ ├── useLatest.ts │ └── useWebAnimations.ts ├── animations │ ├── backInDown.ts │ ├── backInLeft.ts │ ├── backInRight.ts │ ├── backInUp.ts │ ├── backOutDown.ts │ ├── backOutLeft.ts │ ├── backOutRight.ts │ ├── backOutUp.ts │ ├── bounce.ts │ ├── bounceIn.ts │ ├── bounceInDown.ts │ ├── bounceInLeft.ts │ ├── bounceInRight.ts │ ├── bounceInUp.ts │ ├── bounceOut.ts │ ├── bounceOutDown.ts │ ├── bounceOutLeft.ts │ ├── bounceOutRight.ts │ ├── bounceOutUp.ts │ ├── fadeIn.ts │ ├── fadeInBottomLeft.ts │ ├── fadeInBottomRight.ts │ ├── fadeInDown.ts │ ├── fadeInDownBig.ts │ ├── fadeInLeft.ts │ ├── fadeInLeftBig.ts │ ├── fadeInRight.ts │ ├── fadeInRightBig.ts │ ├── fadeInTopLeft.ts │ ├── fadeInTopRight.ts │ ├── fadeInUp.ts │ ├── fadeInUpBig.ts │ ├── fadeOut.ts │ ├── fadeOutBottomLeft.ts │ ├── fadeOutBottomRight.ts │ ├── fadeOutDown.ts │ ├── fadeOutDownBig.ts │ ├── fadeOutLeft.ts │ ├── fadeOutLeftBig.ts │ ├── fadeOutRight.ts │ ├── fadeOutRightBig.ts │ ├── fadeOutTopLeft.ts │ ├── fadeOutTopRight.ts │ ├── fadeOutUp.ts │ ├── fadeOutUpBig.ts │ ├── flash.ts │ ├── flip.ts │ ├── flipInX.ts │ ├── flipInY.ts │ ├── flipOutX.ts │ ├── flipOutY.ts │ ├── headShake.ts │ ├── heartBeat.ts │ ├── hinge.ts │ ├── index.ts │ ├── jackInTheBox.ts │ ├── jello.ts │ ├── lightSpeedInLeft.ts │ ├── lightSpeedInRight.ts │ ├── lightSpeedOutLeft.ts │ ├── lightSpeedOutRight.ts │ ├── pulse.ts │ ├── rollIn.ts │ ├── rollOut.ts │ ├── rotateIn.ts │ ├── rotateInDownLeft.ts │ ├── rotateInDownRight.ts │ ├── rotateInUpLeft.ts │ ├── rotateInUpRight.ts │ ├── rotateOut.ts │ ├── rotateOutDownLeft.ts │ ├── rotateOutDownRight.ts │ ├── rotateOutUpLeft.ts │ ├── rotateOutUpRight.ts │ ├── rubberBand.ts │ ├── shakeX.ts │ ├── shakeY.ts │ ├── slideInDown.ts │ ├── slideInLeft.ts │ ├── slideInRight.ts │ ├── slideInUp.ts │ ├── slideOutDown.ts │ ├── slideOutLeft.ts │ ├── slideOutRight.ts │ ├── slideOutUp.ts │ ├── swing.ts │ ├── tada.ts │ ├── wobble.ts │ ├── zoomIn.ts │ ├── zoomInDown.ts │ ├── zoomInLeft.ts │ ├── zoomInRight.ts │ ├── zoomInUp.ts │ ├── zoomOut.ts │ ├── zoomOutDown.ts │ ├── zoomOutLeft.ts │ ├── zoomOutRight.ts │ └── zoomOutUp.ts ├── index.ts ├── use-web-animations.d.ts ├── useLatest.ts └── useWebAnimations.ts ├── tsconfig.json └── yarn.lock /.all-contributorsrc: -------------------------------------------------------------------------------- 1 | { 2 | "files": [ 3 | "README.md" 4 | ], 5 | "imageSize": 100, 6 | "commit": false, 7 | "contributors": [ 8 | { 9 | "login": "wellyshen", 10 | "name": "Welly", 11 | "avatar_url": "https://avatars1.githubusercontent.com/u/21308003?v=4", 12 | "profile": "https://wellyshen.com", 13 | "contributions": [ 14 | "code", 15 | "doc", 16 | "maintenance" 17 | ] 18 | } 19 | ], 20 | "contributorsPerLine": 7, 21 | "projectName": "use-web-animations", 22 | "projectOwner": "wellyshen", 23 | "repoType": "github", 24 | "repoHost": "https://github.com", 25 | "skipCi": true 26 | } 27 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | app 2 | dist -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ["welly"], 3 | rules: { 4 | "no-console": [ 5 | "warn", 6 | { 7 | allow: ["warn", "error"], 8 | }, 9 | ], 10 | "react/react-in-jsx-scope": "off", 11 | }, 12 | }; 13 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: use-web-animations 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 13 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "\U0001F41B Bug Report" 3 | about: Create a report to help us improve 4 | title: "" 5 | labels: "" 6 | assignees: "" 7 | --- 8 | 9 | # Bug Report 10 | 11 | ## Describe the Bug 12 | 13 | A clear and concise description of what the bug is. 14 | 15 | ## How to Reproduce 16 | 17 | Steps to reproduce the behavior, please provide code snippets or a repository: 18 | 19 | 1. Go to '....' 20 | 2. Click on '....' 21 | 3. See error 22 | 23 | ## CodeSandbox Link 24 | 25 | Show me the bug on [CodeSandbox](https://codesandbox.io). 26 | 27 | ## Expected Behavior 28 | 29 | Tell me what should happen. 30 | 31 | ## Screenshots 32 | 33 | Add screenshots to help explain your problem. 34 | 35 | ## Your Environment 36 | 37 | - Device: [e.g. MacBook Pro, iPhone12] 38 | - OS: [e.g. macOS, iOS, Windows] 39 | - Browser: [e.g. Chrome, Safari] 40 | - Version: [e.g. v1.0.0] 41 | 42 | ## Additional Information 43 | 44 | Any other information about the problem here. 45 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "\U0001F4A1 Feature Request" 3 | about: Suggest an idea for this project 4 | title: "" 5 | labels: "" 6 | assignees: "" 7 | --- 8 | 9 | # Feature Request 10 | 11 | ## Describe the Feature 12 | 13 | A clear and concise description of what you want and what your use case is. 14 | 15 | ## Describe the Solution You'd Like 16 | 17 | A clear and concise description of what you want to happen. 18 | 19 | ## Describe Alternatives You've Considered 20 | 21 | A clear and concise description of any alternative solutions or features you've considered. 22 | 23 | ## Additional Information 24 | 25 | Any other information about the feature here. 26 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/question.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "\U0001F914 Questions and Help" 3 | about: This issue tracker is not for questions. Please ask questions at https://stackoverflow.com/questions/tagged/react. 4 | title: "" 5 | labels: "" 6 | assignees: "" 7 | --- 8 | 9 | GitHub Issues are reserved for Bug reports and Feature requests. Support requests that are created as issues are likely to be closed. We want to make sure you are able to find the help you seek. Please take a look at the following resources. 10 | 11 | ## Coding Questions 12 | 13 | If you have a coding question related to React, it might be better suited for Stack Overflow. It's a great place to browse through frequent questions about using React, as well as ask for help with specific questions. 14 | 15 | https://stackoverflow.com/questions/tagged/react 16 | 17 | ## Support Forums 18 | 19 | There are many online forums which are a great place for discussion about best practices and application architecture. 20 | 21 | https://reactjs.org/community/support.html#popular-discussion-forums 22 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 12 | 13 | ## What 14 | 15 | What changes are being made? (e.g. feature, bug, docs, etc.) 16 | 17 | ## Why 18 | 19 | Why are these changes necessary? 20 | 21 | ## How 22 | 23 | How were these changes implemented? 24 | 25 | ## Checklist 26 | 27 | Have you done all of these things? 28 | 29 | 30 | 31 | 32 | - [ ] Documentation added 33 | - [ ] Tests 34 | - [ ] TypeScript definitions updated 35 | - [ ] Ready to be merged 36 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: npm 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | time: "21:00" 8 | open-pull-requests-limit: 10 9 | ignore: 10 | - dependency-name: husky 11 | versions: 12 | - 5.0.9 13 | - 5.1.1 14 | - 6.0.0 15 | - dependency-name: "@rollup/plugin-commonjs" 16 | versions: 17 | - 18.0.0 18 | - dependency-name: stylelint-config-standard 19 | versions: 20 | - 21.0.0 21 | - dependency-name: "@babel/runtime" 22 | versions: 23 | - 7.13.8 24 | - dependency-name: "@testing-library/react-hooks" 25 | versions: 26 | - 5.0.3 27 | - package-ecosystem: github-actions 28 | directory: "/" 29 | schedule: 30 | interval: daily 31 | time: "21:00" 32 | open-pull-requests-limit: 10 33 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | tags-ignore: 8 | - "**" 9 | pull_request: 10 | branches: 11 | - master 12 | 13 | jobs: 14 | build: 15 | runs-on: ubuntu-latest 16 | 17 | strategy: 18 | matrix: 19 | node: [16] 20 | 21 | steps: 22 | - uses: actions/checkout@v3 23 | - name: Use Node.js ${{ matrix.node-version }} 24 | uses: actions/setup-node@v3 25 | with: 26 | node-version: ${{ matrix.node-version }} 27 | 28 | - name: Get yarn cache directory path 29 | id: yarn-cache-dir-path 30 | run: echo "::set-output name=dir::$(yarn cache dir)" 31 | - uses: actions/cache@v3 32 | id: yarn-cache 33 | with: 34 | path: ${{ steps.yarn-cache-dir-path.outputs.dir }} 35 | key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} 36 | restore-keys: | 37 | ${{ runner.os }}-yarn- 38 | - name: Install dependencies 39 | run: yarn install --frozen-lockfile 40 | - name: Run lint 41 | run: yarn lint 42 | - name: Run test 43 | run: yarn test:cov 44 | - name: Run build package 45 | run: yarn build:prod 46 | 47 | - name: Coveralls GitHub Action 48 | uses: coverallsapp/github-action@master 49 | with: 50 | github-token: ${{ secrets.GITHUB_TOKEN }} 51 | 52 | deploy: 53 | runs-on: ubuntu-latest 54 | needs: build 55 | if: github.ref == 'refs/heads/master' 56 | 57 | steps: 58 | - uses: actions/checkout@v3 59 | 60 | - name: Get yarn cache directory path 61 | id: yarn-cache-dir-path 62 | run: echo "::set-output name=dir::$(yarn cache dir)" 63 | - uses: actions/cache@v3 64 | id: yarn-cache 65 | with: 66 | path: ${{ steps.yarn-cache-dir-path.outputs.dir }} 67 | key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} 68 | restore-keys: | 69 | ${{ runner.os }}-yarn- 70 | - name: Install dependencies 71 | run: yarn install --frozen-lockfile 72 | - name: Run build demo app 73 | run: yarn build:demo 74 | 75 | - name: Netlify GitHub Action 76 | uses: netlify/actions/cli@master 77 | with: 78 | args: deploy --prod --dir=app/build 79 | env: 80 | NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }} 81 | NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }} 82 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | name: "CodeQL" 2 | 3 | on: 4 | push: 5 | branches: [master] 6 | pull_request: 7 | branches: [master] 8 | schedule: 9 | # ┌───────────── minute (0 - 59) 10 | # │ ┌───────────── hour (0 - 23) 11 | # │ │ ┌───────────── day of the month (1 - 31) 12 | # │ │ │ ┌───────────── month (1 - 12 or JAN-DEC) 13 | # │ │ │ │ ┌───────────── day of the week (0 - 6 or SUN-SAT) 14 | # │ │ │ │ │ 15 | # │ │ │ │ │ 16 | # │ │ │ │ │ 17 | # * * * * * 18 | - cron: "40 18 * * 1" 19 | 20 | jobs: 21 | CodeQL-Build: 22 | # CodeQL runs on ubuntu-latest, windows-latest, and macos-latest 23 | runs-on: ubuntu-latest 24 | 25 | permissions: 26 | # required for all workflows 27 | security-events: write 28 | 29 | # only required for workflows in private repositories 30 | actions: read 31 | contents: read 32 | 33 | steps: 34 | - name: Checkout repository 35 | uses: actions/checkout@v3 36 | 37 | # Initializes the CodeQL tools for scanning. 38 | - name: Initialize CodeQL 39 | uses: github/codeql-action/init@v2 40 | # Override language selection by uncommenting this and choosing your languages 41 | with: 42 | languages: javascript 43 | 44 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 45 | # If this step fails, then you should remove it and run the build manually (see below). 46 | - name: Autobuild 47 | uses: github/codeql-action/autobuild@v2 48 | 49 | # ℹ️ Command-line programs to run using the OS shell. 50 | # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun 51 | 52 | # ✏️ If the Autobuild fails above, remove it and uncomment the following 53 | # three lines and modify them (or add more) to build your code if your 54 | # project uses a compiled language 55 | 56 | #- run: | 57 | # make bootstrap 58 | # make release 59 | 60 | - name: Perform CodeQL Analysis 61 | uses: github/codeql-action/analyze@v2 62 | -------------------------------------------------------------------------------- /.github/workflows/size.yml: -------------------------------------------------------------------------------- 1 | name: Compressed Size 2 | 3 | on: [pull_request] 4 | 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | 9 | steps: 10 | - uses: actions/checkout@v3 11 | - uses: preactjs/compressed-size-action@v2 12 | with: 13 | repo-token: "${{ secrets.GITHUB_TOKEN }}" 14 | build-script: "build:prod" 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | *.log 3 | 4 | node_modules 5 | dist 6 | coverage 7 | .size-snapshot.json -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | yarn lint-staged 5 | -------------------------------------------------------------------------------- /.husky/pre-push: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | yarn test 5 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | lts/* 2 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | *.log 2 | app 3 | dist 4 | coverage 5 | .size-snapshot.json -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "typescript.tsdk": "node_modules/typescript/lib" 3 | } 4 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | This project adheres to [Semantic Versioning](http://semver.org). 4 | Every release, along with the migration instructions, is documented on the Github [Releases](https://github.com/wellyshen/use-web-animations/releases) page. 5 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our 6 | community a harassment-free experience for everyone, regardless of age, body 7 | size, visible or invisible disability, ethnicity, sex characteristics, gender 8 | identity and expression, level of experience, education, socio-economic status, 9 | nationality, personal appearance, race, religion, or sexual identity 10 | and orientation. 11 | 12 | We pledge to act and interact in ways that contribute to an open, welcoming, 13 | diverse, inclusive, and healthy community. 14 | 15 | ## Our Standards 16 | 17 | Examples of behavior that contributes to a positive environment for our 18 | community include: 19 | 20 | - Demonstrating empathy and kindness toward other people 21 | - Being respectful of differing opinions, viewpoints, and experiences 22 | - Giving and gracefully accepting constructive feedback 23 | - Accepting responsibility and apologizing to those affected by our mistakes, 24 | and learning from the experience 25 | - Focusing on what is best not just for us as individuals, but for the 26 | overall community 27 | 28 | Examples of unacceptable behavior include: 29 | 30 | - The use of sexualized language or imagery, and sexual attention or 31 | advances of any kind 32 | - Trolling, insulting or derogatory comments, and personal or political attacks 33 | - Public or private harassment 34 | - Publishing others' private information, such as a physical or email 35 | address, without their explicit permission 36 | - Other conduct which could reasonably be considered inappropriate in a 37 | professional setting 38 | 39 | ## Enforcement Responsibilities 40 | 41 | Community leaders are responsible for clarifying and enforcing our standards of 42 | acceptable behavior and will take appropriate and fair corrective action in 43 | response to any behavior that they deem inappropriate, threatening, offensive, 44 | or harmful. 45 | 46 | Community leaders have the right and responsibility to remove, edit, or reject 47 | comments, commits, code, wiki edits, issues, and other contributions that are 48 | not aligned to this Code of Conduct, and will communicate reasons for moderation 49 | decisions when appropriate. 50 | 51 | ## Scope 52 | 53 | This Code of Conduct applies within all community spaces, and also applies when 54 | an individual is officially representing the community in public spaces. 55 | Examples of representing our community include using an official e-mail address, 56 | posting via an official social media account, or acting as an appointed 57 | representative at an online or offline event. 58 | 59 | ## Enforcement 60 | 61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 62 | reported to the community leaders responsible for enforcement at 63 | hivoid19@gmail.com. 64 | All complaints will be reviewed and investigated promptly and fairly. 65 | 66 | All community leaders are obligated to respect the privacy and security of the 67 | reporter of any incident. 68 | 69 | ## Enforcement Guidelines 70 | 71 | Community leaders will follow these Community Impact Guidelines in determining 72 | the consequences for any action they deem in violation of this Code of Conduct: 73 | 74 | ### 1. Correction 75 | 76 | **Community Impact**: Use of inappropriate language or other behavior deemed 77 | unprofessional or unwelcome in the community. 78 | 79 | **Consequence**: A private, written warning from community leaders, providing 80 | clarity around the nature of the violation and an explanation of why the 81 | behavior was inappropriate. A public apology may be requested. 82 | 83 | ### 2. Warning 84 | 85 | **Community Impact**: A violation through a single incident or series 86 | of actions. 87 | 88 | **Consequence**: A warning with consequences for continued behavior. No 89 | interaction with the people involved, including unsolicited interaction with 90 | those enforcing the Code of Conduct, for a specified period of time. This 91 | includes avoiding interactions in community spaces as well as external channels 92 | like social media. Violating these terms may lead to a temporary or 93 | permanent ban. 94 | 95 | ### 3. Temporary Ban 96 | 97 | **Community Impact**: A serious violation of community standards, including 98 | sustained inappropriate behavior. 99 | 100 | **Consequence**: A temporary ban from any sort of interaction or public 101 | communication with the community for a specified period of time. No public or 102 | private interaction with the people involved, including unsolicited interaction 103 | with those enforcing the Code of Conduct, is allowed during this period. 104 | Violating these terms may lead to a permanent ban. 105 | 106 | ### 4. Permanent Ban 107 | 108 | **Community Impact**: Demonstrating a pattern of violation of community 109 | standards, including sustained inappropriate behavior, harassment of an 110 | individual, or aggression toward or disparagement of classes of individuals. 111 | 112 | **Consequence**: A permanent ban from any sort of public interaction within 113 | the community. 114 | 115 | ## Attribution 116 | 117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 118 | version 2.0, available at 119 | [https://www.contributor-covenant.org/version/2/0/code_of_conduct.html][v2.0]. 120 | 121 | Community Impact Guidelines were inspired by 122 | [Mozilla's code of conduct enforcement ladder][mozilla coc]. 123 | 124 | For answers to common questions about this code of conduct, see the FAQ at 125 | [https://www.contributor-covenant.org/faq][faq]. Translations are available 126 | at [https://www.contributor-covenant.org/translations][translations]. 127 | 128 | [homepage]: https://www.contributor-covenant.org 129 | [v2.0]: https://www.contributor-covenant.org/version/2/0/code_of_conduct.html 130 | [mozilla coc]: https://github.com/mozilla/diversity 131 | [faq]: https://www.contributor-covenant.org/faq 132 | [translations]: https://www.contributor-covenant.org/translations 133 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to USE-WEB-ANIMATIONS 2 | 3 | When contributing to this repository, please first discuss the change you wish to make via issue, email, or any other method with the owners of this repository before making a change. 4 | 5 | Please note we have a [code of conduct](CODE_OF_CONDUCT.md), please follow it in all your interactions with the project. 6 | 7 | > Working on your first Pull Request? You can learn how from [this free video series](https://egghead.io/courses/how-to-contribute-to-an-open-source-project-on-github). 8 | 9 | ## Pull Request Process 10 | 11 | 1. Fork the repository and create your branch from `master`. 12 | 2. Run `yarn` to install dependencies. 13 | 3. If you’ve fixed a bug or added code that should be tested. 14 | 4. Ensure the test suite passes by running `yarn test`. 15 | 5. Update the [README.md](README.md) with details of changes to the interface. 16 | 6. Update the [demo app](app/src) if needed. 17 | 7. Make sure your code lints by running `yarn lint`. 18 | 19 | ## Development Workflow 20 | 21 | You can test new features or debug an issue by the way that I'm using. 22 | 23 | 1. Run `yarn link-pkg` to link the package into the [app directory](app). 24 | 2. Run `yarn start` to create an `ESM` build and type definition file by `rollup` watch mode. 25 | 3. Access the [app directory](app). 26 | 4. In the **app directory**, run `yarn link-pkg` to link with the package then run `yarn start` to start development. 27 | 5. Try something cool via the [demo app](app/src). 28 | 29 | ## Scripts 30 | 31 | There're several useful scripts that you can use during the development: 32 | 33 | - `yarn link-pkg` links the package into the [app directory](app). You can develop or debug it via the [demo app](app/src). 34 | - `yarn start` creates a `dist` folder with an `ESM` build and type definition file by `rollup` watch mode. 35 | - `yarn lint:code` lints all `.js` and `.tsx?` files. 36 | - `yarn lint:type` runs the [TypeScript](https://www.typescriptlang.org) type-checks. 37 | - `yarn lint:format` formats all files except the file list of `.prettierignore`. 38 | - `yarn lint` lints `code`, `type`, and `format`. 39 | - `yarn test` runs the complete test suite. 40 | - `yarn test:watch` runs an interactive test watcher (helpful in development). 41 | - `yarn test:cov` runs the complete test suite with coverage report. 42 | - `yarn build:dev` creates a `dist` folder with an `ESM` build and type definition file for development. 43 | - `yarn build:prod` creates a `dist` folder with package builds (`CJS` & `ESM`) and type definition file. You can test the package locally via [yarn link](https://yarnpkg.com/lang/en/docs/cli/link). 44 | - `yarn clean:dist` deletes the `dist` build folder. 45 | - `yarn clean:size` deletes the `.size-snapshot.json` file. 46 | - `yarn clean:cov` deletes the `coverage` report folder. 47 | - `yarn clean` deletes build, test, and size relevant files. 48 | 49 | ## Style Guide 50 | 51 | We use [ESLint](https://eslint.org), [StyleLint](https://stylelint.io) and [Prettier](https://prettier.io) for code style and formatting. Run `yarn lint` after making any changes to the code. Then, our linter will catch most issues that may exist in your code. 52 | 53 | However, there are still some styles that the linter cannot pick up. If you are unsure about something, looking at [Airbnb’s Style Guide](https://github.com/airbnb/javascript) will guide you in the right direction. 54 | 55 | ## License 56 | 57 | By contributing to USE-WEB-ANIMATIONS, you agree that your contributions will be licensed under its MIT license. 58 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2020 Welly Shen 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # USE-WEB-ANIMATIONS 2 | 3 | Using [Web Animations API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Animations_API) (a.k.a WAAPI) in the React [hook](https://reactjs.org/docs/hooks-custom.html#using-a-custom-hook) way. Let's create highly-performant, flexible and manipulable web animations in the modern world. Hope you guys 👍🏻 it! 4 | 5 | ❤️ it? ⭐️ it on [GitHub](https://github.com/wellyshen/use-web-animations/stargazers) or [Tweet](https://twitter.com/intent/tweet?text=With%20@wellyshen/use-web-animations,%20I%20can%20build%20fancy%20and%20performant%20animations%20for%20my%20web%20app.%20Thanks,%20@Welly%20Shen%20🤩) about it. 6 | 7 | [![build status](https://img.shields.io/github/workflow/status/wellyshen/use-web-animations/CI?style=flat-square)](https://github.com/wellyshen/use-web-animations/actions?query=workflow%3ACI) 8 | [![coverage status](https://img.shields.io/coveralls/github/wellyshen/use-web-animations?style=flat-square)](https://coveralls.io/github/wellyshen/use-web-animations?branch=master) 9 | [![npm version](https://img.shields.io/npm/v/@wellyshen/use-web-animations?style=flat-square)](https://www.npmjs.com/package/@wellyshen/use-web-animations) 10 | [![npm downloads](https://img.shields.io/npm/dm/@wellyshen/use-web-animations?style=flat-square)](https://www.npmtrends.com/@wellyshen/use-web-animations) 11 | [![npm downloads](https://img.shields.io/npm/dt/@wellyshen/use-web-animations?style=flat-square)](https://www.npmtrends.com/@wellyshen/use-web-animations) 12 | [![gzip size](https://badgen.net/bundlephobia/minzip/@wellyshen/use-web-animations?label=gzip%20size&style=flat-square)](https://bundlephobia.com/result?p=@wellyshen/use-web-animations) 13 | [![All Contributors](https://img.shields.io/badge/all_contributors-1-orange.svg?style=flat-square)](#contributors-) 14 | [![PRs welcome](https://img.shields.io/badge/PRs-welcome-brightgreen?style=flat-square)](CONTRIBUTING.md) 15 | [![Twitter URL](https://img.shields.io/twitter/url?style=social&url=https%3A%2F%2Fgithub.com%2Fwellyshen%2Fuse-web-animations)](https://twitter.com/intent/tweet?text=With%20@wellyshen/use-web-animations,%20I%20can%20build%20fancy%20and%20performant%20animations%20for%20my%20web%20app.%20Thanks,%20@Welly%20Shen%20🤩) 16 | 17 | ![demo_basic](https://user-images.githubusercontent.com/21308003/91186058-280b7d00-e721-11ea-8180-e739d14c7ec7.gif) 18 | 19 | ⚡️ Try yourself: https://use-web-animations.netlify.app 20 | 21 | ![demo_animate](https://user-images.githubusercontent.com/21308003/91186066-2a6dd700-e721-11ea-84c5-b2c4ff984cf5.gif) 22 | 23 | ⚡️ Try yourself: https://use-web-animations.netlify.app#animations 24 | 25 | ## Features 26 | 27 | - 🚀 Animate on the Web with [highly-performant](https://web.dev/animations-overview/#css-js) and manipulable way, using [Web Animations API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Animations_API). 28 | - 🎣 Easy to use, based on React [hook](https://reactjs.org/docs/hooks-custom.html#using-a-custom-hook). 29 | - 🎛 Super flexible [API](#api) design that can cover [all the cases](#usage) that you need. 30 | - 🎞 [Built-ins animations](#use-built-in-animations) for you, based on [Animate.css](https://animate.style). 31 | - 🔩 Supports custom `refs` for [some reasons](#use-your-own-ref). 32 | - 📜 Supports [TypeScript](https://www.typescriptlang.org) type definition. 33 | - 🗄️ Server-side rendering compatibility. 34 | - 🦔 Tiny size ([~ 4.4kB gzipped](https://bundlephobia.com/result?p=@wellyshen/use-web-animations)). No external dependencies, aside for the `react`. 35 | 36 | ## Requirement 37 | 38 | To use `use-web-animations`, you must use `react@16.8.0` or greater which includes hooks. 39 | 40 | ## Installation 41 | 42 | This package is distributed via [npm](https://www.npmjs.com/package/@wellyshen/use-web-animations). 43 | 44 | ```sh 45 | $ yarn add @wellyshen/use-web-animations 46 | # or 47 | $ npm install --save @wellyshen/use-web-animations 48 | ``` 49 | 50 | ## Before We Start 51 | 52 | With the Web Animations API, we can move interactive animations from stylesheets to JavaScript, separating presentation from behavior. The API was designed based on the concept of the [CSS Animations](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Animations/Using_CSS_animations) but there're still some differences between them. I strongly recommend you to read the [documentation](https://developer.mozilla.org/en-US/docs/Web/API/Web_Animations_API/Using_the_Web_Animations_API) before we dive into this hook. 53 | 54 | ## Usage 55 | 56 | The [API](#api) design of the hook not only inherits the DX of the [Web Animations API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Animations_API) but also provides useful features and sugar events to us. Here are some examples to show you how does it work. 57 | 58 | > ⚠️ [Most modern browsers support Web Animations API natively](https://caniuse.com/web-animation). You can also use [polyfill](#use-polyfill) for full browser support. 59 | 60 | ### Basic Usage 61 | 62 | Create an animation by the `keyframes` and `animationOptions` options (these are the [parameters](https://developer.mozilla.org/en-US/docs/Web/API/Element/animate#parameters) of the `Element.animate()`). 63 | 64 | > 💡 This hook supports the [pseudoElement](https://css-tricks.com/pseudo-elements-in-the-web-animations-api/) property via the `animationOptions` option. 65 | 66 | [![Edit useWebAnimations - basic](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/usewebanimations-basic-nf0kd?fontsize=14&hidenavigation=1&theme=dark) 67 | 68 | ```js 69 | import useWebAnimations from "@wellyshen/use-web-animations"; 70 | 71 | const App = () => { 72 | const { ref, playState } = useWebAnimations({ 73 | keyframes: { 74 | transform: "translateX(500px)", // Move by 500px 75 | background: ["red", "blue", "green"], // Go through three colors 76 | }, 77 | animationOptions: { 78 | delay: 500, // Start with a 500ms delay 79 | duration: 1000, // Run for 1000ms 80 | iterations: 2, // Repeat once 81 | direction: "alternate", // Run the animation forwards and then backwards 82 | easing: "ease-in-out", // Use a fancy timing function 83 | }, 84 | onReady: ({ playState, animate, animation }) => { 85 | // Triggered when the animation is ready to play 86 | }, 87 | onUpdate: ({ playState, animate, animation }) => { 88 | // Triggered when the animation enters the running state or changes state 89 | }, 90 | onFinish: ({ playState, animate, animation }) => { 91 | // Triggered when the animation enters the finished state 92 | }, 93 | // More useful options... 94 | }); 95 | 96 | return ( 97 |
98 |

🍿 Animation is {playState}

99 |
100 |
101 | ); 102 | }; 103 | ``` 104 | 105 | For browsers that don't yet support the `onReady` and `onFinish` events, we can use the `onUpdate` to monitor the animation's state instead. 106 | 107 | ```js 108 | let prevPending = true; 109 | 110 | const App = () => { 111 | const { ref } = useWebAnimations({ 112 | // ... 113 | onUpdate: ({ playState, animation: { pending } }) => { 114 | if (prevPending && !pending) { 115 | console.log("Animation is ready to play"); 116 | } 117 | prevPending = pending; 118 | 119 | if (playState === "finished") { 120 | console.log("Animation has finished playing"); 121 | } 122 | }, 123 | }); 124 | 125 | // ... 126 | }; 127 | ``` 128 | 129 | ### Setting/Updating Animation 130 | 131 | The `keyframes` and `animationOptions` are cached when the hook is mounted. However, we can set/update the animation by the `animation` method. 132 | 133 | ```js 134 | const { animation } = useWebAnimations(); 135 | 136 | const changeAnim = () => 137 | animation({ 138 | keyframes: { transform: ["translateX(0)", "translateX(100px)"] }, 139 | animationOptions: 1000, 140 | id: "123", 141 | playbackRate: 1, 142 | autoPlay: true, 143 | }); 144 | ``` 145 | 146 | ### Playback Control 147 | 148 | The shortcoming with existing technologies was the lack of playback control. The Web Animations API provides several useful methods for controlling playback: play, pause, reverse, cancel, finish, seek, control speed via the [methods](https://developer.mozilla.org/en-US/docs/Web/API/Animation#Methods) of the **Animation** interface. This hook exposes the animation instance for us to interact with animations, we can access it by the `getAnimation()` return value. 149 | 150 | [![Edit useWebAnimations - controls](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/usewebanimations-controls-hst8v?fontsize=14&hidenavigation=1&theme=dark) 151 | 152 | ```js 153 | import useWebAnimations from "@wellyshen/use-web-animations"; 154 | 155 | const App = () => { 156 | const { ref, playState, getAnimation } = useWebAnimations({ 157 | playbackRate: 0.5, // Change playback rate, default is 1 158 | autoPlay: false, // Automatically starts the animation, default is true 159 | keyframes: { transform: "translateX(500px)" }, 160 | animationOptions: { duration: 1000, fill: "forwards" }, 161 | }); 162 | 163 | const play = () => { 164 | getAnimation().play(); 165 | }; 166 | 167 | const pause = () => { 168 | getAnimation().pause(); 169 | }; 170 | 171 | const reverse = () => { 172 | getAnimation().reverse(); 173 | }; 174 | 175 | const cancel = () => { 176 | getAnimation().cancel(); 177 | }; 178 | 179 | const finish = () => { 180 | getAnimation().finish(); 181 | }; 182 | 183 | const seek = (e) => { 184 | const animation = getAnimation(); 185 | const time = (animation.effect.getTiming().duration / 100) * e.target.value; 186 | animation.currentTime = time; 187 | }; 188 | 189 | const updatePlaybackRate = (e) => { 190 | getAnimation().updatePlaybackRate(e.target.value); 191 | }; 192 | 193 | return ( 194 |
195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 |
203 |
204 | ); 205 | }; 206 | ``` 207 | 208 | ### Getting Animation's Information 209 | 210 | When using the Web Animations API, we can get the information of an animation via the [properties](https://developer.mozilla.org/en-US/docs/Web/API/Animation#Properties) of the **Animation** interface. However, we can get the information of an animation by the `getAnimation()` return value as well. 211 | 212 | ```js 213 | import useWebAnimations from "@wellyshen/use-web-animations"; 214 | 215 | const App = () => { 216 | const { ref, getAnimation } = useWebAnimations({ 217 | keyframes: { transform: "translateX(500px)" }, 218 | animationOptions: { duration: 1000, fill: "forwards" }, 219 | }); 220 | 221 | const speedUp = () => { 222 | const animation = getAnimation(); 223 | animation.updatePlaybackRate(animation.playbackRate * 0.25); 224 | }; 225 | 226 | const jumpToHalf = () => { 227 | const animation = getAnimation(); 228 | animation.currentTime = animation.effect.getTiming().duration / 2; 229 | }; 230 | 231 | return ( 232 |
233 | 234 | 235 |
236 |
237 | ); 238 | }; 239 | ``` 240 | 241 | The animation instance isn't a part of [React state](https://reactjs.org/docs/hooks-state.html), which means we need to access it by the `getAnimation()` whenever we need. If you want to monitor an animation's information, here's the `onUpdate` event for you. The event is implemented by the [requestAnimationFrame](https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame) internally and the event callback is triggered when the animation enters `running` state or changes state. 242 | 243 | ```js 244 | import { useState } from "react"; 245 | import useWebAnimations from "@wellyshen/use-web-animations"; 246 | 247 | const App = () => { 248 | const [showEl, setShowEl] = useState(false); 249 | const { ref } = useWebAnimations({ 250 | keyframes: { transform: "translateX(500px)" }, 251 | animationOptions: { duration: 1000, fill: "forwards" }, 252 | onUpdate: ({ animation }) => { 253 | if (animation.currentTime > animation.effect.getTiming().duration / 2) 254 | setShowEl(true); 255 | }, 256 | }); 257 | 258 | return ( 259 |
260 | {showEl &&
} 261 |
262 |
263 | ); 264 | }; 265 | ``` 266 | 267 | ### Dynamic Interactions with Animation 268 | 269 | We can create and play an animation at the `animationOptions` we want by the `animate` method, which is implemented based on the [Element.animate()](https://developer.mozilla.org/en-US/docs/Web/API/Element/animate). It's useful for interactions and the [composite modes](https://css-tricks.com/additive-animation-web-animations-api). 270 | 271 | Let's create a mouse interaction effect: 272 | 273 | [![Edit useWebAnimations - interaction](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/usewebanimations-interaction-4jrs9?fontsize=14&hidenavigation=1&theme=dark) 274 | 275 | ```js 276 | import { useEffect } from "react"; 277 | import useWebAnimations from "@wellyshen/use-web-animations"; 278 | 279 | const App = () => { 280 | const { ref, animate } = useWebAnimations(); 281 | 282 | useEffect(() => { 283 | document.addEventListener("mousemove", (e) => { 284 | // The target will follow the mouse cursor 285 | animate({ 286 | keyframes: { transform: `translate(${e.clientX}px, ${e.clientY}px)` }, 287 | animationOptions: { duration: 500, fill: "forwards" }, 288 | }); 289 | }); 290 | }, [animate]); 291 | 292 | return ( 293 |
294 |
295 |
296 | ); 297 | }; 298 | ``` 299 | 300 | Create a bounce effect via lifecycle and composite mode: 301 | 302 | ```js 303 | import useWebAnimations from "@wellyshen/use-web-animations"; 304 | 305 | const App = () => { 306 | const { ref, animate } = useWebAnimations({ 307 | id: "fall", // Set animation id, default is empty string 308 | keyframes: [{ top: 0, easing: "ease-in" }, { top: "500px" }], 309 | animationOptions: { duration: 300, fill: "forwards" }, 310 | onFinish: ({ animate, animation }) => { 311 | // Lifecycle is triggered by each animation, we can check the id to prevent animation from repeating 312 | if (animation.id === "bounce") return; 313 | 314 | animate({ 315 | id: "bounce", 316 | keyframes: [ 317 | { top: "500px", easing: "ease-in" }, 318 | { top: "10px", easing: "ease-out" }, 319 | ], 320 | animationOptions: { duration: 300, composite: "add" }, 321 | }); 322 | }, 323 | }); 324 | 325 | return ( 326 |
327 |
328 |
329 | ); 330 | }; 331 | ``` 332 | 333 | > ⚠️ Composite modes isn't fully supported by all the browsers, please check the [browser compatibility](https://developer.mozilla.org/en-US/docs/Web/API/KeyframeEffect/composite#Browser_compatibility) carefully before using it. 334 | 335 | ## Use Built-in Animations 336 | 337 | Too lazy to think about animation? We provide a collection of ready-to-use animations for you, they are implemented based on [Animate.css](https://animate.style). 338 | 339 | 👉🏻 [Check out the demo](https://use-web-animations.netlify.app#animations). 340 | 341 | ```js 342 | import useWebAnimations, { bounce } from "@wellyshen/use-web-animations"; 343 | 344 | const App = () => { 345 | // Add a pre-defined effect to the target 346 | const { ref } = useWebAnimations({ ...bounce }); 347 | 348 | return ( 349 |
350 |
351 |
352 | ); 353 | }; 354 | ``` 355 | 356 | We can customize the built-in animation by overriding its properties: 357 | 358 | ```js 359 | const { keyframes, animationOptions } = bounce; 360 | const { ref } = useWebAnimations({ 361 | keyframes, 362 | animationOptions: { 363 | ...animationOptions, 364 | delay: 1000, // Delay 1s 365 | duration: animationOptions.duration * 0.75, // Speed up the animation 366 | }, 367 | }); 368 | ``` 369 | 370 |
371 | See all available animations 372 | 373 | #### Attention seekers 374 | 375 | - bounce 376 | - flash 377 | - pulse 378 | - rubberBand 379 | - shakeX 380 | - shakeY 381 | - headShake 382 | - swing 383 | - tada 384 | - wobble 385 | - jello 386 | - heartBeat 387 | 388 | #### Back entrances 389 | 390 | - backInDown 391 | - backInLeft 392 | - backInRight 393 | - backInUp 394 | 395 | #### Back exits 396 | 397 | - backOutDown 398 | - backOutLeft 399 | - backOutRight 400 | - backOutUp 401 | 402 | #### Bouncing entrances 403 | 404 | - bounceIn 405 | - bounceInDown 406 | - bounceInLeft 407 | - bounceInRight 408 | - bounceInUp 409 | 410 | #### Bouncing exits 411 | 412 | - bounceOut 413 | - bounceOutDown 414 | - bounceOutLeft 415 | - bounceOutRight 416 | - bounceOutUp 417 | 418 | #### Fading entrances 419 | 420 | - fadeIn 421 | - fadeInDown 422 | - fadeInDownBig 423 | - fadeInLeft 424 | - fadeInLeftBig 425 | - fadeInRight 426 | - fadeInRightBig 427 | - fadeInUp 428 | - fadeInUpBig 429 | - fadeInTopLeft 430 | - fadeInTopRight 431 | - fadeInBottomLeft 432 | - fadeInBottomRight 433 | 434 | #### Fading exits 435 | 436 | - fadeOut 437 | - fadeOutDown 438 | - fadeOutDownBig 439 | - fadeOutLeft 440 | - fadeOutLeftBig 441 | - fadeOutRight 442 | - fadeOutRightBig 443 | - fadeOutUp 444 | - fadeOutUpBig 445 | - fadeOutTopLeft 446 | - fadeOutTopRight 447 | - fadeOutBottomLeft 448 | - fadeOutBottomRight 449 | 450 | #### Flippers 451 | 452 | - flip 453 | - flipInX 454 | - flipInY 455 | - flipOutX 456 | - flipOutY 457 | 458 | #### Lightspeed 459 | 460 | - lightSpeedInRight 461 | - lightSpeedInLeft 462 | - lightSpeedOutRight 463 | - lightSpeedOutLeft 464 | 465 | #### Rotating entrances 466 | 467 | - rotateIn 468 | - rotateInDownLeft 469 | - rotateInDownRight 470 | - rotateInUpLeft 471 | - rotateInUpRight 472 | 473 | #### Rotating exits 474 | 475 | - rotateOut 476 | - rotateOutDownLeft 477 | - rotateOutDownRight 478 | - rotateOutUpLeft 479 | - rotateOutUpRight 480 | 481 | #### Specials 482 | 483 | - hinge 484 | - jackInTheBox 485 | - rollIn 486 | - rollOut 487 | 488 | #### Zooming entrances 489 | 490 | - zoomIn 491 | - zoomInDown 492 | - zoomInLeft 493 | - zoomInRight 494 | - zoomInUp 495 | 496 | #### Zooming exits 497 | 498 | - zoomOut 499 | - zoomOutDown 500 | - zoomOutLeft 501 | - zoomOutRight 502 | - zoomOutUp 503 | 504 | #### Sliding entrances 505 | 506 | - slideInDown 507 | - slideInLeft 508 | - slideInRight 509 | - slideInUp 510 | 511 | #### Sliding exits 512 | 513 | - slideOutDown 514 | - slideOutLeft 515 | - slideOutRight 516 | - slideOutUp 517 |
518 | 519 | ## Use Your Own `ref` 520 | 521 | In case of you had a ref already or you want to share a ref for other purposes. You can pass in the ref instead of using the one provided by this hook. 522 | 523 | ```js 524 | const ref = useRef(); 525 | const { playState } = useWebAnimations({ ref }); 526 | ``` 527 | 528 | ## Working in TypeScript 529 | 530 | This hook supports [TypeScript](https://www.typescriptlang.org), you can tell the hook what type of element you are going to animate through the [generic type](https://www.typescriptlang.org/docs/handbook/generics.html): 531 | 532 | ```ts 533 | const App = () => { 534 | const { ref } = useWebAnimations(); 535 | 536 | return
; 537 | }; 538 | ``` 539 | 540 | > 💡 For more available types, please [check it out](src/use-web-animations.d.ts). 541 | 542 | ## API 543 | 544 | ```js 545 | const returnObj = useWebAnimations(options?: object); 546 | ``` 547 | 548 | ### Return Object 549 | 550 | It's returned with the following properties. 551 | 552 | | Key | Type | Default | Description | 553 | | -------------- | ------------------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | 554 | | `ref` | object | | Used to set the target element for animating. | 555 | | `playState` | string \| undefined | | Describes the [playback state](https://developer.mozilla.org/en-US/docs/Web/API/Animation/playState#value) of an animation. | 556 | | `getAnimation` | function | | Access the [animation instance](https://developer.mozilla.org/en-US/docs/Web/API/Animation) for [playback control](#playback-control), [animation's information](#getting-animations-information) and more. | 557 | | `animate` | function | | Imperatively [set/update the animation](#settingupdating-animation). Useful for [interactive animations and composite animations](#dynamic-interactions-with-animation). | 558 | 559 | ### Parameter 560 | 561 | The `options` provides the following configurations and event callbacks for you. 562 | 563 | | Key | Type | Default | Description | 564 | | ------------------ | ---------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | 565 | | `ref` | object | | For [some reasons](#use-your-own-ref), you can pass in your own ref instead of using the built-in. | 566 | | `id` | string | `""` | Sets the ID of an animation, implemented based on the [Animation.id](https://developer.mozilla.org/en-US/docs/Web/API/Animation/id). | 567 | | `playbackRate` | number | `1` | Sets the playback rate of an animation, implemented based on the [Animation.playbackRate](https://developer.mozilla.org/en-US/docs/Web/API/Animation/playbackRate). | 568 | | `autoPlay` | boolean | `true` | Automatically starts the animation. | 569 | | `keyframes` | Array \| object | | An array of keyframe objects, or a keyframe object whose property are arrays of values to iterate over. See [basic usage](#basic-usage) for more details. | 570 | | `animationOptions` | number \| object | | An **integer** representing the animation's duration (in milliseconds), or an **object** containing one or more timing properties. See [basic usage](#basic-usage) for more details. | 571 | | `onReady` | function | | It's invoked when an animation is ready to play. You can access the [playState](#basic-usage), [animate](#dynamic-interactions-with-animation) and [animation](#getting-animations-information) from the event object. | 572 | | `onUpdate` | function | | It's invoked when an animation enters the `running` state or changes state. You can access the [playState](#basic-usage), [animate](#dynamic-interactions-with-animation) and [animation](#getting-animations-information) from the event object. | 573 | | `onFinish` | function | | It's invoked when an animation enters the `finished` state. You can access the [playState](#basic-usage), [animate](#dynamic-interactions-with-animation) and [animation](#getting-animations-information) from the event object. | 574 | 575 | ## Use Polyfill 576 | 577 | [Web Animations API has good support amongst browsers](https://caniuse.com/web-animation), but it's not universal. You'll need to polyfill browsers that don't support it. Polyfills is something you should do consciously at the application level. Therefore `use-web-animations` doesn't include it. 578 | 579 | Install [web-animations-js](https://github.com/web-animations/web-animations-js): 580 | 581 | ```sh 582 | $ yarn add web-animations-js 583 | # or 584 | $ npm install --save web-animations-js 585 | ``` 586 | 587 | Then import it at your app's entry point: 588 | 589 | ```js 590 | import "web-animations-js/web-animations.min"; 591 | ``` 592 | 593 | You can read the [document](https://github.com/web-animations/web-animations-js/blob/dev/docs/support.md) for more information. 594 | 595 | ## Articles / Blog Posts 596 | 597 | > 💡 If you have written any blog post or article about `use-web-animations`, please open a PR to add it here. 598 | 599 | - Featured on [React Status #196](https://react.statuscode.com/issues/196). 600 | - Featured on [JavaScript Weekly #496](https://javascriptweekly.com/issues/496). 601 | - Featured on [React Newsletter #218](https://reactnewsletter.com/issues/218). 602 | 603 | ## Contributors ✨ 604 | 605 | Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)): 606 | 607 | 608 | 609 | 610 | 611 | 612 | 613 | 614 |

Welly

💻 📖 🚧
615 | 616 | 617 | 618 | 619 | 620 | 621 | This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome! 622 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Supported Versions 4 | 5 | Use this section to tell people about which versions of your project are 6 | currently being supported with security updates. 7 | 8 | | Version | Supported | 9 | | ------- | ------------------ | 10 | | 5.1.x | :white_check_mark: | 11 | | 5.0.x | :x: | 12 | | 4.0.x | :white_check_mark: | 13 | | < 4.0 | :x: | 14 | 15 | ## Reporting a Vulnerability 16 | 17 | Use this section to tell people how to report a vulnerability. 18 | 19 | Tell them where to go, how often they can expect to get an update on a 20 | reported vulnerability, what to expect if the vulnerability is accepted or 21 | declined, etc. 22 | -------------------------------------------------------------------------------- /app/.eslintignore: -------------------------------------------------------------------------------- 1 | build -------------------------------------------------------------------------------- /app/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ["react-app", "welly"], 3 | rules: { 4 | "react/react-in-jsx-scope": "off", 5 | }, 6 | }; 7 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /app/.stylelintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ["stylelint-config-standard", "stylelint-config-prettier"], 3 | customSyntax: "postcss-scss", 4 | rules: { 5 | "at-rule-no-unknown": [ 6 | true, 7 | { 8 | ignoreAtRules: ["function", "if", "each", "include", "mixin"], 9 | }, 10 | ], 11 | }, 12 | ignoreFiles: ["build/**/*.css"], 13 | }; 14 | -------------------------------------------------------------------------------- /app/README.md: -------------------------------------------------------------------------------- 1 | # App 2 | 3 | 🍿 The demo app of USE-WEB-ANIMATIONS. 4 | -------------------------------------------------------------------------------- /app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "demo-app", 3 | "private": true, 4 | "scripts": { 5 | "link-pkg": "yarn link '@wellyshen/use-web-animations'", 6 | "start": "react-scripts start", 7 | "build": "react-scripts build", 8 | "lint": "run-s lint:*", 9 | "lint:code": "eslint --fix . --ext .js,.ts,.tsx", 10 | "lint:type": "tsc", 11 | "lint:style": "stylelint --fix \"**/*.{css,scss}\"", 12 | "lint:format": "prettier -w . -u --loglevel silent", 13 | "clean": "rimraf build", 14 | "eject": "react-scripts eject" 15 | }, 16 | "browserslist": { 17 | "production": [ 18 | ">0.2%", 19 | "not dead", 20 | "not op_mini all" 21 | ], 22 | "development": [ 23 | "last 1 chrome version", 24 | "last 1 firefox version", 25 | "last 1 safari version" 26 | ] 27 | }, 28 | "dependencies": { 29 | "@types/node": "^16.11.33", 30 | "@types/react": "^18.0.8", 31 | "@types/react-dom": "^18.0.3", 32 | "@wellyshen/use-web-animations": "latest", 33 | "normalize.css": "^8.0.1", 34 | "react": "^18.1.0", 35 | "react-dom": "^18.1.0", 36 | "react-scripts": "5.0.1", 37 | "sass": "^1.51.0", 38 | "typescript": "^4.6.4" 39 | }, 40 | "devDependencies": { 41 | "eslint-config-welly": "^1.13.0", 42 | "npm-run-all": "^4.1.5", 43 | "postcss-scss": "^4.0.4", 44 | "prettier": "^2.6.2", 45 | "rimraf": "^3.0.2", 46 | "stylelint": "^14.8.2", 47 | "stylelint-config-prettier": "^9.0.3", 48 | "stylelint-config-standard": "^25.0.0" 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /app/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wellyshen/use-web-animations/8e3a4ed54910eb8271f42b06b7d7ab1da636baa1/app/public/favicon.ico -------------------------------------------------------------------------------- /app/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | USE-WEB-ANIMATIONS 28 | 29 | 30 | 34 | 38 | 39 | 40 | 41 | 42 | 43 | 47 | 48 | 49 | 50 |
51 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /app/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wellyshen/use-web-animations/8e3a4ed54910eb8271f42b06b7d7ab1da636baa1/app/public/logo192.png -------------------------------------------------------------------------------- /app/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wellyshen/use-web-animations/8e3a4ed54910eb8271f42b06b7d7ab1da636baa1/app/public/logo512.png -------------------------------------------------------------------------------- /app/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "REACT COOL IMG", 3 | "name": "REACT COOL IMG", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /app/public/og_image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wellyshen/use-web-animations/8e3a4ed54910eb8271f42b06b7d7ab1da636baa1/app/public/og_image.png -------------------------------------------------------------------------------- /app/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /app/src/Animations/index.tsx: -------------------------------------------------------------------------------- 1 | import type { FC, ChangeEvent } from "react"; 2 | import useWebAnimations, * as animations from "@wellyshen/use-web-animations"; 3 | 4 | import styles from "./styles.module.scss"; 5 | 6 | const Animations: FC = () => { 7 | const { bounce } = animations; 8 | const { ref, getAnimation, animate } = useWebAnimations({ 9 | keyframes: bounce.keyframes, 10 | animationOptions: { ...bounce.animationOptions, fill: "auto" }, 11 | }); 12 | 13 | const play = () => { 14 | getAnimation()?.play(); 15 | }; 16 | 17 | const handleChangeSelect = ({ 18 | currentTarget, 19 | }: ChangeEvent) => { 20 | // @ts-ignore 21 | const { keyframes, animationOptions } = animations[currentTarget.value]; 22 | 23 | animate({ 24 | keyframes, 25 | animationOptions: { ...animationOptions, fill: "auto" }, 26 | }); 27 | }; 28 | 29 | return ( 30 |
31 |

32 | ANIMATIONS 33 |

34 |

35 | A collection of animations for Web Animations API, based on{" "} 36 | 42 | Animate.className 43 | 44 | . 45 |

46 |
54 | 55 | 🍿 56 | 57 |
58 | 189 |
190 | ); 191 | }; 192 | 193 | export default Animations; 194 | -------------------------------------------------------------------------------- /app/src/Animations/styles.module.scss: -------------------------------------------------------------------------------- 1 | @import "../mixins/mq.scss"; 2 | @import "../mixins/common.scss"; 3 | 4 | .container { 5 | @include container; 6 | 7 | margin-bottom: 7rem; 8 | 9 | .title { 10 | @include title; 11 | } 12 | 13 | .subtitle { 14 | @include subtitle; 15 | } 16 | 17 | .link { 18 | color: #fff; 19 | 20 | &:hover { 21 | text-decoration: none; 22 | } 23 | } 24 | 25 | .target { 26 | display: flex; 27 | justify-content: center; 28 | align-items: center; 29 | margin-bottom: 2.5rem; 30 | width: 140px; 31 | height: 140px; 32 | font-size: 6rem; 33 | background: #333; 34 | cursor: pointer; 35 | user-select: none; 36 | 37 | &:focus { 38 | outline: none; 39 | } 40 | 41 | @include sm { 42 | width: 200px; 43 | height: 200px; 44 | font-size: 7rem; 45 | } 46 | } 47 | 48 | .select { 49 | appearance: none; 50 | appearance: none; 51 | appearance: none; 52 | padding: 0.5rem 1rem; 53 | border: 1px dashed #fff; 54 | border-radius: 0; 55 | font-weight: bold; 56 | color: #fff; 57 | background-color: #000 !important; 58 | background-image: url("data:image/svg+xml;charset=US-ASCII,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%22292.4%22%20height%3D%22292.4%22%3E%3Cpath%20fill%3D%22%23FFFFF2%22%20d%3D%22M287%2069.4a17.6%2017.6%200%200%200-13-5.4H18.4c-5%200-9.3%201.8-12.9%205.4A17.6%2017.6%200%200%200%200%2082.2c0%205%201.8%209.3%205.4%2012.9l128%20127.9c3.6%203.6%207.8%205.4%2012.8%205.4s9.2-1.8%2012.8-5.4L287%2095c3.5-3.5%205.4-7.8%205.4-12.8%200-5-1.9-9.2-5.5-12.8z%22%2F%3E%3C%2Fsvg%3E"); 59 | background-repeat: no-repeat; 60 | background-position: right 1rem top 50%; 61 | background-size: 0.7rem auto; 62 | 63 | &:hover { 64 | background-color: rgb(255 255 255 / 20%); 65 | } 66 | 67 | &:focus { 68 | outline: none; 69 | } 70 | 71 | &::-ms-expand { 72 | display: none; 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /app/src/App/index.tsx: -------------------------------------------------------------------------------- 1 | import type { FC } from "react"; 2 | 3 | import GitHubCorner from "../GitHubCorner"; 4 | import Main from "../Main"; 5 | import Animate from "../Animations"; 6 | import "./styles.scss"; 7 | 8 | const App: FC = () => ( 9 | <> 10 | 11 |
12 | 13 | 14 | ); 15 | 16 | export default App; 17 | -------------------------------------------------------------------------------- /app/src/App/styles.scss: -------------------------------------------------------------------------------- 1 | body { 2 | color: #fff; 3 | background: #000; 4 | font-family: Roboto, sans-serif; 5 | 6 | h1, 7 | h2 { 8 | font-family: "Bungee Shade", cursive; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /app/src/GitHubCorner/index.tsx: -------------------------------------------------------------------------------- 1 | import type { FC } from "react"; 2 | 3 | import styles from "./styles.module.scss"; 4 | 5 | interface Props { 6 | url: string; 7 | } 8 | 9 | const GitHubCorner: FC = ({ url }: Props) => ( 10 | 11 | 29 | 30 | ); 31 | 32 | export default GitHubCorner; 33 | -------------------------------------------------------------------------------- /app/src/GitHubCorner/styles.module.scss: -------------------------------------------------------------------------------- 1 | @import "../mixins/mq.scss"; 2 | 3 | @keyframes wave { 4 | 0%, 5 | 100% { 6 | transform: rotate(0); 7 | } 8 | 9 | 20%, 10 | 60% { 11 | transform: rotate(-25deg); 12 | } 13 | 14 | 40%, 15 | 80% { 16 | transform: rotate(10deg); 17 | } 18 | } 19 | 20 | .github { 21 | position: absolute; 22 | top: 0; 23 | right: 0; 24 | 25 | .octo { 26 | position: absolute; 27 | top: 0; 28 | right: 0; 29 | border: 0; 30 | fill: #fff; 31 | color: #000; 32 | } 33 | 34 | .octo-arm { 35 | transform-origin: 130px 106px; 36 | animation: wave 560ms ease-in-out; 37 | 38 | @include sm { 39 | animation: none; 40 | } 41 | } 42 | 43 | &:hover .octo-arm { 44 | animation: none; 45 | 46 | @include sm { 47 | animation: wave 560ms ease-in-out; 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /app/src/Main/index.tsx: -------------------------------------------------------------------------------- 1 | import type { FC, MouseEvent, ChangeEvent } from "react"; 2 | import useWebAnimations from "@wellyshen/use-web-animations"; 3 | 4 | import styles from "./styles.module.scss"; 5 | 6 | const Main: FC = () => { 7 | const { ref: blockRef, getAnimation: getBlockAnim } = 8 | useWebAnimations({ 9 | keyframes: { width: ["0", "100%", "0"], left: ["0", "0", "100%"] }, 10 | animationOptions: { 11 | duration: 2000, 12 | fill: "forwards", 13 | easing: "cubic-bezier(0.74, 0.06, 0.4, 0.92)", 14 | }, 15 | }); 16 | const { ref: textRef, getAnimation: getTxtAnim } = 17 | useWebAnimations({ 18 | keyframes: { opacity: ["0", "1"] }, 19 | animationOptions: { 20 | delay: 1600, 21 | duration: 1000, 22 | fill: "forwards", 23 | }, 24 | }); 25 | const { ref: heartRef, getAnimation: getHeartAnim } = 26 | useWebAnimations({ 27 | keyframes: { 28 | transform: ["translate3d(0, 0, 0)", "translate3d(0, -100%, 0)"], 29 | }, 30 | animationOptions: { 31 | delay: 2000, 32 | duration: 250, 33 | fill: "forwards", 34 | easing: "cubic-bezier(0.175, 0.885, 0.32, 1.275)", 35 | }, 36 | }); 37 | 38 | const handlePlayback = (e: MouseEvent) => { 39 | const method = (e.target as HTMLButtonElement).id; 40 | 41 | (getBlockAnim() as any)[method](); 42 | (getTxtAnim() as any)[method](); 43 | (getHeartAnim() as any)[method](); 44 | }; 45 | 46 | const handleSeek = (e: ChangeEvent) => { 47 | const value = parseInt((e.target as HTMLInputElement).value, 10); 48 | 49 | const blockAnim = getBlockAnim(); 50 | const blockTiming = blockAnim?.effect?.getTiming(); 51 | if (blockAnim?.playState === "running") blockAnim.pause(); 52 | // @ts-ignore 53 | blockAnim.currentTime = ((blockTiming?.duration as number) / 100) * value; 54 | 55 | const txtAnim = getTxtAnim(); 56 | const txtTiming = txtAnim?.effect?.getTiming(); 57 | if (txtAnim?.playState === "running") txtAnim.pause(); 58 | // @ts-ignore 59 | txtAnim.currentTime = 60 | // @ts-ignore 61 | ((txtTiming.delay + (txtTiming.duration as number)) / 100) * value; 62 | 63 | const heartAnim = getHeartAnim(); 64 | const heartTiming = heartAnim?.effect?.getTiming(); 65 | if (heartAnim?.playState === "running") heartAnim.pause(); 66 | // @ts-ignore 67 | heartAnim.currentTime = 68 | // @ts-ignore 69 | ((heartTiming.delay + (heartTiming.duration as number)) / 100) * value; 70 | }; 71 | 72 | return ( 73 |
74 |

USE-WEB-ANIMATIONS

75 |

76 | React hook for highly-performant and manipulable animations using Web 77 | Animations API. 78 |

79 |
80 |
81 | 82 | BLACK LIVES MATTER 83 | 84 | 85 | ❤ 86 | 87 |
88 |
89 |
90 | 98 | 106 | 114 | 122 |
123 | 129 |
130 |
131 | ); 132 | }; 133 | 134 | export default Main; 135 | -------------------------------------------------------------------------------- /app/src/Main/styles.module.scss: -------------------------------------------------------------------------------- 1 | @import "../mixins/mq.scss"; 2 | @import "../mixins/common.scss"; 3 | 4 | .container { 5 | @include container; 6 | 7 | .title { 8 | @include title; 9 | } 10 | 11 | .subtitle { 12 | @include subtitle; 13 | } 14 | 15 | .mask { 16 | position: relative; 17 | margin-bottom: 2.5rem; 18 | width: 87vw; 19 | text-align: left; 20 | overflow: hidden; 21 | user-select: none; 22 | 23 | @include md { 24 | width: 29rem; 25 | } 26 | } 27 | 28 | .block { 29 | position: absolute; 30 | height: 100%; 31 | background: #fff; 32 | } 33 | 34 | .txt { 35 | font-family: "Bowlby One SC", cursive; 36 | font-size: 12vw; 37 | opacity: 0; 38 | 39 | @include md { 40 | font-size: 4rem; 41 | } 42 | } 43 | 44 | .heart { 45 | position: absolute; 46 | top: 100%; 47 | margin-left: 1rem; 48 | font-size: 9vw; 49 | color: #f00; 50 | 51 | @include md { 52 | font-size: 3rem; 53 | } 54 | } 55 | 56 | .btn { 57 | padding: 0.5rem; 58 | border: 1px dashed #fff; 59 | font-weight: bold; 60 | color: #fff; 61 | background: none; 62 | cursor: pointer; 63 | 64 | &:not(:first-of-type) { 65 | margin-left: -1px; 66 | } 67 | 68 | &:hover { 69 | background: rgb(255 255 255 / 20%); 70 | } 71 | 72 | &:focus { 73 | outline: none; 74 | } 75 | 76 | @include sm { 77 | padding: 0.5rem 1rem; 78 | } 79 | } 80 | 81 | .slider { 82 | position: relative; 83 | margin-top: 1.5rem; 84 | width: 100%; 85 | background: none; 86 | appearance: none; 87 | 88 | &:focus { 89 | border: none; 90 | outline: none; 91 | } 92 | 93 | &::-moz-range-track { 94 | background: #fff; 95 | } 96 | 97 | &::-webkit-slider-thumb { 98 | appearance: none; 99 | width: 21px; 100 | height: 21px; 101 | background: #fff; 102 | cursor: pointer; 103 | } 104 | 105 | &::-moz-range-thumb { 106 | width: 21px; 107 | height: 21px; 108 | border: none; 109 | background: #fff; 110 | cursor: pointer; 111 | } 112 | 113 | &::after { 114 | content: ""; 115 | position: absolute; 116 | top: 10px; 117 | width: 100%; 118 | height: 1px; 119 | background: #fff; 120 | cursor: pointer; 121 | } 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /app/src/index.tsx: -------------------------------------------------------------------------------- 1 | import { StrictMode } from "react"; 2 | import { createRoot } from "react-dom/client"; 3 | import "normalize.css"; 4 | 5 | import App from "./App"; 6 | 7 | const root = createRoot(document.getElementById("root") as HTMLElement); 8 | root.render( 9 | 10 | 11 | 12 | ); 13 | -------------------------------------------------------------------------------- /app/src/mixins/common.scss: -------------------------------------------------------------------------------- 1 | @import "./mq.scss"; 2 | 3 | @mixin container { 4 | display: flex; 5 | flex-direction: column; 6 | align-items: center; 7 | padding: 5rem 5%; 8 | text-align: center; 9 | 10 | @include sm { 11 | padding-left: 10%; 12 | padding-right: 10%; 13 | } 14 | 15 | @include md { 16 | padding-left: 12.5%; 17 | padding-right: 12.5%; 18 | } 19 | 20 | @include lg { 21 | padding-left: 15%; 22 | padding-right: 15%; 23 | } 24 | } 25 | 26 | @mixin title { 27 | margin: 0 0 0.75rem; 28 | font-size: 2rem; 29 | } 30 | 31 | @mixin subtitle { 32 | margin: 0 0 5rem; 33 | } 34 | -------------------------------------------------------------------------------- /app/src/mixins/mq.scss: -------------------------------------------------------------------------------- 1 | @mixin sm { 2 | @media (min-width: 576px) { 3 | @content; 4 | } 5 | } 6 | 7 | @mixin md { 8 | @media (min-width: 768px) { 9 | @content; 10 | } 11 | } 12 | 13 | @mixin lg { 14 | @media (min-width: 992px) { 15 | @content; 16 | } 17 | } 18 | 19 | @mixin xl { 20 | @media (min-width: 1200px) { 21 | @content; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /app/src/react-app-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /app/src/types/index.d.ts: -------------------------------------------------------------------------------- 1 | declare module "*.css"; 2 | -------------------------------------------------------------------------------- /app/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "allowJs": true, 6 | "skipLibCheck": true, 7 | "esModuleInterop": true, 8 | "allowSyntheticDefaultImports": true, 9 | "strict": true, 10 | "forceConsistentCasingInFileNames": true, 11 | "noFallthroughCasesInSwitch": true, 12 | "module": "esnext", 13 | "moduleResolution": "node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true, 17 | "jsx": "react-jsx" 18 | }, 19 | "include": ["src"] 20 | } 21 | -------------------------------------------------------------------------------- /babel.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | ["@babel/preset-env", { "loose": true }], 4 | "@babel/preset-typescript", 5 | "@babel/preset-react" 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | preset: "ts-jest", 3 | testEnvironment: "jsdom", 4 | collectCoverageFrom: ["src/*.(ts|tsx)", "!src/index.ts", "!src/*.d.ts"], 5 | }; 6 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@wellyshen/use-web-animations", 3 | "version": "0.9.1", 4 | "description": "React hook for highly-performant and manipulable animations using Web Animations API.", 5 | "license": "MIT", 6 | "homepage": "https://use-web-animations.netlify.app", 7 | "repository": "https://github.com/wellyshen/use-web-animations", 8 | "bugs": "https://github.com/wellyshen/use-web-animations/issues", 9 | "keywords": [ 10 | "react", 11 | "hook", 12 | "react-hook", 13 | "web-animations-hook", 14 | "use", 15 | "use-web-animations", 16 | "web-animations", 17 | "web-animations-api", 18 | "waapi", 19 | "animation", 20 | "transition", 21 | "motion", 22 | "playback-control", 23 | "performance", 24 | "interaction", 25 | "ux", 26 | "typescript" 27 | ], 28 | "author": "Welly Shen (https://github.com/wellyshen)", 29 | "main": "dist/index.js", 30 | "module": "dist/index.esm.js", 31 | "types": "dist/index.d.ts", 32 | "files": [ 33 | "dist" 34 | ], 35 | "scripts": { 36 | "link-pkg": "yarn build:dev && npm link app/node_modules/react && yarn link", 37 | "start": "yarn clean:dist && yarn build:dev -w", 38 | "lint": "run-s lint:*", 39 | "lint:code": "eslint --fix . --ext .js,.ts,.tsx", 40 | "lint:type": "tsc", 41 | "lint:format": "prettier -w . -u --loglevel silent", 42 | "test": "jest", 43 | "test:watch": "yarn test --watch", 44 | "test:cov": "yarn clean:cov && yarn test --coverage", 45 | "build:dev": "yarn clean:dist && rollup -c", 46 | "build:prod": "yarn clean:dist && yarn clean:size && rollup -c --environment BUILD:production", 47 | "build:demo": "cd app && yarn && yarn build", 48 | "preversion": "run-s lint test build:prod", 49 | "postversion": "git push --follow-tags --no-verify && npm publish && yarn clean:dist && yarn clean:size", 50 | "clean:dist": "rimraf dist", 51 | "clean:size": "rimraf .size-snapshot.json", 52 | "clean:cov": "rimraf coverage", 53 | "clean": "run-p clean:*", 54 | "prepare": "husky install" 55 | }, 56 | "lint-staged": { 57 | "*.{js,ts,tsx}": "eslint --fix", 58 | "**/*": "prettier -w -u" 59 | }, 60 | "dependencies": { 61 | "@babel/runtime": "^7.14.6" 62 | }, 63 | "devDependencies": { 64 | "@babel/core": "^7.14.6", 65 | "@babel/plugin-transform-runtime": "^7.14.5", 66 | "@babel/preset-env": "^7.14.7", 67 | "@babel/preset-react": "^7.14.5", 68 | "@babel/preset-typescript": "^7.14.5", 69 | "@rollup/plugin-babel": "^5.3.0", 70 | "@rollup/plugin-commonjs": "^19.0.0", 71 | "@rollup/plugin-node-resolve": "^13.0.0", 72 | "@rollup/plugin-replace": "^2.4.2", 73 | "@testing-library/react-hooks": "^7.0.1", 74 | "@types/jest": "^26.0.24", 75 | "@types/react": "^17.0.14", 76 | "@types/react-dom": "^17.0.9", 77 | "animate.css": "^4.1.1", 78 | "eslint": "^7.2.0", 79 | "eslint-config-welly": "^1.11.3", 80 | "husky": "^7.0.1", 81 | "jest": "^27.0.6", 82 | "lint-staged": "^11.0.0", 83 | "npm-run-all": "^4.1.5", 84 | "prettier": "^2.3.2", 85 | "react": "^17.0.2", 86 | "react-dom": "^17.0.2", 87 | "react-test-renderer": "^17.0.2", 88 | "rimraf": "^3.0.2", 89 | "rollup": "^2.53.0", 90 | "rollup-plugin-copy": "^3.4.0", 91 | "rollup-plugin-size-snapshot": "^0.12.0", 92 | "rollup-plugin-terser": "^7.0.2", 93 | "ts-jest": "^27.0.3", 94 | "typescript": "^4.3.5" 95 | }, 96 | "peerDependencies": { 97 | "react": ">= 16.8.0" 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | import resolve from "@rollup/plugin-node-resolve"; 2 | import commonjs from "@rollup/plugin-commonjs"; 3 | import babel from "@rollup/plugin-babel"; 4 | import replace from "@rollup/plugin-replace"; 5 | import { sizeSnapshot } from "rollup-plugin-size-snapshot"; 6 | import { terser } from "rollup-plugin-terser"; 7 | import copy from "rollup-plugin-copy"; 8 | 9 | import pkg from "./package.json"; 10 | 11 | const isDev = process.env.BUILD !== "production"; 12 | 13 | const babelRuntimeVersion = pkg.dependencies["@babel/runtime"].replace( 14 | /^[^0-9]*/, 15 | "" 16 | ); 17 | 18 | const makeExternalPredicate = (external) => 19 | !external.length 20 | ? () => false 21 | : (id) => new RegExp(`^(${external.join("|")})($|/)`).test(id); 22 | 23 | const cjs = { 24 | file: pkg.main, 25 | format: "cjs", 26 | exports: "named", 27 | sourcemap: true, 28 | plugins: !isDev && [terser()], 29 | }; 30 | 31 | const esm = { 32 | file: pkg.module, 33 | format: "esm", 34 | exports: "named", 35 | sourcemap: true, 36 | }; 37 | 38 | const extensions = [".js", ".ts", ".tsx", ".json"]; 39 | const plugins = [ 40 | resolve({ extensions }), 41 | commonjs(), 42 | babel({ 43 | exclude: "node_modules/**", 44 | plugins: [ 45 | ["@babel/plugin-transform-runtime", { version: babelRuntimeVersion }], 46 | ], 47 | babelHelpers: "runtime", 48 | extensions, 49 | }), 50 | replace({ 51 | "process.env.NODE_ENV": JSON.stringify( 52 | isDev ? "development" : "production" 53 | ), 54 | }), 55 | !isDev && sizeSnapshot(), 56 | copy({ 57 | targets: [ 58 | { 59 | src: "src/use-web-animations.d.ts", 60 | dest: pkg.types.split("/")[0], 61 | rename: "index.d.ts", 62 | }, 63 | ], 64 | }), 65 | ].filter(Boolean); 66 | 67 | export default { 68 | input: "src", 69 | output: isDev ? [esm] : [cjs, esm], 70 | plugins, 71 | external: makeExternalPredicate([ 72 | ...Object.keys(pkg.peerDependencies), 73 | ...Object.keys(pkg.dependencies), 74 | ]), 75 | }; 76 | -------------------------------------------------------------------------------- /src/__tests__/useLatest.ts: -------------------------------------------------------------------------------- 1 | import { renderHook } from "@testing-library/react-hooks"; 2 | 3 | import useLatest from "../useLatest"; 4 | 5 | describe("useLatest", () => { 6 | it("should return correctly", () => { 7 | const val = "test"; 8 | const { result } = renderHook(() => useLatest(val)); 9 | expect(result.current.current).toBe(val); 10 | }); 11 | }); 12 | -------------------------------------------------------------------------------- /src/__tests__/useWebAnimations.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable compat/compat */ 2 | 3 | import { renderHook, act } from "@testing-library/react-hooks"; 4 | 5 | import type { Options } from "../useWebAnimations"; 6 | import useWebAnimations, { polyfillErr, eventErr } from "../useWebAnimations"; 7 | 8 | describe("useWebAnimations", () => { 9 | jest.useFakeTimers(); 10 | 11 | const el = document.createElement("div"); 12 | const target = { current: el }; 13 | const id = "test"; 14 | const playbackRate = 1; 15 | const mockKeyframes = { transform: ["translateX(500px)"] }; 16 | const mockTiming = 3000; 17 | 18 | const renderHelper = ({ 19 | ref = target, 20 | keyframes = mockKeyframes, 21 | animationOptions = mockTiming, 22 | ...rest 23 | }: Partial> = {}) => 24 | renderHook(() => 25 | useWebAnimations({ ref, keyframes, animationOptions, ...rest }) 26 | ); 27 | 28 | const e = { playState: "pause" }; 29 | const animation = { 30 | pending: true, 31 | playState: "pause", 32 | ready: Promise.resolve(e), 33 | pause: jest.fn(), 34 | finish: jest.fn(), 35 | cancel: jest.fn(), 36 | }; 37 | 38 | beforeEach(() => { 39 | // @ts-ignore 40 | el.animate = jest.fn(() => animation); 41 | }); 42 | 43 | it("should cancel animation", async () => { 44 | const { unmount } = renderHelper(); 45 | unmount(); 46 | // @ts-ignore 47 | const anim = el.animate.mock.results[0].value; 48 | expect(anim.finish).toHaveBeenCalled(); 49 | expect(anim.cancel).toHaveBeenCalled(); 50 | }); 51 | 52 | it("should call onReady correctly", async () => { 53 | console.error = jest.fn(); 54 | const onReady = jest.fn(); 55 | renderHelper({ onReady }); 56 | await Promise.resolve(); 57 | expect(onReady).toHaveBeenCalledWith({ 58 | playState: e.playState, 59 | animation: e, 60 | animate: expect.any(Function), 61 | }); 62 | expect(console.error).not.toHaveBeenCalled(); 63 | }); 64 | 65 | it("should call onFinish correctly", async () => { 66 | console.error = jest.fn(); 67 | const onFinish = jest.fn(); 68 | renderHelper({ onFinish }); 69 | // @ts-ignore 70 | el.animate.mock.results[0].value.onfinish({ target: e }); 71 | expect(onFinish).toHaveBeenCalledWith({ 72 | playState: e.playState, 73 | animation: e, 74 | animate: expect.any(Function), 75 | }); 76 | expect(console.error).not.toHaveBeenCalled(); 77 | }); 78 | 79 | it("should call onUpdate correctly", () => { 80 | window.requestAnimationFrame = jest 81 | .fn() 82 | .mockImplementationOnce((cb) => { 83 | setTimeout(cb, 0); 84 | }) 85 | .mockImplementationOnce((cb) => { 86 | setTimeout(cb, 1); 87 | }) 88 | .mockImplementationOnce((cb) => { 89 | setTimeout(cb, 2); 90 | }) 91 | .mockImplementationOnce((cb) => { 92 | setTimeout(cb, 3); 93 | }) 94 | .mockImplementationOnce((cb) => { 95 | setTimeout(cb, 4); 96 | }); 97 | 98 | const onUpdate = jest.fn(); 99 | let evt = { 100 | playState: animation.playState, 101 | animation, 102 | animate: expect.any(Function), 103 | }; 104 | renderHelper({ onUpdate }); 105 | act(() => { 106 | jest.advanceTimersByTime(0); 107 | }); 108 | expect(onUpdate).toHaveBeenCalledWith(evt); 109 | 110 | act(() => { 111 | jest.advanceTimersByTime(1); 112 | }); 113 | expect(onUpdate).toHaveBeenCalledTimes(1); 114 | expect(onUpdate).toHaveBeenCalledWith(evt); 115 | 116 | animation.pending = false; 117 | act(() => { 118 | jest.advanceTimersByTime(2); 119 | }); 120 | expect(onUpdate).toHaveBeenCalledTimes(2); 121 | 122 | animation.playState = "running"; 123 | evt = { 124 | ...evt, 125 | playState: animation.playState, 126 | }; 127 | act(() => { 128 | jest.advanceTimersByTime(3); 129 | }); 130 | expect(onUpdate).toHaveBeenCalledTimes(3); 131 | expect(onUpdate).toHaveBeenCalledWith(evt); 132 | 133 | act(() => { 134 | jest.advanceTimersByTime(4); 135 | }); 136 | expect(onUpdate).toHaveBeenCalledTimes(4); 137 | }); 138 | 139 | it("shouldn't call animate if either ref or keyframes isn't set", () => { 140 | // @ts-ignore 141 | renderHelper({ ref: null }); 142 | expect(el.animate).not.toHaveBeenCalled(); 143 | 144 | // @ts-ignore 145 | renderHelper({ keyframes: null }); 146 | expect(el.animate).not.toHaveBeenCalled(); 147 | }); 148 | 149 | it("should call animate correctly", () => { 150 | renderHelper(); 151 | expect(el.animate).toHaveBeenCalledWith(mockKeyframes, mockTiming); 152 | }); 153 | 154 | it("should return workable ref", () => { 155 | // @ts-ignore 156 | const { result } = renderHelper({ ref: null }); 157 | expect(result.current.ref).toEqual({ current: null }); 158 | 159 | result.current.ref = target; 160 | expect(result.current.ref).toEqual(target); 161 | }); 162 | 163 | it("should return playState correctly", () => { 164 | window.requestAnimationFrame = jest.fn().mockImplementationOnce((cb) => { 165 | setTimeout(cb, 0); 166 | }); 167 | 168 | const { result } = renderHelper(); 169 | act(() => { 170 | jest.runAllTimers(); 171 | }); 172 | expect(result.current.playState).toBe(animation.playState); 173 | }); 174 | 175 | it("should return workable getAnimation method", () => { 176 | const { result } = renderHelper(); 177 | expect(result.current.getAnimation()).toEqual(animation); 178 | }); 179 | 180 | it("should return workable animate method", () => { 181 | const { result } = renderHelper(); 182 | result.current.animate({ 183 | id, 184 | autoPlay: false, 185 | playbackRate, 186 | keyframes: mockKeyframes, 187 | animationOptions: mockTiming, 188 | }); 189 | // @ts-ignore 190 | const anim = el.animate.mock.results[0].value; 191 | expect(anim.pause).toHaveBeenCalled(); 192 | expect(anim.playbackRate).toBe(playbackRate); 193 | expect(anim.id).toBe(id); 194 | expect(el.animate).toHaveBeenCalledWith(mockKeyframes, mockTiming); 195 | }); 196 | 197 | it("should set animation id correctly", () => { 198 | renderHelper({ id }); 199 | // @ts-ignore 200 | expect(el.animate.mock.results[0].value.id).toBe(id); 201 | }); 202 | 203 | it("should pause animation at start", () => { 204 | renderHelper({ autoPlay: false }); 205 | // @ts-ignore 206 | expect(el.animate.mock.results[0].value.pause).toHaveBeenCalled(); 207 | }); 208 | 209 | it("should update playback rate correctly", () => { 210 | renderHelper({ playbackRate }); 211 | // @ts-ignore 212 | expect(el.animate.mock.results[0].value.playbackRate).toBe(playbackRate); 213 | }); 214 | 215 | it("should throw polyfill error", () => { 216 | console.error = jest.fn(); 217 | // @ts-ignore 218 | el.animate = null; 219 | renderHelper(); 220 | expect(console.error).toHaveBeenCalledWith(polyfillErr); 221 | }); 222 | 223 | it("should throw event errors", () => { 224 | console.error = jest.fn(); 225 | // @ts-ignore 226 | animation.ready = null; 227 | renderHelper({ onReady: () => null }); 228 | expect(console.error).toHaveBeenCalledWith(eventErr); 229 | }); 230 | }); 231 | -------------------------------------------------------------------------------- /src/animations/backInDown.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | keyframes: [ 3 | { transform: "translateY(-1200px) scale(0.7)", opacity: 0.7, offset: 0 }, 4 | { transform: "translateY(0px) scale(0.7)", opacity: 0.7, offset: 0.8 }, 5 | { transform: "scale(1)", opacity: 1, offset: 1 }, 6 | ], 7 | animationOptions: { duration: 1000, fill: "both" }, 8 | }; 9 | -------------------------------------------------------------------------------- /src/animations/backInLeft.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | keyframes: [ 3 | { transform: "translateX(-2000px) scale(0.7)", opacity: 0.7, offset: 0 }, 4 | { transform: "translateX(0px) scale(0.7)", opacity: 0.7, offset: 0.8 }, 5 | { transform: "scale(1)", opacity: 1, offset: 1 }, 6 | ], 7 | animationOptions: { duration: 1000, fill: "both" }, 8 | }; 9 | -------------------------------------------------------------------------------- /src/animations/backInRight.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | keyframes: [ 3 | { transform: "translateX(2000px) scale(0.7)", opacity: 0.7, offset: 0 }, 4 | { transform: "translateX(0px) scale(0.7)", opacity: 0.7, offset: 0.8 }, 5 | { transform: "scale(1)", opacity: 1, offset: 1 }, 6 | ], 7 | animationOptions: { duration: 1000, fill: "both" }, 8 | }; 9 | -------------------------------------------------------------------------------- /src/animations/backInUp.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | keyframes: [ 3 | { transform: "translateY(1200px) scale(0.7)", opacity: 0.7, offset: 0 }, 4 | { transform: "translateX(0px) scale(0.7)", opacity: 0.7, offset: 0.8 }, 5 | { transform: "scale(1)", opacity: 1, offset: 1 }, 6 | ], 7 | animationOptions: { duration: 1000, fill: "both" }, 8 | }; 9 | -------------------------------------------------------------------------------- /src/animations/backOutDown.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | keyframes: [ 3 | { transform: "scale(1)", opacity: 1, offset: 0 }, 4 | { transform: "translateY(0px) scale(0.7)", opacity: 0.7, offset: 0.2 }, 5 | { transform: "translateY(700px) scale(0.7)", opacity: 0.7, offset: 1 }, 6 | ], 7 | animationOptions: { duration: 1000, fill: "both" }, 8 | }; 9 | -------------------------------------------------------------------------------- /src/animations/backOutLeft.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | keyframes: [ 3 | { transform: "scale(1)", opacity: 1, offset: 0 }, 4 | { transform: "translateY(0px) scale(0.7)", opacity: 0.7, offset: 0.2 }, 5 | { transform: "translateX(-2000px) scale(0.7)", opacity: 0.7, offset: 1 }, 6 | ], 7 | animationOptions: { duration: 1000, fill: "both" }, 8 | }; 9 | -------------------------------------------------------------------------------- /src/animations/backOutRight.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | keyframes: [ 3 | { transform: "scale(1)", opacity: 1, offset: 0 }, 4 | { transform: "translateY(0px) scale(0.7)", opacity: 0.7, offset: 0.2 }, 5 | { transform: "translateX(2000px) scale(0.7)", opacity: 0.7, offset: 1 }, 6 | ], 7 | animationOptions: { duration: 1000, fill: "both" }, 8 | }; 9 | -------------------------------------------------------------------------------- /src/animations/backOutUp.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | keyframes: [ 3 | { transform: "scale(1)", opacity: 1, offset: 0 }, 4 | { transform: "translateY(0px) scale(0.7)", opacity: 0.7, offset: 0.2 }, 5 | { transform: "translateY(-700px) scale(0.7)", opacity: 0.7, offset: 1 }, 6 | ], 7 | animationOptions: { duration: 1000, fill: "both" }, 8 | }; 9 | -------------------------------------------------------------------------------- /src/animations/bounce.ts: -------------------------------------------------------------------------------- 1 | const easing1 = "cubic-bezier(0.215, 0.61, 0.355, 1)"; 2 | const easing2 = "cubic-bezier(0.755, 0.05, 0.855, 0.06)"; 3 | const transformOrigin = "center bottom"; 4 | const frame1 = { 5 | transform: "translate3d(0, 0, 0)", 6 | easing: easing1, 7 | transformOrigin, 8 | }; 9 | const frame2 = { 10 | transform: "translate3d(0, -30px, 0) scaleY(1.1)", 11 | easing: easing2, 12 | transformOrigin, 13 | }; 14 | 15 | export default { 16 | keyframes: [ 17 | { ...frame1, offset: 0 }, 18 | { ...frame1, offset: 0.2 }, 19 | { ...frame2, offset: 0.4 }, 20 | { ...frame2, offset: 0.43 }, 21 | { ...frame1, offset: 0.53 }, 22 | { 23 | transform: "translate3d(0, -15px, 0) scaleY(1.05)", 24 | easing: easing2, 25 | transformOrigin, 26 | offset: 0.7, 27 | }, 28 | { 29 | transform: "translate3d(0, 0, 0) scaleY(0.95)", 30 | easing: easing1, 31 | transformOrigin, 32 | offset: 0.8, 33 | }, 34 | { 35 | transform: "translate3d(0, -4px, 0) scaleY(1.02)", 36 | transformOrigin, 37 | offset: 0.9, 38 | }, 39 | { ...frame1, offset: 1 }, 40 | ], 41 | animationOptions: { duration: 1000, fill: "both" }, 42 | }; 43 | -------------------------------------------------------------------------------- /src/animations/bounceIn.ts: -------------------------------------------------------------------------------- 1 | const easing = "cubic-bezier(0.215, 0.61, 0.355, 1)"; 2 | 3 | export default { 4 | keyframes: [ 5 | { transform: "scale3d(0.3, 0.3, 0.3)", opacity: 0, easing, offset: 0 }, 6 | { transform: "scale3d(1.1, 1.1, 1.1)", easing, offset: 0.2 }, 7 | { transform: "scale3d(0.9, 0.9, 0.9)", easing, offset: 0.4 }, 8 | { transform: "scale3d(1.03, 1.03, 1.03)", opacity: 1, easing, offset: 0.6 }, 9 | { transform: "scale3d(0.97, 0.97, 0.97)", easing, offset: 0.8 }, 10 | { transform: "scale3d(1, 1, 1)", opacity: 1, easing, offset: 1 }, 11 | ], 12 | animationOptions: { duration: 750, fill: "both" }, 13 | }; 14 | -------------------------------------------------------------------------------- /src/animations/bounceInDown.ts: -------------------------------------------------------------------------------- 1 | const easing = "cubic-bezier(0.215, 0.61, 0.355, 1)"; 2 | 3 | export default { 4 | keyframes: [ 5 | { 6 | transform: "translate3d(0, -3000px, 0) scaleY(3)", 7 | opacity: 0, 8 | easing, 9 | offset: 0, 10 | }, 11 | { 12 | transform: "translate3d(0, 25px, 0) scaleY(0.9)", 13 | opacity: 1, 14 | easing, 15 | offset: 0.6, 16 | }, 17 | { 18 | transform: "translate3d(0, -10px, 0) scaleY(0.95)", 19 | easing, 20 | offset: 0.75, 21 | }, 22 | { transform: "translate3d(0, 5px, 0) scaleY(0.985)", easing, offset: 0.9 }, 23 | { 24 | transform: "translate3d(0, 0, 0)", 25 | opacity: 1, 26 | easing, 27 | offset: 1, 28 | }, 29 | ], 30 | animationOptions: { duration: 1000, fill: "both" }, 31 | }; 32 | -------------------------------------------------------------------------------- /src/animations/bounceInLeft.ts: -------------------------------------------------------------------------------- 1 | const easing = "cubic-bezier(0.215, 0.61, 0.355, 1)"; 2 | 3 | export default { 4 | keyframes: [ 5 | { 6 | transform: "translate3d(-3000px, 0, 0) scaleX(3)", 7 | opacity: 0, 8 | easing, 9 | offset: 0, 10 | }, 11 | { 12 | transform: "translate3d(25px, 0, 0) scaleX(1)", 13 | opacity: 1, 14 | easing, 15 | offset: 0.6, 16 | }, 17 | { 18 | transform: "translate3d(-10px, 0, 0) scaleX(0.98)", 19 | easing, 20 | offset: 0.75, 21 | }, 22 | { transform: "translate3d(5px, 0, 0) scaleX(0.995)", easing, offset: 0.9 }, 23 | { 24 | transform: "translate3d(0, 0, 0)", 25 | opacity: 1, 26 | easing, 27 | offset: 1, 28 | }, 29 | ], 30 | animationOptions: { duration: 1000, fill: "both" }, 31 | }; 32 | -------------------------------------------------------------------------------- /src/animations/bounceInRight.ts: -------------------------------------------------------------------------------- 1 | const easing = "cubic-bezier(0.215, 0.61, 0.355, 1)"; 2 | 3 | export default { 4 | keyframes: [ 5 | { 6 | transform: "translate3d(3000px, 0, 0) scaleX(3)", 7 | opacity: 0, 8 | easing, 9 | offset: 0, 10 | }, 11 | { 12 | transform: "translate3d(-25px, 0, 0) scaleX(1)", 13 | opacity: 1, 14 | easing, 15 | offset: 0.6, 16 | }, 17 | { 18 | transform: "translate3d(10px, 0, 0) scaleX(0.98)", 19 | easing, 20 | offset: 0.75, 21 | }, 22 | { transform: "translate3d(-5px, 0, 0) scaleX(0.995)", easing, offset: 0.9 }, 23 | { 24 | transform: "translate3d(0, 0, 0)", 25 | opacity: 1, 26 | easing, 27 | offset: 1, 28 | }, 29 | ], 30 | animationOptions: { duration: 1000, fill: "both" }, 31 | }; 32 | -------------------------------------------------------------------------------- /src/animations/bounceInUp.ts: -------------------------------------------------------------------------------- 1 | const easing = "cubic-bezier(0.215, 0.61, 0.355, 1)"; 2 | 3 | export default { 4 | keyframes: [ 5 | { 6 | transform: "translate3d(0, 3000px, 0) scaleY(5)", 7 | opacity: 0, 8 | easing, 9 | offset: 0, 10 | }, 11 | { 12 | transform: "translate3d(0, -20px, 0) scaleY(0.9)", 13 | opacity: 1, 14 | easing, 15 | offset: 0.6, 16 | }, 17 | { 18 | transform: "translate3d(0, 10px, 0) scaleY(0.95)", 19 | easing, 20 | offset: 0.75, 21 | }, 22 | { transform: "translate3d(0, -5px, 0) scaleY(0.985)", easing, offset: 0.9 }, 23 | { 24 | transform: "translate3d(0, 0, 0)", 25 | opacity: 1, 26 | easing, 27 | offset: 1, 28 | }, 29 | ], 30 | animationOptions: { duration: 1000, fill: "both" }, 31 | }; 32 | -------------------------------------------------------------------------------- /src/animations/bounceOut.ts: -------------------------------------------------------------------------------- 1 | const frame = { transform: "scale3d(1.1, 1.1, 1.1)", opacity: 1 }; 2 | 3 | export default { 4 | keyframes: [ 5 | { transform: "none", opacity: 1, offset: 0 }, 6 | { transform: "scale3d(0.9, 0.9, 0.9)", offset: 0.2 }, 7 | { ...frame, offset: 0.5 }, 8 | { ...frame, offset: 0.55 }, 9 | { transform: "scale3d(0.3, 0.3, 0.3)", opacity: 0, offset: 1 }, 10 | ], 11 | animationOptions: { duration: 750, fill: "both" }, 12 | }; 13 | -------------------------------------------------------------------------------- /src/animations/bounceOutDown.ts: -------------------------------------------------------------------------------- 1 | const frame = { transform: "translate3d(0, -20px, 0) scaleY(0.9)", opacity: 1 }; 2 | 3 | export default { 4 | keyframes: [ 5 | { transform: "none", opacity: 1, offset: 0 }, 6 | { transform: "translate3d(0, 10px, 0) scaleY(0.985)", offset: 0.2 }, 7 | { ...frame, offset: 0.4 }, 8 | { ...frame, offset: 0.45 }, 9 | { transform: "translate3d(0, 2000px, 0) scaleY(3)", opacity: 0, offset: 1 }, 10 | ], 11 | animationOptions: { duration: 1000, fill: "both" }, 12 | }; 13 | -------------------------------------------------------------------------------- /src/animations/bounceOutLeft.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | keyframes: [ 3 | { transform: "none", opacity: 1, offset: 0 }, 4 | { 5 | transform: "translate3d(20px, 0, 0) scaleX(0.9)", 6 | opacity: 1, 7 | offset: 0.2, 8 | }, 9 | { 10 | transform: "translate3d(-2000px, 0, 0) scaleX(2)", 11 | opacity: 0, 12 | offset: 1, 13 | }, 14 | ], 15 | animationOptions: { duration: 1000, fill: "both" }, 16 | }; 17 | -------------------------------------------------------------------------------- /src/animations/bounceOutRight.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | keyframes: [ 3 | { transform: "none", opacity: 1, offset: 0 }, 4 | { 5 | transform: "translate3d(-20px, 0, 0) scaleX(0.9)", 6 | opacity: 1, 7 | offset: 0.2, 8 | }, 9 | { 10 | transform: "translate3d(2000px, 0, 0) scaleX(2)", 11 | opacity: 0, 12 | offset: 1, 13 | }, 14 | ], 15 | animationOptions: { duration: 1000, fill: "both" }, 16 | }; 17 | -------------------------------------------------------------------------------- /src/animations/bounceOutUp.ts: -------------------------------------------------------------------------------- 1 | const frame = { transform: "translate3d(0, 20px, 0) scaleY(0.9)", opacity: 1 }; 2 | 3 | export default { 4 | keyframes: [ 5 | { transform: "none", opacity: 1, offset: 0 }, 6 | { transform: "translate3d(0, -10px, 0) scaleY(0.985)", offset: 0.2 }, 7 | { ...frame, offset: 0.4 }, 8 | { ...frame, offset: 0.45 }, 9 | { 10 | transform: "translate3d(0, -2000px, 0) scaleY(3)", 11 | opacity: 0, 12 | offset: 1, 13 | }, 14 | ], 15 | animationOptions: { duration: 1000, fill: "both" }, 16 | }; 17 | -------------------------------------------------------------------------------- /src/animations/fadeIn.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | keyframes: [{ opacity: 0 }, { opacity: 1 }], 3 | animationOptions: { duration: 1000, fill: "both" }, 4 | }; 5 | -------------------------------------------------------------------------------- /src/animations/fadeInBottomLeft.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | keyframes: [ 3 | { transform: "translate3d(-100%, 100%, 0)", opacity: 0 }, 4 | { transform: "translate3d(0, 0, 0)", opacity: 1 }, 5 | ], 6 | animationOptions: { duration: 1000, fill: "both" }, 7 | }; 8 | -------------------------------------------------------------------------------- /src/animations/fadeInBottomRight.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | keyframes: [ 3 | { transform: "translate3d(100%, 100%, 0)", opacity: 0 }, 4 | { transform: "translate3d(0, 0, 0)", opacity: 1 }, 5 | ], 6 | animationOptions: { duration: 1000, fill: "both" }, 7 | }; 8 | -------------------------------------------------------------------------------- /src/animations/fadeInDown.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | keyframes: [ 3 | { transform: "translate3d(0, -100%, 0)", opacity: 0 }, 4 | { transform: "translate3d(0, 0, 0)", opacity: 1 }, 5 | ], 6 | animationOptions: { duration: 1000, fill: "both" }, 7 | }; 8 | -------------------------------------------------------------------------------- /src/animations/fadeInDownBig.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | keyframes: [ 3 | { transform: "translate3d(0, -2000px, 0)", opacity: 0 }, 4 | { transform: "translate3d(0, 0, 0)", opacity: 1 }, 5 | ], 6 | animationOptions: { duration: 1000, fill: "both" }, 7 | }; 8 | -------------------------------------------------------------------------------- /src/animations/fadeInLeft.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | keyframes: [ 3 | { transform: "translate3d(-100%, 0, 0)", opacity: 0 }, 4 | { transform: "translate3d(0, 0, 0)", opacity: 1 }, 5 | ], 6 | animationOptions: { duration: 1000, fill: "both" }, 7 | }; 8 | -------------------------------------------------------------------------------- /src/animations/fadeInLeftBig.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | keyframes: [ 3 | { transform: "translate3d(-2000px, 0, 0)", opacity: 0 }, 4 | { transform: "translate3d(0, 0, 0)", opacity: 1 }, 5 | ], 6 | animationOptions: { duration: 1000, fill: "both" }, 7 | }; 8 | -------------------------------------------------------------------------------- /src/animations/fadeInRight.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | keyframes: [ 3 | { transform: "translate3d(100%, 0, 0)", opacity: 0 }, 4 | { transform: "translate3d(0, 0, 0)", opacity: 1 }, 5 | ], 6 | animationOptions: { duration: 1000, fill: "both" }, 7 | }; 8 | -------------------------------------------------------------------------------- /src/animations/fadeInRightBig.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | keyframes: [ 3 | { transform: "translate3d(2000px, 0, 0)", opacity: 0 }, 4 | { transform: "translate3d(0, 0, 0)", opacity: 1 }, 5 | ], 6 | animationOptions: { duration: 1000, fill: "both" }, 7 | }; 8 | -------------------------------------------------------------------------------- /src/animations/fadeInTopLeft.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | keyframes: [ 3 | { transform: "translate3d(-100%, -100%, 0)", opacity: 0 }, 4 | { transform: "translate3d(0, 0, 0)", opacity: 1 }, 5 | ], 6 | animationOptions: { duration: 1000, fill: "both" }, 7 | }; 8 | -------------------------------------------------------------------------------- /src/animations/fadeInTopRight.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | keyframes: [ 3 | { transform: "translate3d(100%, -100%, 0)", opacity: 0 }, 4 | { transform: "translate3d(0, 0, 0)", opacity: 1 }, 5 | ], 6 | animationOptions: { duration: 1000, fill: "both" }, 7 | }; 8 | -------------------------------------------------------------------------------- /src/animations/fadeInUp.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | keyframes: [ 3 | { transform: "translate3d(0, 100%, 0)", opacity: 0 }, 4 | { transform: "translate3d(0, 0, 0)", opacity: 1 }, 5 | ], 6 | animationOptions: { duration: 1000, fill: "both" }, 7 | }; 8 | -------------------------------------------------------------------------------- /src/animations/fadeInUpBig.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | keyframes: [ 3 | { transform: "translate3d(0, 2000px, 0)", opacity: 0 }, 4 | { transform: "translate3d(0, 0, 0)", opacity: 1 }, 5 | ], 6 | animationOptions: { duration: 1000, fill: "both" }, 7 | }; 8 | -------------------------------------------------------------------------------- /src/animations/fadeOut.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | keyframes: [{ opacity: 1 }, { opacity: 0 }], 3 | animationOptions: { duration: 1000, fill: "both" }, 4 | }; 5 | -------------------------------------------------------------------------------- /src/animations/fadeOutBottomLeft.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | keyframes: [ 3 | { transform: "translate3d(0, 0, 0)", opacity: 1 }, 4 | { transform: "translate3d(-100%, 100%, 0)", opacity: 0 }, 5 | ], 6 | animationOptions: { duration: 1000, fill: "both" }, 7 | }; 8 | -------------------------------------------------------------------------------- /src/animations/fadeOutBottomRight.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | keyframes: [ 3 | { transform: "translate3d(0, 0, 0)", opacity: 1 }, 4 | { transform: "translate3d(100%, 100%, 0)", opacity: 0 }, 5 | ], 6 | animationOptions: { duration: 1000, fill: "both" }, 7 | }; 8 | -------------------------------------------------------------------------------- /src/animations/fadeOutDown.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | keyframes: [ 3 | { transform: "translate3d(0, 0, 0)", opacity: 1 }, 4 | { transform: "translate3d(0, 100%, 0)", opacity: 0 }, 5 | ], 6 | animationOptions: { duration: 1000, fill: "both" }, 7 | }; 8 | -------------------------------------------------------------------------------- /src/animations/fadeOutDownBig.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | keyframes: [ 3 | { transform: "translate3d(0, 0, 0)", opacity: 1 }, 4 | { transform: "translate3d(0, 2000px, 0)", opacity: 0 }, 5 | ], 6 | animationOptions: { duration: 1000, fill: "both" }, 7 | }; 8 | -------------------------------------------------------------------------------- /src/animations/fadeOutLeft.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | keyframes: [ 3 | { transform: "translate3d(0, 0, 0)", opacity: 1 }, 4 | { transform: "translate3d(-100%, 0, 0)", opacity: 0 }, 5 | ], 6 | animationOptions: { duration: 1000, fill: "both" }, 7 | }; 8 | -------------------------------------------------------------------------------- /src/animations/fadeOutLeftBig.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | keyframes: [ 3 | { transform: "translate3d(0, 0, 0)", opacity: 1 }, 4 | { transform: "translate3d(-2000px, 0, 0)", opacity: 0 }, 5 | ], 6 | animationOptions: { duration: 1000, fill: "both" }, 7 | }; 8 | -------------------------------------------------------------------------------- /src/animations/fadeOutRight.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | keyframes: [ 3 | { transform: "translate3d(0, 0, 0)", opacity: 1 }, 4 | { transform: "translate3d(100%, 0, 0)", opacity: 0 }, 5 | ], 6 | animationOptions: { duration: 1000, fill: "both" }, 7 | }; 8 | -------------------------------------------------------------------------------- /src/animations/fadeOutRightBig.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | keyframes: [ 3 | { transform: "translate3d(0, 0, 0)", opacity: 1 }, 4 | { transform: "translate3d(2000px, 0, 0)", opacity: 0 }, 5 | ], 6 | animationOptions: { duration: 1000, fill: "both" }, 7 | }; 8 | -------------------------------------------------------------------------------- /src/animations/fadeOutTopLeft.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | keyframes: [ 3 | { transform: "translate3d(0, 0, 0)", opacity: 1 }, 4 | { transform: "translate3d(-100%, -100%, 0)", opacity: 0 }, 5 | ], 6 | animationOptions: { duration: 1000, fill: "both" }, 7 | }; 8 | -------------------------------------------------------------------------------- /src/animations/fadeOutTopRight.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | keyframes: [ 3 | { transform: "translate3d(0, 0, 0)", opacity: 1 }, 4 | { transform: "translate3d(100%, -100%, 0)", opacity: 0 }, 5 | ], 6 | animationOptions: { duration: 1000, fill: "both" }, 7 | }; 8 | -------------------------------------------------------------------------------- /src/animations/fadeOutUp.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | keyframes: [ 3 | { transform: "translate3d(0, 0, 0)", opacity: 1 }, 4 | { transform: "translate3d(0, -100%, 0)", opacity: 0 }, 5 | ], 6 | animationOptions: { duration: 1000, fill: "both" }, 7 | }; 8 | -------------------------------------------------------------------------------- /src/animations/fadeOutUpBig.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | keyframes: [ 3 | { transform: "translate3d(0, 0, 0)", opacity: 1 }, 4 | { transform: "translate3d(0, -2000px, 0)", opacity: 0 }, 5 | ], 6 | animationOptions: { duration: 1000, fill: "both" }, 7 | }; 8 | -------------------------------------------------------------------------------- /src/animations/flash.ts: -------------------------------------------------------------------------------- 1 | const frame1 = { opacity: 1 }; 2 | const frame2 = { opacity: 0 }; 3 | 4 | export default { 5 | keyframes: [ 6 | { ...frame1, offset: 0 }, 7 | { ...frame2, offset: 0.25 }, 8 | { ...frame1, offset: 0.5 }, 9 | { ...frame2, offset: 0.75 }, 10 | { ...frame1, offset: 1 }, 11 | ], 12 | animationOptions: { duration: 1000, fill: "both" }, 13 | }; 14 | -------------------------------------------------------------------------------- /src/animations/flip.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | keyframes: [ 3 | { 4 | transform: 5 | "perspective(400px) scale3d(1, 1, 1) translate3d(0, 0, 0) rotate3d(0, 1, 0, -360deg)", 6 | backfaceVisibility: "visible", 7 | easing: "ease-out", 8 | offset: 0, 9 | }, 10 | { 11 | transform: 12 | "perspective(400px) scale3d(1, 1, 1) translate3d(0, 0, 150px) rotate3d(0, 1, 0, -190deg)", 13 | easing: "ease-out", 14 | offset: 0.4, 15 | }, 16 | { 17 | transform: 18 | "perspective(400px) scale3d(1, 1, 1) translate3d(0, 0, 150px) rotate3d(0, 1, 0, -170deg)", 19 | easing: "ease-in", 20 | offset: 0.5, 21 | }, 22 | { 23 | transform: 24 | "perspective(400px) scale3d(0.95, 0.95, 0.95) translate3d(0, 0, 0) rotate3d(0, 1, 0, 0deg)", 25 | easing: "ease-in", 26 | offset: 0.8, 27 | }, 28 | { 29 | transform: 30 | "perspective(400px) scale3d(1, 1, 1) translate3d(0, 0, 0) rotate3d(0, 1, 0, 0deg)", 31 | backfaceVisibility: "visible", 32 | easing: "ease-in", 33 | offset: 1, 34 | }, 35 | ], 36 | animationOptions: { duration: 1000, fill: "both" }, 37 | }; 38 | -------------------------------------------------------------------------------- /src/animations/flipInX.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | keyframes: [ 3 | { 4 | transform: "perspective(400px) rotate3d(1, 0, 0, 90deg)", 5 | opacity: 0, 6 | backfaceVisibility: "visible !important", 7 | easing: "ease-in", 8 | offset: 0, 9 | }, 10 | { 11 | transform: "perspective(400px) rotate3d(1, 0, 0, -20deg)", 12 | easing: "ease-in", 13 | offset: 0.4, 14 | }, 15 | { 16 | transform: "perspective(400px) rotate3d(1, 0, 0, 10deg)", 17 | opacity: 1, 18 | offset: 0.6, 19 | }, 20 | { 21 | transform: "perspective(400px) rotate3d(1, 0, 0, -5deg)", 22 | offset: 0.8, 23 | }, 24 | { 25 | transform: "perspective(400px)", 26 | opacity: 1, 27 | backfaceVisibility: "visible !important", 28 | offset: 1, 29 | }, 30 | ], 31 | animationOptions: { duration: 1000, fill: "both" }, 32 | }; 33 | -------------------------------------------------------------------------------- /src/animations/flipInY.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | keyframes: [ 3 | { 4 | transform: "perspective(400px) rotate3d(0, 1, 0, 90deg)", 5 | opacity: 0, 6 | backfaceVisibility: "visible !important", 7 | easing: "ease-in", 8 | offset: 0, 9 | }, 10 | { 11 | transform: "perspective(400px) rotate3d(0, 1, 0, -20deg)", 12 | easing: "ease-in", 13 | offset: 0.4, 14 | }, 15 | { 16 | transform: "perspective(400px) rotate3d(0, 1, 0, 10deg)", 17 | opacity: 1, 18 | offset: 0.6, 19 | }, 20 | { 21 | transform: "perspective(400px) rotate3d(0, 1, 0, -5deg)", 22 | offset: 0.8, 23 | }, 24 | { 25 | transform: "perspective(400px)", 26 | opacity: 1, 27 | backfaceVisibility: "visible !important", 28 | offset: 1, 29 | }, 30 | ], 31 | animationOptions: { duration: 1000, fill: "both" }, 32 | }; 33 | -------------------------------------------------------------------------------- /src/animations/flipOutX.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | keyframes: [ 3 | { 4 | transform: "perspective(400px)", 5 | opacity: 1, 6 | backfaceVisibility: "visible !important", 7 | offset: 0, 8 | }, 9 | { 10 | transform: "perspective(400px) rotate3d(1, 0, 0, -20deg)", 11 | opacity: 1, 12 | offset: 0.3, 13 | }, 14 | { 15 | transform: "perspective(400px) rotate3d(1, 0, 0, 90deg)", 16 | opacity: 0, 17 | backfaceVisibility: "visible !important", 18 | offset: 1, 19 | }, 20 | ], 21 | animationOptions: { duration: 750, fill: "both" }, 22 | }; 23 | -------------------------------------------------------------------------------- /src/animations/flipOutY.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | keyframes: [ 3 | { 4 | transform: "perspective(400px)", 5 | opacity: 1, 6 | backfaceVisibility: "visible !important", 7 | offset: 0, 8 | }, 9 | { 10 | transform: "perspective(400px) rotate3d(0, 1, 0, -15deg)", 11 | opacity: 1, 12 | offset: 0.3, 13 | }, 14 | { 15 | transform: "perspective(400px) rotate3d(0, 1, 0, 90deg)", 16 | opacity: 0, 17 | backfaceVisibility: "visible !important", 18 | offset: 1, 19 | }, 20 | ], 21 | animationOptions: { duration: 750, fill: "both" }, 22 | }; 23 | -------------------------------------------------------------------------------- /src/animations/headShake.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | keyframes: [ 3 | { transform: "translateX(0)", offset: 0 }, 4 | { transform: "translateX(-6px) rotateY(-9deg)", offset: 0.065 }, 5 | { transform: "translateX(5px) rotateY(7deg)", offset: 0.185 }, 6 | { transform: "translateX(-3px) rotateY(-5deg)", offset: 0.315 }, 7 | { transform: "translateX(2px) rotateY(3deg)", offset: 0.435 }, 8 | { transform: "translateX(0)", offset: 0.5 }, 9 | { transform: "none", offset: 1 }, 10 | ], 11 | animationOptions: { duration: 1000, fill: "both", easing: "ease-in-out" }, 12 | }; 13 | -------------------------------------------------------------------------------- /src/animations/heartBeat.ts: -------------------------------------------------------------------------------- 1 | const frame1 = { transform: "scale(1)" }; 2 | const frame2 = { transform: "scale(1.3)" }; 3 | 4 | export default { 5 | keyframes: [ 6 | { ...frame1, offset: 0 }, 7 | { ...frame2, offset: 0.14 }, 8 | { ...frame1, offset: 0.28 }, 9 | { ...frame2, offset: 0.42 }, 10 | { ...frame1, offset: 0.7 }, 11 | { transform: "none", offset: 1 }, 12 | ], 13 | animationOptions: { duration: 1300, fill: "both", easing: "ease-in-out" }, 14 | }; 15 | -------------------------------------------------------------------------------- /src/animations/hinge.ts: -------------------------------------------------------------------------------- 1 | const easing = "ease-in-out"; 2 | const transformOrigin = "top left"; 3 | const frame1 = { 4 | transform: "rotate3d(0, 0, 1, 80deg)", 5 | easing, 6 | transformOrigin, 7 | }; 8 | const frame2 = { 9 | transform: "rotate3d(0, 0, 1, 60deg)", 10 | opacity: 1, 11 | easing, 12 | transformOrigin, 13 | }; 14 | 15 | export default { 16 | keyframes: [ 17 | { 18 | transform: "translate3d(0, 0, 0)", 19 | opacity: 1, 20 | easing, 21 | transformOrigin, 22 | offset: 0, 23 | }, 24 | { ...frame1, offset: 0.2 }, 25 | { ...frame2, offset: 0.4 }, 26 | { ...frame1, offset: 0.6 }, 27 | { ...frame2, offset: 0.8 }, 28 | { 29 | transform: "translate3d(0, 700px, 0)", 30 | opacity: 0, 31 | transformOrigin, 32 | offset: 1, 33 | }, 34 | ], 35 | animationOptions: { duration: 2000, fill: "both" }, 36 | }; 37 | -------------------------------------------------------------------------------- /src/animations/index.ts: -------------------------------------------------------------------------------- 1 | export { default as bounce } from "./bounce"; 2 | export { default as flash } from "./flash"; 3 | export { default as pulse } from "./pulse"; 4 | export { default as rubberBand } from "./rubberBand"; 5 | export { default as shakeX } from "./shakeX"; 6 | export { default as shakeY } from "./shakeY"; 7 | export { default as headShake } from "./headShake"; 8 | export { default as swing } from "./swing"; 9 | export { default as tada } from "./tada"; 10 | export { default as wobble } from "./wobble"; 11 | export { default as jello } from "./jello"; 12 | export { default as heartBeat } from "./heartBeat"; 13 | export { default as backInDown } from "./backInDown"; 14 | export { default as backInLeft } from "./backInLeft"; 15 | export { default as backInRight } from "./backInRight"; 16 | export { default as backInUp } from "./backInUp"; 17 | export { default as backOutDown } from "./backOutDown"; 18 | export { default as backOutLeft } from "./backOutLeft"; 19 | export { default as backOutRight } from "./backOutRight"; 20 | export { default as backOutUp } from "./backOutUp"; 21 | export { default as bounceIn } from "./bounceIn"; 22 | export { default as bounceInDown } from "./bounceInDown"; 23 | export { default as bounceInLeft } from "./bounceInLeft"; 24 | export { default as bounceInRight } from "./bounceInRight"; 25 | export { default as bounceInUp } from "./bounceInUp"; 26 | export { default as bounceOut } from "./bounceOut"; 27 | export { default as bounceOutDown } from "./bounceOutDown"; 28 | export { default as bounceOutLeft } from "./bounceOutLeft"; 29 | export { default as bounceOutRight } from "./bounceOutRight"; 30 | export { default as bounceOutUp } from "./bounceOutUp"; 31 | export { default as fadeIn } from "./fadeIn"; 32 | export { default as fadeInDown } from "./fadeInDown"; 33 | export { default as fadeInDownBig } from "./fadeInDownBig"; 34 | export { default as fadeInLeft } from "./fadeInLeft"; 35 | export { default as fadeInLeftBig } from "./fadeInLeftBig"; 36 | export { default as fadeInRight } from "./fadeInRight"; 37 | export { default as fadeInRightBig } from "./fadeInRightBig"; 38 | export { default as fadeInUp } from "./fadeInUp"; 39 | export { default as fadeInUpBig } from "./fadeInUpBig"; 40 | export { default as fadeInTopLeft } from "./fadeInTopLeft"; 41 | export { default as fadeInTopRight } from "./fadeInTopRight"; 42 | export { default as fadeInBottomLeft } from "./fadeInBottomLeft"; 43 | export { default as fadeInBottomRight } from "./fadeInBottomRight"; 44 | export { default as fadeOut } from "./fadeOut"; 45 | export { default as fadeOutDown } from "./fadeOutDown"; 46 | export { default as fadeOutDownBig } from "./fadeOutDownBig"; 47 | export { default as fadeOutLeft } from "./fadeOutLeft"; 48 | export { default as fadeOutLeftBig } from "./fadeOutLeftBig"; 49 | export { default as fadeOutRight } from "./fadeOutRight"; 50 | export { default as fadeOutRightBig } from "./fadeOutRightBig"; 51 | export { default as fadeOutUp } from "./fadeOutUp"; 52 | export { default as fadeOutUpBig } from "./fadeOutUpBig"; 53 | export { default as fadeOutTopLeft } from "./fadeOutTopLeft"; 54 | export { default as fadeOutTopRight } from "./fadeOutTopRight"; 55 | export { default as fadeOutBottomLeft } from "./fadeOutBottomLeft"; 56 | export { default as fadeOutBottomRight } from "./fadeOutBottomRight"; 57 | export { default as flip } from "./flip"; 58 | export { default as flipInX } from "./flipInX"; 59 | export { default as flipInY } from "./flipInY"; 60 | export { default as flipOutX } from "./flipOutX"; 61 | export { default as flipOutY } from "./flipOutY"; 62 | export { default as lightSpeedInRight } from "./lightSpeedInRight"; 63 | export { default as lightSpeedInLeft } from "./lightSpeedInLeft"; 64 | export { default as lightSpeedOutRight } from "./lightSpeedOutRight"; 65 | export { default as lightSpeedOutLeft } from "./lightSpeedOutLeft"; 66 | export { default as rotateIn } from "./rotateIn"; 67 | export { default as rotateInDownLeft } from "./rotateInDownLeft"; 68 | export { default as rotateInDownRight } from "./rotateInDownRight"; 69 | export { default as rotateInUpLeft } from "./rotateInUpLeft"; 70 | export { default as rotateInUpRight } from "./rotateInUpRight"; 71 | export { default as rotateOut } from "./rotateOut"; 72 | export { default as rotateOutDownLeft } from "./rotateOutDownLeft"; 73 | export { default as rotateOutDownRight } from "./rotateOutDownRight"; 74 | export { default as rotateOutUpLeft } from "./rotateOutUpLeft"; 75 | export { default as rotateOutUpRight } from "./rotateOutUpRight"; 76 | export { default as hinge } from "./hinge"; 77 | export { default as jackInTheBox } from "./jackInTheBox"; 78 | export { default as rollIn } from "./rollIn"; 79 | export { default as rollOut } from "./rollOut"; 80 | export { default as zoomIn } from "./zoomIn"; 81 | export { default as zoomInDown } from "./zoomInDown"; 82 | export { default as zoomInLeft } from "./zoomInLeft"; 83 | export { default as zoomInRight } from "./zoomInRight"; 84 | export { default as zoomInUp } from "./zoomInUp"; 85 | export { default as zoomOut } from "./zoomOut"; 86 | export { default as zoomOutDown } from "./zoomOutDown"; 87 | export { default as zoomOutLeft } from "./zoomOutLeft"; 88 | export { default as zoomOutRight } from "./zoomOutRight"; 89 | export { default as zoomOutUp } from "./zoomOutUp"; 90 | export { default as slideInDown } from "./slideInDown"; 91 | export { default as slideInLeft } from "./slideInLeft"; 92 | export { default as slideInRight } from "./slideInRight"; 93 | export { default as slideInUp } from "./slideInUp"; 94 | export { default as slideOutDown } from "./slideOutDown"; 95 | export { default as slideOutLeft } from "./slideOutLeft"; 96 | export { default as slideOutRight } from "./slideOutRight"; 97 | export { default as slideOutUp } from "./slideOutUp"; 98 | -------------------------------------------------------------------------------- /src/animations/jackInTheBox.ts: -------------------------------------------------------------------------------- 1 | const transformOrigin = "center bottom"; 2 | 3 | export default { 4 | keyframes: [ 5 | { 6 | transform: "scale(0.1) rotate(30deg)", 7 | opacity: 0, 8 | transformOrigin, 9 | offset: 0, 10 | }, 11 | { transform: "rotate(-10deg)", transformOrigin, offset: 0.5 }, 12 | { transform: "rotate(3deg)", transformOrigin, offset: 0.7 }, 13 | { transform: "scale(1)", opacity: 1, transformOrigin, offset: 1 }, 14 | ], 15 | animationOptions: { duration: 1000, fill: "both" }, 16 | }; 17 | -------------------------------------------------------------------------------- /src/animations/jello.ts: -------------------------------------------------------------------------------- 1 | const transformOrigin = "center"; 2 | const frame = { transform: "translate3d(0, 0, 0)", transformOrigin }; 3 | 4 | export default { 5 | keyframes: [ 6 | { ...frame, offset: 0 }, 7 | { ...frame, offset: 0.111 }, 8 | { 9 | transform: "skewX(-12.5deg) skewY(-12.5deg)", 10 | transformOrigin, 11 | offset: 0.222, 12 | }, 13 | { 14 | transform: "skewX(6.25deg) skewY(6.25deg)", 15 | transformOrigin, 16 | offset: 0.333, 17 | }, 18 | { 19 | transform: "skewX(-3.125deg) skewY(-3.125deg)", 20 | transformOrigin, 21 | offset: 0.444, 22 | }, 23 | { 24 | transform: "skewX(1.5625deg) skewY(1.5625deg)", 25 | transformOrigin, 26 | offset: 0.555, 27 | }, 28 | { 29 | transform: "skewX(-0.78125deg) skewY(-0.78125deg)", 30 | transformOrigin, 31 | offset: 0.666, 32 | }, 33 | { 34 | transform: "skewX(0.390625deg) skewY(0.390625deg)", 35 | transformOrigin, 36 | offset: 0.777, 37 | }, 38 | { 39 | transform: "skewX(-0.1953125deg) skewY(-0.1953125deg)", 40 | transformOrigin, 41 | offset: 0.888, 42 | }, 43 | { ...frame, offset: 1 }, 44 | ], 45 | animationOptions: { duration: 1000, fill: "both" }, 46 | }; 47 | -------------------------------------------------------------------------------- /src/animations/lightSpeedInLeft.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | keyframes: [ 3 | { 4 | transform: "translate3d(-100%, 0, 0) skewX(30deg)", 5 | opacity: 0, 6 | offset: 0, 7 | }, 8 | { 9 | transform: "skewX(-20deg)", 10 | opacity: 1, 11 | offset: 0.6, 12 | }, 13 | { 14 | transform: "skewX(5deg)", 15 | offset: 0.8, 16 | }, 17 | { 18 | transform: "translate3d(0, 0, 0)", 19 | opacity: 1, 20 | offset: 1, 21 | }, 22 | ], 23 | animationOptions: { duration: 1000, fill: "both", easing: "ease-out" }, 24 | }; 25 | -------------------------------------------------------------------------------- /src/animations/lightSpeedInRight.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | keyframes: [ 3 | { 4 | transform: "translate3d(100%, 0, 0) skewX(-30deg)", 5 | opacity: 0, 6 | offset: 0, 7 | }, 8 | { 9 | transform: "skewX(20deg)", 10 | opacity: 1, 11 | offset: 0.6, 12 | }, 13 | { 14 | transform: "skewX(-5deg)", 15 | offset: 0.8, 16 | }, 17 | { 18 | transform: "translate3d(0, 0, 0)", 19 | opacity: 1, 20 | offset: 1, 21 | }, 22 | ], 23 | animationOptions: { duration: 1000, fill: "both", easing: "ease-out" }, 24 | }; 25 | -------------------------------------------------------------------------------- /src/animations/lightSpeedOutLeft.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | keyframes: [ 3 | { transform: "none", opacity: 1 }, 4 | { transform: "translate3d(-100%, 0, 0) skewX(-30deg)", opacity: 0 }, 5 | ], 6 | animationOptions: { duration: 1000, fill: "both", easing: "ease-in" }, 7 | }; 8 | -------------------------------------------------------------------------------- /src/animations/lightSpeedOutRight.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | keyframes: [ 3 | { transform: "none", opacity: 1 }, 4 | { transform: "translate3d(100%, 0, 0) skewX(30deg)", opacity: 0 }, 5 | ], 6 | animationOptions: { duration: 1000, fill: "both", easing: "ease-in" }, 7 | }; 8 | -------------------------------------------------------------------------------- /src/animations/pulse.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | keyframes: [ 3 | { transform: "scale3d(1, 1, 1)", offset: 0 }, 4 | { transform: "scale3d(1.05, 1.05, 1.05)", offset: 0.5 }, 5 | { transform: "scale3d(1, 1, 1)", offset: 1 }, 6 | ], 7 | animationOptions: { duration: 1000, fill: "both", easing: "ease-in-out" }, 8 | }; 9 | -------------------------------------------------------------------------------- /src/animations/rollIn.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | keyframes: [ 3 | { 4 | transform: "translate3d(-100%, 0, 0) rotate3d(0, 0, 1, -120deg)", 5 | opacity: 0, 6 | }, 7 | { transform: "translate3d(0, 0, 0)", opacity: 1 }, 8 | ], 9 | animationOptions: { duration: 1000, fill: "both" }, 10 | }; 11 | -------------------------------------------------------------------------------- /src/animations/rollOut.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | keyframes: [ 3 | { transform: "none", opacity: 1 }, 4 | { 5 | transform: "translate3d(100%, 0, 0) rotate3d(0, 0, 1, 120deg)", 6 | opacity: 0, 7 | }, 8 | ], 9 | animationOptions: { duration: 1000, fill: "both" }, 10 | }; 11 | -------------------------------------------------------------------------------- /src/animations/rotateIn.ts: -------------------------------------------------------------------------------- 1 | const transformOrigin = "center"; 2 | 3 | export default { 4 | keyframes: [ 5 | { transform: "rotate3d(0, 0, 1, -200deg)", opacity: 0, transformOrigin }, 6 | { transform: "translate3d(0, 0, 0)", opacity: 1, transformOrigin }, 7 | ], 8 | animationOptions: { duration: 1000, fill: "both" }, 9 | }; 10 | -------------------------------------------------------------------------------- /src/animations/rotateInDownLeft.ts: -------------------------------------------------------------------------------- 1 | const transformOrigin = "left bottom"; 2 | 3 | export default { 4 | keyframes: [ 5 | { transform: "rotate3d(0, 0, 1, -45deg)", opacity: 0, transformOrigin }, 6 | { transform: "translate3d(0, 0, 0)", opacity: 1, transformOrigin }, 7 | ], 8 | animationOptions: { duration: 1000, fill: "both" }, 9 | }; 10 | -------------------------------------------------------------------------------- /src/animations/rotateInDownRight.ts: -------------------------------------------------------------------------------- 1 | const transformOrigin = "right bottom"; 2 | 3 | export default { 4 | keyframes: [ 5 | { transform: "rotate3d(0, 0, 1, 45deg)", opacity: 0, transformOrigin }, 6 | { transform: "translate3d(0, 0, 0)", opacity: 1, transformOrigin }, 7 | ], 8 | animationOptions: { duration: 1000, fill: "both" }, 9 | }; 10 | -------------------------------------------------------------------------------- /src/animations/rotateInUpLeft.ts: -------------------------------------------------------------------------------- 1 | const transformOrigin = "left bottom"; 2 | 3 | export default { 4 | keyframes: [ 5 | { transform: "rotate3d(0, 0, 1, 45deg)", opacity: 0, transformOrigin }, 6 | { transform: "translate3d(0, 0, 0)", opacity: 1, transformOrigin }, 7 | ], 8 | animationOptions: { duration: 1000, fill: "both" }, 9 | }; 10 | -------------------------------------------------------------------------------- /src/animations/rotateInUpRight.ts: -------------------------------------------------------------------------------- 1 | const transformOrigin = "right bottom"; 2 | 3 | export default { 4 | keyframes: [ 5 | { transform: "rotate3d(0, 0, 1, -90deg)", opacity: 0, transformOrigin }, 6 | { transform: "translate3d(0, 0, 0)", opacity: 1, transformOrigin }, 7 | ], 8 | animationOptions: { duration: 1000, fill: "both" }, 9 | }; 10 | -------------------------------------------------------------------------------- /src/animations/rotateOut.ts: -------------------------------------------------------------------------------- 1 | const transformOrigin = "center"; 2 | 3 | export default { 4 | keyframes: [ 5 | { transform: "none", opacity: 1, transformOrigin }, 6 | { transform: "rotate3d(0, 0, 1, 200deg)", opacity: 0, transformOrigin }, 7 | ], 8 | animationOptions: { duration: 1000, fill: "both" }, 9 | }; 10 | -------------------------------------------------------------------------------- /src/animations/rotateOutDownLeft.ts: -------------------------------------------------------------------------------- 1 | const transformOrigin = "left bottom"; 2 | 3 | export default { 4 | keyframes: [ 5 | { transform: "none", opacity: 1, transformOrigin }, 6 | { transform: "rotate3d(0, 0, 1, 45deg)", opacity: 0, transformOrigin }, 7 | ], 8 | animationOptions: { duration: 1000, fill: "both" }, 9 | }; 10 | -------------------------------------------------------------------------------- /src/animations/rotateOutDownRight.ts: -------------------------------------------------------------------------------- 1 | const transformOrigin = "right bottom"; 2 | 3 | export default { 4 | keyframes: [ 5 | { transform: "none", opacity: 1, transformOrigin }, 6 | { transform: "rotate3d(0, 0, 1, -45deg)", opacity: 0, transformOrigin }, 7 | ], 8 | animationOptions: { duration: 1000, fill: "both" }, 9 | }; 10 | -------------------------------------------------------------------------------- /src/animations/rotateOutUpLeft.ts: -------------------------------------------------------------------------------- 1 | const transformOrigin = "left bottom"; 2 | 3 | export default { 4 | keyframes: [ 5 | { transform: "none", opacity: 1, transformOrigin }, 6 | { transform: "rotate3d(0, 0, 1, -45deg)", opacity: 0, transformOrigin }, 7 | ], 8 | animationOptions: { duration: 1000, fill: "both" }, 9 | }; 10 | -------------------------------------------------------------------------------- /src/animations/rotateOutUpRight.ts: -------------------------------------------------------------------------------- 1 | const transformOrigin = "right bottom"; 2 | 3 | export default { 4 | keyframes: [ 5 | { transform: "none", opacity: 1, transformOrigin }, 6 | { transform: "rotate3d(0, 0, 1, 90deg)", opacity: 0, transformOrigin }, 7 | ], 8 | animationOptions: { duration: 1000, fill: "both" }, 9 | }; 10 | -------------------------------------------------------------------------------- /src/animations/rubberBand.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | keyframes: [ 3 | { transform: "scale3d(1, 1, 1)", offset: 0 }, 4 | { transform: "scale3d(1.25, 0.75, 1)", offset: 0.3 }, 5 | { transform: "scale3d(0.75, 1.25, 1)", offset: 0.4 }, 6 | { transform: "scale3d(1.15, 0.85, 1)", offset: 0.5 }, 7 | { transform: "scale3d(0.95, 1.05, 1)", offset: 0.65 }, 8 | { transform: "scale3d(1.05, 0.95, 1)", offset: 0.75 }, 9 | { transform: "scale3d(1, 1, 1)", offset: 1 }, 10 | ], 11 | animationOptions: { duration: 1000, fill: "both" }, 12 | }; 13 | -------------------------------------------------------------------------------- /src/animations/shakeX.ts: -------------------------------------------------------------------------------- 1 | const frame1 = { transform: "translate3d(0, 0, 0)" }; 2 | const frame2 = { transform: "translate3d(-10px, 0, 0)" }; 3 | const frame3 = { transform: "translate3d(10px, 0, 0)" }; 4 | 5 | export default { 6 | keyframes: [ 7 | { ...frame1, offset: 0 }, 8 | { ...frame2, offset: 0.1 }, 9 | { ...frame3, offset: 0.2 }, 10 | { ...frame2, offset: 0.3 }, 11 | { ...frame3, offset: 0.4 }, 12 | { ...frame2, offset: 0.5 }, 13 | { ...frame3, offset: 0.6 }, 14 | { ...frame2, offset: 0.7 }, 15 | { ...frame3, offset: 0.8 }, 16 | { ...frame2, offset: 0.9 }, 17 | { ...frame1, offset: 1 }, 18 | ], 19 | animationOptions: { duration: 1000, fill: "both" }, 20 | }; 21 | -------------------------------------------------------------------------------- /src/animations/shakeY.ts: -------------------------------------------------------------------------------- 1 | const frame1 = { transform: "translate3d(0, 0, 0)" }; 2 | const frame2 = { transform: "translate3d(0, -10px, 0)" }; 3 | const frame3 = { transform: "translate3d(0, 10px, 0)" }; 4 | 5 | export default { 6 | keyframes: [ 7 | { ...frame1, offset: 0 }, 8 | { ...frame2, offset: 0.1 }, 9 | { ...frame3, offset: 0.2 }, 10 | { ...frame2, offset: 0.3 }, 11 | { ...frame3, offset: 0.4 }, 12 | { ...frame2, offset: 0.5 }, 13 | { ...frame3, offset: 0.6 }, 14 | { ...frame2, offset: 0.7 }, 15 | { ...frame3, offset: 0.8 }, 16 | { ...frame2, offset: 0.9 }, 17 | { ...frame1, offset: 1 }, 18 | ], 19 | animationOptions: { duration: 1000, fill: "both" }, 20 | }; 21 | -------------------------------------------------------------------------------- /src/animations/slideInDown.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | keyframes: [ 3 | { transform: "translate3d(0, -100%, 0)", visibility: "visible", offset: 0 }, 4 | { transform: "translate3d(0, 0, 0)", visibility: "visible", offset: 1 }, 5 | ], 6 | animationOptions: { duration: 1000, fill: "both" }, 7 | }; 8 | -------------------------------------------------------------------------------- /src/animations/slideInLeft.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | keyframes: [ 3 | { transform: "translate3d(-100%, 0, 0)", visibility: "visible", offset: 0 }, 4 | { transform: "translate3d(0, 0, 0)", visibility: "visible", offset: 1 }, 5 | ], 6 | animationOptions: { duration: 1000, fill: "both" }, 7 | }; 8 | -------------------------------------------------------------------------------- /src/animations/slideInRight.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | keyframes: [ 3 | { transform: "translate3d(100%, 0, 0)", visibility: "visible", offset: 0 }, 4 | { transform: "translate3d(0, 0, 0)", visibility: "visible", offset: 1 }, 5 | ], 6 | animationOptions: { duration: 1000, fill: "both" }, 7 | }; 8 | -------------------------------------------------------------------------------- /src/animations/slideInUp.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | keyframes: [ 3 | { transform: "translate3d(0, 100%, 0)", visibility: "visible", offset: 0 }, 4 | { transform: "translate3d(0, 0, 0)", visibility: "visible", offset: 1 }, 5 | ], 6 | animationOptions: { duration: 1000, fill: "both" }, 7 | }; 8 | -------------------------------------------------------------------------------- /src/animations/slideOutDown.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | keyframes: [ 3 | { transform: "translate3d(0, 0, 0)", visibility: "visible", offset: 0 }, 4 | { transform: "translate3d(0, 100%, 0)", visibility: "hidden", offset: 1 }, 5 | ], 6 | animationOptions: { duration: 1000, fill: "both" }, 7 | }; 8 | -------------------------------------------------------------------------------- /src/animations/slideOutLeft.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | keyframes: [ 3 | { transform: "translate3d(0, 0, 0)", visibility: "visible", offset: 0 }, 4 | { transform: "translate3d(-100%, 0, 0)", visibility: "hidden", offset: 1 }, 5 | ], 6 | animationOptions: { duration: 1000, fill: "both" }, 7 | }; 8 | -------------------------------------------------------------------------------- /src/animations/slideOutRight.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | keyframes: [ 3 | { transform: "translate3d(0, 0, 0)", visibility: "visible", offset: 0 }, 4 | { transform: "translate3d(100%, 0, 0)", visibility: "hidden", offset: 1 }, 5 | ], 6 | animationOptions: { duration: 1000, fill: "both" }, 7 | }; 8 | -------------------------------------------------------------------------------- /src/animations/slideOutUp.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | keyframes: [ 3 | { transform: "translate3d(0, 0, 0)", visibility: "visible", offset: 0 }, 4 | { transform: "translate3d(0, -100%, 0)", visibility: "hidden", offset: 1 }, 5 | ], 6 | animationOptions: { duration: 1000, fill: "both" }, 7 | }; 8 | -------------------------------------------------------------------------------- /src/animations/swing.ts: -------------------------------------------------------------------------------- 1 | const transformOrigin = "top center"; 2 | 3 | export default { 4 | keyframes: [ 5 | { transform: "rotate3d(0, 0, 1, 0deg)", transformOrigin, offset: 0 }, 6 | { transform: "rotate3d(0, 0, 1, 15deg)", transformOrigin, offset: 0.2 }, 7 | { transform: "rotate3d(0, 0, 1, -10deg)", transformOrigin, offset: 0.4 }, 8 | { transform: "rotate3d(0, 0, 1, 5deg)", transformOrigin, offset: 0.6 }, 9 | { transform: "rotate3d(0, 0, 1, -5deg)", transformOrigin, offset: 0.8 }, 10 | { transform: "rotate3d(0, 0, 1, 0deg)", transformOrigin, offset: 1 }, 11 | ], 12 | animationOptions: { duration: 1000, fill: "both" }, 13 | }; 14 | -------------------------------------------------------------------------------- /src/animations/tada.ts: -------------------------------------------------------------------------------- 1 | const frame1 = { transform: "scale3d(1, 1, 1)" }; 2 | const frame2 = { transform: "scale3d(0.9, 0.9, 0.9) rotate3d(0, 0, 1, -3deg)" }; 3 | const frame3 = { transform: "scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, 3deg)" }; 4 | const frame4 = { transform: "scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, -3deg)" }; 5 | 6 | export default { 7 | keyframes: [ 8 | { ...frame1, offset: 0 }, 9 | { ...frame2, offset: 0.1 }, 10 | { ...frame2, offset: 0.2 }, 11 | { ...frame3, offset: 0.3 }, 12 | { ...frame4, offset: 0.4 }, 13 | { ...frame3, offset: 0.5 }, 14 | { ...frame4, offset: 0.6 }, 15 | { ...frame3, offset: 0.7 }, 16 | { ...frame4, offset: 0.8 }, 17 | { ...frame3, offset: 0.9 }, 18 | { ...frame1, offset: 1 }, 19 | ], 20 | animationOptions: { duration: 1000, fill: "both" }, 21 | }; 22 | -------------------------------------------------------------------------------- /src/animations/wobble.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | keyframes: [ 3 | { transform: "translate3d(0, 0, 0)", offset: 0 }, 4 | { 5 | transform: "translate3d(-25%, 0, 0) rotate3d(0, 0, 1, -5deg)", 6 | offset: 0.15, 7 | }, 8 | { 9 | transform: "translate3d(20%, 0, 0) rotate3d(0, 0, 1, 3deg)", 10 | offset: 0.3, 11 | }, 12 | { 13 | transform: "translate3d(-15%, 0, 0) rotate3d(0, 0, 1, -3deg)", 14 | offset: 0.45, 15 | }, 16 | { 17 | transform: "translate3d(10%, 0, 0) rotate3d(0, 0, 1, 2deg)", 18 | offset: 0.6, 19 | }, 20 | { 21 | transform: "translate3d(-5%, 0, 0) rotate3d(0, 0, 1, -1deg)", 22 | offset: 0.75, 23 | }, 24 | { transform: "translate3d(0, 0, 0)", offset: 1 }, 25 | ], 26 | animationOptions: { duration: 1000, fill: "both" }, 27 | }; 28 | -------------------------------------------------------------------------------- /src/animations/zoomIn.ts: -------------------------------------------------------------------------------- 1 | const frame = { transform: "none", opacity: 1 }; 2 | 3 | export default { 4 | keyframes: [ 5 | { transform: "scale3d(0.3, 0.3, 0.3)", opacity: 0, offset: 0 }, 6 | { ...frame, offset: 0.5 }, 7 | { ...frame, offset: 1 }, 8 | ], 9 | animationOptions: { duration: 1000, fill: "both" }, 10 | }; 11 | -------------------------------------------------------------------------------- /src/animations/zoomInDown.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | keyframes: [ 3 | { 4 | transform: "scale3d(0.1, 0.1, 0.1) translate3d(0, -1000px, 0)", 5 | opacity: 0, 6 | easing: "cubic-bezier(0.55, 0.055, 0.675, 0.19)", 7 | offset: 0, 8 | }, 9 | { 10 | transform: "scale3d(0.475, 0.475, 0.475) translate3d(0, 60px, 0)", 11 | opacity: 1, 12 | easing: "cubic-bezier(0.175, 0.885, 0.32, 1)", 13 | offset: 0.6, 14 | }, 15 | { transform: "none", opacity: 1, offset: 1 }, 16 | ], 17 | animationOptions: { duration: 1000, fill: "both" }, 18 | }; 19 | -------------------------------------------------------------------------------- /src/animations/zoomInLeft.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | keyframes: [ 3 | { 4 | transform: "scale3d(0.1, 0.1, 0.1) translate3d(-1000px, 0, 0)", 5 | opacity: 0, 6 | easing: "cubic-bezier(0.55, 0.055, 0.675, 0.19)", 7 | offset: 0, 8 | }, 9 | { 10 | transform: "scale3d(0.475, 0.475, 0.475) translate3d(10px, 0, 0)", 11 | opacity: 1, 12 | easing: "cubic-bezier(0.175, 0.885, 0.32, 1)", 13 | offset: 0.6, 14 | }, 15 | { transform: "none", opacity: 1, offset: 1 }, 16 | ], 17 | animationOptions: { duration: 1000, fill: "both" }, 18 | }; 19 | -------------------------------------------------------------------------------- /src/animations/zoomInRight.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | keyframes: [ 3 | { 4 | transform: "scale3d(0.1, 0.1, 0.1) translate3d(1000px, 0, 0)", 5 | opacity: 0, 6 | easing: "cubic-bezier(0.55, 0.055, 0.675, 0.19)", 7 | offset: 0, 8 | }, 9 | { 10 | transform: "scale3d(0.475, 0.475, 0.475) translate3d(-10px, 0, 0)", 11 | opacity: 1, 12 | easing: "cubic-bezier(0.175, 0.885, 0.32, 1)", 13 | offset: 0.6, 14 | }, 15 | { transform: "none", opacity: 1, offset: 1 }, 16 | ], 17 | animationOptions: { duration: 1000, fill: "both" }, 18 | }; 19 | -------------------------------------------------------------------------------- /src/animations/zoomInUp.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | keyframes: [ 3 | { 4 | transform: "scale3d(0.1, 0.1, 0.1) translate3d(0, 1000px, 0)", 5 | opacity: 0, 6 | easing: "cubic-bezier(0.55, 0.055, 0.675, 0.19)", 7 | offset: 0, 8 | }, 9 | { 10 | transform: "scale3d(0.475, 0.475, 0.475) translate3d(0, -60px, 0)", 11 | opacity: 1, 12 | easing: "cubic-bezier(0.175, 0.885, 0.32, 1)", 13 | offset: 0.6, 14 | }, 15 | { transform: "none", opacity: 1, offset: 1 }, 16 | ], 17 | animationOptions: { duration: 1000, fill: "both" }, 18 | }; 19 | -------------------------------------------------------------------------------- /src/animations/zoomOut.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | keyframes: [ 3 | { transform: "none", opacity: 1, offset: 0 }, 4 | { transform: "scale3d(0.3, 0.3, 0.3)", opacity: 0, offset: 0.5 }, 5 | { transform: "none", opacity: 0, offset: 1 }, 6 | ], 7 | animationOptions: { duration: 1000, fill: "both" }, 8 | }; 9 | -------------------------------------------------------------------------------- /src/animations/zoomOutDown.ts: -------------------------------------------------------------------------------- 1 | const transformOrigin = "center bottom"; 2 | 3 | export default { 4 | keyframes: [ 5 | { transform: "none", opacity: 1, transformOrigin, offset: 0 }, 6 | { 7 | transform: "scale3d(0.475, 0.475, 0.475) translate3d(0, -60px, 0)", 8 | opacity: 1, 9 | easing: "cubic-bezier(0.55, 0.055, 0.675, 0.19)", 10 | transformOrigin, 11 | offset: 0.4, 12 | }, 13 | { 14 | transform: "scale3d(0.1, 0.1, 0.1) translate3d(0, 2000px, 0)", 15 | opacity: 0, 16 | easing: "cubic-bezier(0.175, 0.885, 0.32, 1)", 17 | transformOrigin, 18 | offset: 1, 19 | }, 20 | ], 21 | animationOptions: { duration: 1000, fill: "both" }, 22 | }; 23 | -------------------------------------------------------------------------------- /src/animations/zoomOutLeft.ts: -------------------------------------------------------------------------------- 1 | const transformOrigin = "left center"; 2 | 3 | export default { 4 | keyframes: [ 5 | { transform: "none", opacity: 1, transformOrigin, offset: 0 }, 6 | { 7 | transform: "scale3d(0.475, 0.475, 0.475) translate3d(42px, 0, 0)", 8 | opacity: 1, 9 | transformOrigin, 10 | offset: 0.4, 11 | }, 12 | { 13 | transform: "scale(0.1) translate3d(-2000px, 0, 0)", 14 | opacity: 0, 15 | transformOrigin, 16 | offset: 1, 17 | }, 18 | ], 19 | animationOptions: { duration: 1000, fill: "both" }, 20 | }; 21 | -------------------------------------------------------------------------------- /src/animations/zoomOutRight.ts: -------------------------------------------------------------------------------- 1 | const transformOrigin = "right center"; 2 | 3 | export default { 4 | keyframes: [ 5 | { transform: "none", opacity: 1, transformOrigin, offset: 0 }, 6 | { 7 | transform: "scale3d(0.475, 0.475, 0.475) translate3d(-42px, 0, 0)", 8 | opacity: 1, 9 | transformOrigin, 10 | offset: 0.4, 11 | }, 12 | { 13 | transform: "scale(0.1) translate3d(2000px, 0, 0)", 14 | opacity: 0, 15 | transformOrigin, 16 | offset: 1, 17 | }, 18 | ], 19 | animationOptions: { duration: 1000, fill: "both" }, 20 | }; 21 | -------------------------------------------------------------------------------- /src/animations/zoomOutUp.ts: -------------------------------------------------------------------------------- 1 | const transformOrigin = "center bottom"; 2 | 3 | export default { 4 | keyframes: [ 5 | { transform: "none", opacity: 1, transformOrigin, offset: 0 }, 6 | { 7 | transform: "scale3d(0.475, 0.475, 0.475) translate3d(0, 60px, 0)", 8 | opacity: 1, 9 | easing: "cubic-bezier(0.55, 0.055, 0.675, 0.19)", 10 | transformOrigin, 11 | offset: 0.4, 12 | }, 13 | { 14 | transform: "scale3d(0.1, 0.1, 0.1) translate3d(0, -2000px, 0)", 15 | opacity: 0, 16 | easing: "cubic-bezier(0.175, 0.885, 0.32, 1)", 17 | transformOrigin, 18 | offset: 1, 19 | }, 20 | ], 21 | animationOptions: { duration: 1000, fill: "both" }, 22 | }; 23 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from "./useWebAnimations"; 2 | export * from "./animations"; 3 | -------------------------------------------------------------------------------- /src/use-web-animations.d.ts: -------------------------------------------------------------------------------- 1 | declare module "@wellyshen/use-web-animations" { 2 | import type { RefObject } from "react"; 3 | 4 | export type Keyframes = Keyframe[] | PropertyIndexedKeyframes; 5 | 6 | export type PlayState = AnimationPlayState | undefined; 7 | 8 | type BaseOptions = Partial<{ 9 | id: string; 10 | playbackRate: number; 11 | autoPlay: boolean; 12 | animationOptions: 13 | | number 14 | | (KeyframeAnimationOptions & { pseudoElement?: string }); 15 | }>; 16 | 17 | export type AnimateOptions = BaseOptions & { keyframes: Keyframes }; 18 | 19 | export interface Animate { 20 | (options: AnimateOptions): void; 21 | } 22 | 23 | export interface Event { 24 | playState: PlayState; 25 | animate: Animate; 26 | animation: Animation; 27 | } 28 | 29 | export interface Callback { 30 | (event: Event): void; 31 | } 32 | 33 | export interface Options extends BaseOptions { 34 | ref?: RefObject; 35 | keyframes?: Keyframes; 36 | onReady?: Callback; 37 | onUpdate?: Callback; 38 | onFinish?: Callback; 39 | } 40 | 41 | export interface GetAnimation { 42 | (): Animation | undefined; 43 | } 44 | 45 | export interface Return { 46 | ref: RefObject; 47 | playState: PlayState; 48 | getAnimation: GetAnimation; 49 | animate: Animate; 50 | } 51 | 52 | const useWebAnimations: ( 53 | options?: Options 54 | ) => Return; 55 | 56 | export default useWebAnimations; 57 | 58 | interface Anim { 59 | keyframes: Keyframe[]; 60 | animationOptions: EffectTiming; 61 | } 62 | 63 | export const bounce: Anim; 64 | export const flash: Anim; 65 | export const pulse: Anim; 66 | export const rubberBand: Anim; 67 | export const shakeX: Anim; 68 | export const shakeY: Anim; 69 | export const headShake: Anim; 70 | export const swing: Anim; 71 | export const tada: Anim; 72 | export const wobble: Anim; 73 | export const jello: Anim; 74 | export const heartBeat: Anim; 75 | export const backInDown: Anim; 76 | export const backInLeft: Anim; 77 | export const backInRight: Anim; 78 | export const backInUp: Anim; 79 | export const backOutDown: Anim; 80 | export const backOutLeft: Anim; 81 | export const backOutRight: Anim; 82 | export const backOutUp: Anim; 83 | export const bounceIn: Anim; 84 | export const bounceInDown: Anim; 85 | export const bounceInLeft: Anim; 86 | export const bounceInRight: Anim; 87 | export const bounceInUp: Anim; 88 | export const bounceOut: Anim; 89 | export const bounceOutDown: Anim; 90 | export const bounceOutLeft: Anim; 91 | export const bounceOutRight: Anim; 92 | export const bounceOutUp: Anim; 93 | export const fadeIn: Anim; 94 | export const fadeInDown: Anim; 95 | export const fadeInDownBig: Anim; 96 | export const fadeInLeft: Anim; 97 | export const fadeInLeftBig: Anim; 98 | export const fadeInRight: Anim; 99 | export const fadeInRightBig: Anim; 100 | export const fadeInUp: Anim; 101 | export const fadeInUpBig: Anim; 102 | export const fadeInTopLeft: Anim; 103 | export const fadeInTopRight: Anim; 104 | export const fadeInBottomLeft: Anim; 105 | export const fadeInBottomRight: Anim; 106 | export const fadeOut: Anim; 107 | export const fadeOutDown: Anim; 108 | export const fadeOutDownBig: Anim; 109 | export const fadeOutLeft: Anim; 110 | export const fadeOutLeftBig: Anim; 111 | export const fadeOutRight: Anim; 112 | export const fadeOutRightBig: Anim; 113 | export const fadeOutUp: Anim; 114 | export const fadeOutUpBig: Anim; 115 | export const fadeOutTopLeft: Anim; 116 | export const fadeOutTopRight: Anim; 117 | export const fadeOutBottomLeft: Anim; 118 | export const fadeOutBottomRight: Anim; 119 | export const flip: Anim; 120 | export const flipInX: Anim; 121 | export const flipInY: Anim; 122 | export const flipOutX: Anim; 123 | export const flipOutY: Anim; 124 | export const lightSpeedInRight: Anim; 125 | export const lightSpeedInLeft: Anim; 126 | export const lightSpeedOutRight: Anim; 127 | export const lightSpeedOutLeft: Anim; 128 | export const rotateIn: Anim; 129 | export const rotateInDownLeft: Anim; 130 | export const rotateInDownRight: Anim; 131 | export const rotateInUpLeft: Anim; 132 | export const rotateInUpRight: Anim; 133 | export const rotateOut: Anim; 134 | export const rotateOutDownLeft: Anim; 135 | export const rotateOutDownRight: Anim; 136 | export const rotateOutUpLeft: Anim; 137 | export const rotateOutUpRight: Anim; 138 | export const hinge: Anim; 139 | export const jackInTheBox: Anim; 140 | export const rollIn: Anim; 141 | export const rollOut: Anim; 142 | export const zoomIn: Anim; 143 | export const zoomInDown: Anim; 144 | export const zoomInLeft: Anim; 145 | export const zoomInRight: Anim; 146 | export const zoomInUp: Anim; 147 | export const zoomOut: Anim; 148 | export const zoomOutDown: Anim; 149 | export const zoomOutLeft: Anim; 150 | export const zoomOutRight: Anim; 151 | export const zoomOutUp: Anim; 152 | export const slideInDown: Anim; 153 | export const slideInLeft: Anim; 154 | export const slideInRight: Anim; 155 | export const slideInUp: Anim; 156 | export const slideOutDown: Anim; 157 | export const slideOutLeft: Anim; 158 | export const slideOutRight: Anim; 159 | export const slideOutUp: Anim; 160 | } 161 | -------------------------------------------------------------------------------- /src/useLatest.ts: -------------------------------------------------------------------------------- 1 | import type { RefObject } from "react"; 2 | import { useRef } from "react"; 3 | 4 | export default (val: T): RefObject => { 5 | const ref = useRef(val); 6 | ref.current = val; 7 | return ref; 8 | }; 9 | -------------------------------------------------------------------------------- /src/useWebAnimations.ts: -------------------------------------------------------------------------------- 1 | import type { RefObject } from "react"; 2 | import { useState, useRef, useCallback, useEffect } from "react"; 3 | 4 | import useLatest from "./useLatest"; 5 | 6 | export const polyfillErr = 7 | "💡 use-web-animations: please install polyfill to use this hook. See https://github.com/wellyshen/use-web-animations##use-polyfill"; 8 | export const eventErr = `💡 use-web-animations: the browser doesn't support "onReady" event, please use "onUpdate" to monitor the animation's state instead. See https://github.com/wellyshen/use-web-animations#basic-usage`; 9 | 10 | type Keyframes = Keyframe[] | PropertyIndexedKeyframes; 11 | type PlayState = AnimationPlayState | undefined; 12 | type BaseOptions = Partial<{ 13 | id: string; 14 | playbackRate: number; 15 | autoPlay: boolean; 16 | animationOptions: 17 | | number 18 | | (KeyframeAnimationOptions & { pseudoElement?: string }); 19 | }>; 20 | interface Animate { 21 | (options: BaseOptions & { keyframes: Keyframes }): void; 22 | } 23 | interface Callback { 24 | (event: { 25 | playState: PlayState; 26 | animate: Animate; 27 | animation: Animation; 28 | }): void; 29 | } 30 | export interface Options extends BaseOptions { 31 | ref?: RefObject; 32 | keyframes?: Keyframes; 33 | onReady?: Callback; 34 | onUpdate?: Callback; 35 | onFinish?: Callback; 36 | } 37 | interface Return { 38 | ref: RefObject; 39 | playState: PlayState; 40 | getAnimation: () => Animation | undefined; 41 | animate: Animate; 42 | } 43 | 44 | const useWebAnimations = ({ 45 | ref: refOpt, 46 | id, 47 | playbackRate, 48 | autoPlay, 49 | keyframes, 50 | animationOptions, 51 | onReady, 52 | onUpdate, 53 | onFinish, 54 | }: Options = {}): Return => { 55 | const [playState, setPlayState] = useState(); 56 | const hasUnmountedRef = useRef(false); 57 | const animRef = useRef(); 58 | const prevPendingRef = useRef(); 59 | const prevPlayStateRef = useRef(); 60 | const keyframesRef = useRef(keyframes); 61 | const animationOptionsRef = useRef(animationOptions); 62 | const onReadyRef = useLatest(onReady); 63 | const onUpdateRef = useLatest(onUpdate); 64 | const onFinishRef = useLatest(onFinish); 65 | const refVar = useRef(null); 66 | const ref = refOpt || refVar; 67 | 68 | const getAnimation = useCallback(() => animRef.current, []); 69 | 70 | const animate: Animate = useCallback( 71 | (args) => { 72 | if (!ref.current || !args.keyframes) return; 73 | if (!ref.current.animate) { 74 | console.error(polyfillErr); 75 | return; 76 | } 77 | 78 | animRef.current = ref.current.animate( 79 | args.keyframes, 80 | args.animationOptions 81 | ); 82 | const { current: anim } = animRef; 83 | 84 | if (args.autoPlay === false) anim.pause(); 85 | if (args.id) anim.id = args.id; 86 | if (args.playbackRate) anim.playbackRate = args.playbackRate; 87 | 88 | if (onReadyRef.current) { 89 | if (anim.ready) { 90 | // eslint-disable-next-line promise/catch-or-return, promise/always-return 91 | anim.ready.then((animation) => { 92 | // @ts-ignore 93 | onReadyRef.current({ 94 | playState: animation.playState, 95 | animate, 96 | animation, 97 | }); 98 | }); 99 | } else { 100 | console.error(eventErr); 101 | } 102 | } 103 | 104 | if (onFinishRef.current) { 105 | anim.onfinish = (e) => { 106 | const animation = e.target as Animation; 107 | 108 | if (!hasUnmountedRef.current) { 109 | // @ts-ignore 110 | onFinishRef.current({ 111 | playState: animation.playState, 112 | animate, 113 | animation, 114 | }); 115 | } 116 | }; 117 | } 118 | 119 | prevPlayStateRef.current = undefined; 120 | }, 121 | [onFinishRef, onReadyRef, ref] 122 | ); 123 | 124 | useEffect(() => { 125 | if (keyframesRef.current) 126 | animate({ 127 | id, 128 | playbackRate, 129 | autoPlay, 130 | keyframes: keyframesRef.current, 131 | animationOptions: animationOptionsRef.current, 132 | }); 133 | }, [animate, autoPlay, id, playbackRate]); 134 | 135 | useEffect(() => { 136 | let rafId: number; 137 | 138 | const update = () => { 139 | const animation = getAnimation(); 140 | 141 | if (animation) { 142 | const { pending, playState: curPlayState } = animation; 143 | 144 | if (prevPlayStateRef.current !== curPlayState) 145 | setPlayState(curPlayState); 146 | 147 | if ( 148 | onUpdateRef.current && 149 | (prevPendingRef.current !== pending || 150 | prevPlayStateRef.current !== curPlayState || 151 | curPlayState === "running") 152 | ) 153 | onUpdateRef.current({ playState: curPlayState, animate, animation }); 154 | 155 | prevPendingRef.current = pending; 156 | prevPlayStateRef.current = curPlayState; 157 | } 158 | 159 | rafId = requestAnimationFrame(update); 160 | }; 161 | 162 | rafId = requestAnimationFrame(update); 163 | 164 | return () => { 165 | cancelAnimationFrame(rafId); 166 | 167 | hasUnmountedRef.current = true; 168 | getAnimation()?.finish(); 169 | getAnimation()?.cancel(); 170 | }; 171 | }, [animate, getAnimation, onUpdateRef]); 172 | 173 | return { ref, playState, getAnimation, animate }; 174 | }; 175 | 176 | export default useWebAnimations; 177 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "sourceMap": true, 4 | "target": "ESNext", 5 | "module": "ESNext", 6 | "lib": ["dom", "dom.iterable", "esnext"], 7 | "jsx": "react-jsx", 8 | // Specify module resolution strategy: "node" (Node.js) or "classic" (TypeScript pre-1.6) 9 | "moduleResolution": "node", 10 | // Ensure that Babel can safely transpile files in the TypeScript project 11 | "isolatedModules": true, 12 | // Enable all strict type-checking options. Recommended by TS 13 | "strict": true, 14 | // Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. 15 | // Implies "allowSyntheticDefaultImports". Recommended by TS 16 | "esModuleInterop": true, 17 | // Skip type checking of declaration files. Recommended by TS 18 | "skipLibCheck": true, 19 | // Disallow inconsistently-cased references to the same file. Recommended by TS 20 | "forceConsistentCasingInFileNames": true, 21 | // Do not emit outputs 22 | "noEmit": true, 23 | // Raise error on expressions and declarations with an implied "any" type 24 | "noImplicitAny": true, 25 | // Report errors on unused locals 26 | "noUnusedLocals": true, 27 | // Report errors on unused parameters 28 | "noUnusedParameters": true, 29 | // Report error when not all code paths in function return a value 30 | "noImplicitReturns": true, 31 | // Report errors for fallthrough cases in switch statement 32 | "noFallthroughCasesInSwitch": true 33 | }, 34 | "include": ["src"] 35 | } 36 | --------------------------------------------------------------------------------