├── src ├── react-app-env.d.ts ├── setupTests.ts ├── app │ ├── hooks │ │ ├── useCanvas.ts │ │ ├── useResponsiveSize.ts │ │ └── useColor.ts │ ├── index.tsx │ ├── components │ │ ├── canvas.tsx │ │ └── wave.tsx │ └── entity │ │ └── WaveEntity.ts ├── reportWebVitals.ts ├── index.tsx └── index.css ├── public ├── favicon.ico ├── robots.txt ├── manifest.json └── index.html ├── .github ├── FUNDING.yml └── workflows │ └── gh-pages-deployment.yml ├── .gitignore ├── tsconfig.json ├── README.md ├── LICENSE └── package.json /src/react-app-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ashiishme/react-sine-wave/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [ashiishme] 4 | custom: ['https://www.buymeacoffee.com/ashiishme'] 5 | -------------------------------------------------------------------------------- /src/setupTests.ts: -------------------------------------------------------------------------------- 1 | // jest-dom adds custom jest matchers for asserting on DOM nodes. 2 | // allows you to do things like: 3 | // expect(element).toHaveTextContent(/react/i) 4 | // learn more: https://github.com/testing-library/jest-dom 5 | import '@testing-library/jest-dom' 6 | -------------------------------------------------------------------------------- /src/app/hooks/useCanvas.ts: -------------------------------------------------------------------------------- 1 | import { createContext, useContext } from 'react' 2 | 3 | export const CanvasContext = createContext<{ 4 | context: CanvasRenderingContext2D | undefined 5 | }>({ 6 | context: undefined, 7 | }) 8 | 9 | export const useCanvasContext = () => { 10 | return useContext(CanvasContext) 11 | } 12 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /src/reportWebVitals.ts: -------------------------------------------------------------------------------- 1 | import { ReportHandler } from 'web-vitals' 2 | 3 | const reportWebVitals = (onPerfEntry?: ReportHandler) => { 4 | if (onPerfEntry && onPerfEntry instanceof Function) { 5 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { 6 | getCLS(onPerfEntry) 7 | getFID(onPerfEntry) 8 | getFCP(onPerfEntry) 9 | getLCP(onPerfEntry) 10 | getTTFB(onPerfEntry) 11 | }) 12 | } 13 | } 14 | 15 | export default reportWebVitals 16 | -------------------------------------------------------------------------------- /src/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import './index.css' 4 | import App from 'app' 5 | import reportWebVitals from './reportWebVitals' 6 | 7 | ReactDOM.render( 8 | 9 | 10 | , 11 | document.getElementById('root') 12 | ) 13 | 14 | // If you want to start measuring performance in your app, pass a function 15 | // to log results (for example: reportWebVitals(console.log)) 16 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals 17 | reportWebVitals() 18 | -------------------------------------------------------------------------------- /src/app/hooks/useResponsiveSize.ts: -------------------------------------------------------------------------------- 1 | import { useCallback, useEffect, useState } from 'react' 2 | 3 | const useResponsiveSize = () => { 4 | const [width, setWidth] = useState(0) 5 | const [height, setHeight] = useState(0) 6 | 7 | const setSizes = useCallback(() => { 8 | setWidth(window.innerWidth) 9 | setHeight(window.innerHeight) 10 | }, [setWidth, setHeight]) 11 | 12 | useEffect(() => { 13 | window.addEventListener('resize', setSizes) 14 | setSizes() 15 | }, [setSizes]) 16 | 17 | return { width, height } 18 | } 19 | 20 | export default useResponsiveSize 21 | -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 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 | -------------------------------------------------------------------------------- /src/app/index.tsx: -------------------------------------------------------------------------------- 1 | import Canvas from 'app/components/canvas' 2 | 3 | function App() { 4 | return ( 5 |
6 |
7 |

React Sine Wave

8 |

9 | 14 | [ Find project on GitHub ] 15 | 16 |

17 |
18 | 19 | 20 |
21 | ) 22 | } 23 | 24 | export default App 25 | -------------------------------------------------------------------------------- /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 | "baseUrl": "src" 19 | }, 20 | "include": ["src"] 21 | } 22 | -------------------------------------------------------------------------------- /src/app/hooks/useColor.ts: -------------------------------------------------------------------------------- 1 | const useColor = () => { 2 | const randomInteger = () => Math.floor(Math.random() * 180) 3 | 4 | const generateColor = () => { 5 | const red = randomInteger() 6 | const green = randomInteger() 7 | const blue = randomInteger() 8 | const primary = `rgba(${red}, ${green}, ${blue}, 0.88)` 9 | const secondary = `rgba(${red}, ${green}, ${blue}, 0.48)` 10 | const bodyBackgroundColor = `rgba(${red}, ${green}, ${blue}, 0.28)` 11 | // TODO: remove it from color generator 12 | document.body.style.backgroundColor = bodyBackgroundColor 13 | return { 14 | frontWave: primary, 15 | backWave: secondary, 16 | backgroundColor: bodyBackgroundColor, 17 | } 18 | } 19 | 20 | return { 21 | generateColor, 22 | } 23 | } 24 | 25 | export default useColor 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | ashiishme react sinve wave logo built with react, typescript and canvas API 3 |

React Sine Wave

4 |

Sine wave animation with random colors using React & Typescript with Canvas API :heart_eyes:.

5 |
6 | 7 | # Demo 8 | 9 | Deployed URL: 10 | 11 | 12 | 13 | # Installation 14 | 15 | ```bash 16 | git clone https://github.com/ashiishme/react-sine-wave.git 17 | cd react-sine-wave 18 | yarn install 19 | ``` 20 | 21 | # Start the server 22 | 23 | ```bash 24 | yarn start 25 | ``` 26 | 27 | Enjoy the wave animation at: 28 | -------------------------------------------------------------------------------- /src/app/components/canvas.tsx: -------------------------------------------------------------------------------- 1 | import { useRef, FC, useEffect, useState } from 'react' 2 | 3 | import { CanvasContext } from 'app/hooks/useCanvas' 4 | import useResponsiveSize from 'app/hooks/useResponsiveSize' 5 | import Wave from './wave' 6 | 7 | const Canvas: FC = () => { 8 | const canvasRef = useRef(null) 9 | const { width, height } = useResponsiveSize() 10 | 11 | const [context, setContext] = useState() 12 | 13 | useEffect(() => { 14 | const ctx = canvasRef?.current?.getContext('2d') 15 | if (ctx) setContext(ctx) 16 | }, []) 17 | 18 | return ( 19 | <> 20 | 21 | 27 | 28 | 29 | 30 | ) 31 | } 32 | 33 | export default Canvas 34 | -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | * { 2 | margin: 0; 3 | padding: 0; 4 | } 5 | 6 | html, 7 | body { 8 | height: 100%; 9 | width: 100%; 10 | } 11 | 12 | body { 13 | margin: 0; 14 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 15 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 16 | sans-serif; 17 | -webkit-font-smoothing: antialiased; 18 | -moz-osx-font-smoothing: grayscale; 19 | background: #ffffff; 20 | position: relative; 21 | } 22 | 23 | code { 24 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 25 | monospace; 26 | } 27 | 28 | canvas { 29 | position: absolute; 30 | width: 100%; 31 | height: 100%; 32 | } 33 | 34 | .header-info { 35 | position: absolute; 36 | top: 10%; 37 | left: 50%; 38 | transform: translateX(-50%); 39 | text-align: center; 40 | z-index: 1; 41 | } 42 | 43 | .app-title { 44 | font-size: 2rem; 45 | color: #212121; 46 | } 47 | 48 | .project-source { 49 | margin-top: 0.5rem; 50 | } 51 | 52 | .project-source a { 53 | color: #212121; 54 | text-decoration: none; 55 | } 56 | -------------------------------------------------------------------------------- /src/app/entity/WaveEntity.ts: -------------------------------------------------------------------------------- 1 | class WaveEntity { 2 | private waveLength: number[] 3 | private color: string 4 | 5 | constructor(waveLength: number[], color: string) { 6 | this.waveLength = waveLength 7 | this.color = color 8 | } 9 | 10 | public set waveColor(color: string) { 11 | this.color = color 12 | } 13 | 14 | public draw = ( 15 | context: CanvasRenderingContext2D, 16 | width: number, 17 | height: number, 18 | frequency: number 19 | ): void => { 20 | context.beginPath() 21 | context.moveTo(0, height) 22 | if (this.waveLength.length < 3) { 23 | return 24 | } 25 | for (let i = 0; i < width; i++) { 26 | let wave1 = Math.sin(i * this.waveLength[0] - frequency) 27 | let wave2 = Math.sin(i * this.waveLength[1] - frequency) 28 | let wave3 = Math.sin(i * this.waveLength[2] - frequency) 29 | 30 | context.lineTo(i * 2.5, height - 400 + wave1 * wave2 * wave3 * 200) 31 | } 32 | context.lineTo(width, height) 33 | context.fillStyle = this.color 34 | context.fill() 35 | context.closePath() 36 | } 37 | } 38 | 39 | export default WaveEntity 40 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Ashish Yadav 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 | -------------------------------------------------------------------------------- /src/app/components/wave.tsx: -------------------------------------------------------------------------------- 1 | import { FC } from 'react' 2 | import { useCanvasContext } from 'app/hooks/useCanvas' 3 | import useResponsiveSize from 'app/hooks/useResponsiveSize' 4 | import WaveEntity from 'app/entity/WaveEntity' 5 | import useColor from 'app/hooks/useColor' 6 | 7 | const Wave: FC = () => { 8 | const { context } = useCanvasContext() 9 | const { width, height } = useResponsiveSize() 10 | const { generateColor } = useColor() 11 | 12 | let frequency = 0.013 13 | let colors: { [key: string]: string } = generateColor() 14 | let timer = 1 15 | const waves = { 16 | frontWave: new WaveEntity([0.0211, 0.028, 0.015], 'rgba(255,179,0,0.88)'), 17 | backWave: new WaveEntity([0.0122, 0.018, 0.005], 'rgba(255,179,0,0.48)'), 18 | } 19 | 20 | const render = () => { 21 | context?.clearRect(0, 0, width, height) 22 | Object.entries(waves).forEach(([waveName, wave]) => { 23 | wave.waveColor = colors[waveName] 24 | wave.draw(context!, width, height, frequency) 25 | }) 26 | if (timer === 500) { 27 | colors = generateColor() 28 | timer = 1 29 | } 30 | timer++ 31 | frequency += 0.013 32 | requestAnimationFrame(render) 33 | } 34 | if (context) render() 35 | return null 36 | } 37 | 38 | export default Wave 39 | -------------------------------------------------------------------------------- /.github/workflows/gh-pages-deployment.yml: -------------------------------------------------------------------------------- 1 | # This is a basic workflow to help you get started with Actions 2 | 3 | name: Deploy React Sine Wave 4 | 5 | # Controls when the action will run. 6 | on: 7 | # Triggers the workflow on push or pull request events but only for the main branch 8 | push: 9 | branches: [ main ] 10 | 11 | # Allows you to run this workflow manually from the Actions tab 12 | workflow_dispatch: 13 | 14 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel 15 | jobs: 16 | # This workflow contains a single job called "build" 17 | build: 18 | # The type of runner that the job will run on 19 | runs-on: ubuntu-latest 20 | 21 | # Steps represent a sequence of tasks that will be executed as part of the job 22 | steps: 23 | # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it 24 | - uses: actions/checkout@v2 25 | 26 | # Runs a single command using the runners shell 27 | - name: Install and Build 28 | run: | 29 | yarn install 30 | yarn build 31 | 32 | # Runs a set of commands using the runners shell 33 | - name: Deploy 34 | uses: JamesIves/github-pages-deploy-action@4.1.3 35 | with: 36 | 37 | branch: gh-pages 38 | folder: build 39 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-sine-wave", 3 | "homepage": "https://ashiishme.github.io/react-sine-wave/", 4 | "version": "0.1.0", 5 | "private": true, 6 | "dependencies": { 7 | "@testing-library/jest-dom": "^5.11.4", 8 | "@testing-library/react": "^11.1.0", 9 | "@testing-library/user-event": "^12.1.10", 10 | "@types/jest": "^26.0.15", 11 | "@types/node": "^12.0.0", 12 | "@types/react": "^17.0.0", 13 | "@types/react-dom": "^17.0.0", 14 | "react": "^17.0.2", 15 | "react-dom": "^17.0.2", 16 | "react-scripts": "4.0.3", 17 | "typescript": "^4.1.2", 18 | "web-vitals": "^1.0.1" 19 | }, 20 | "scripts": { 21 | "predeploy": "yarn build", 22 | "deploy": "gh-pages -d build", 23 | "start": "react-scripts start", 24 | "build": "react-scripts build", 25 | "test": "react-scripts test", 26 | "eject": "react-scripts eject" 27 | }, 28 | "eslintConfig": { 29 | "extends": [ 30 | "react-app", 31 | "react-app/jest" 32 | ] 33 | }, 34 | "browserslist": { 35 | "production": [ 36 | ">0.2%", 37 | "not dead", 38 | "not op_mini all" 39 | ], 40 | "development": [ 41 | "last 1 chrome version", 42 | "last 1 firefox version", 43 | "last 1 safari version" 44 | ] 45 | }, 46 | "devDependencies": { 47 | "gh-pages": "^3.1.0" 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | React Sine Wave 28 | 29 | 30 | 31 |
32 | 42 | 43 | 44 | --------------------------------------------------------------------------------