├── assets └── favicon.png ├── css ├── countdown.css ├── github.css └── main.css ├── index.html ├── readme.md └── js └── index.js /assets/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LucaCuello/CircularCountdownTemplate/HEAD/assets/favicon.png -------------------------------------------------------------------------------- /css/countdown.css: -------------------------------------------------------------------------------- 1 | @keyframes progress { 2 | 100% { 3 | stroke-dashoffset: 1256.12939453125; 4 | } 5 | } -------------------------------------------------------------------------------- /css/github.css: -------------------------------------------------------------------------------- 1 | .github { 2 | position: absolute; 3 | font-size: 40px; 4 | bottom: 10px; 5 | transition: transform 300ms ease-in-out; 6 | } 7 | 8 | .github a { 9 | color: #dcbeee; 10 | } 11 | 12 | .github:hover { 13 | transform: scale(1.1); 14 | transition: transform 300ms ease-in-out; 15 | } -------------------------------------------------------------------------------- /css/main.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css2?family=Lato:ital,wght@0,300;0,400;0,700;0,900;1,300;1,400;1,700;1,900&display=swap'); 2 | @import url(github.css); 3 | 4 | * { 5 | margin: 0; 6 | padding: 0; 7 | box-sizing: border-box; 8 | } 9 | 10 | html { 11 | font-family: 'Lato', sans-serif; 12 | } 13 | 14 | body { 15 | position: relative; 16 | display: flex; 17 | justify-content: center; 18 | align-items: center; 19 | height: 100vh; 20 | width: 100%; 21 | background-color: rgb(19, 16, 16); 22 | } -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Circular countdown 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Circular countdown template 2 | 3 | ## About 4 | 5 | I was coding a pomodoro app and wanted to make a circle countdown but couldn't find a simple way to do it, there were some tutorials but not exactly what I wanted. I made this quick template hoping to help people in a similar situation. 6 | 7 | ## Deploy 8 | 9 | [Go to the deploy](https://circularcountdownlivetest.vercel.app/). 10 | 11 | ## Installation 12 | 13 | A simple and fast way to get started is to include this script on the bottom of your **body** tag: 14 | 15 | ```javascript 16 | 17 | ``` 18 | 19 | And this CSS link between your **head** tag: 20 | 21 | ```javascript 22 | 23 | ``` 24 | 25 | ## Usage 26 | 27 | Once installed, you'll need to paste this template inside your HTML: 28 | 29 | ```javascript 30 |
38 | ``` 39 | 40 | You can **edit** the datasets in order to make a custom countdown. 41 | 42 | - **Data-duration:** sets the duration of the countdown. 43 | 44 | - **Data-transition:** sets the transition of the countdown animation. Examples: "linear", "ease", "ease-in", "ease-out, "ease-in-out". 45 | 46 | - **Data-color:** sets the color of the circle. Accepts any valid color code. Examples: "#c39fe0", "rgb(195, 159, 224)", "hsl(273deg 51% 75%)". 47 | 48 | - **Data-size:** sets the size of the circle. "500" would make a 500x500px circular countdown. 49 | 50 | - **Data-Position:** sets the position of the countdown. Examples: "rigth", "left", "up", "down". 51 | 52 | ## Examples 53 | 54 | large 55 | large 56 | 57 | medium 58 | medium 59 | 60 | small 61 | small 62 | -------------------------------------------------------------------------------- /js/index.js: -------------------------------------------------------------------------------- 1 | // Injecting the countdown into HTML document 2 | 3 | const countdownContainer = document.querySelector(".countdown-container"); 4 | 5 | countdownContainer.innerHTML = ` 6 | 7 | 8 | 9 | 10 | `; 11 | countdownContainer.style.position = "relative"; 12 | 13 | const span = document.querySelector(".seconds"); 14 | 15 | span.style.position = "absolute"; 16 | span.style.color = "#e8deee"; 17 | span.style.fontWeight = "900"; 18 | span.style.top = "50%"; 19 | span.style.left = "50%"; 20 | span.style.transform = "translate(-50%, -50%)"; 21 | 22 | const progressWrapper = document.getElementById("progress-wrapper"), 23 | progress = document.getElementById("progress"), 24 | timeSpan = document.getElementById("seconds"); 25 | 26 | // Countdown functions 27 | 28 | const options = { 29 | duration: +countdownContainer.dataset.duration, 30 | transition: countdownContainer.dataset.transition, 31 | color: countdownContainer.dataset.color, 32 | size: +countdownContainer.dataset.size, 33 | initialPosition: countdownContainer.dataset.position, 34 | }; 35 | 36 | const circularCountdown = ({ 37 | duration, 38 | transition, 39 | color, 40 | size, 41 | initialPosition, 42 | }) => { 43 | // Rendering countdown on HTML 44 | renderSeconds(duration); 45 | // Adjusting timer font-size depending of countdown size 46 | adjustFontSize(size); 47 | // Adjusting circular countdown size 48 | adjustCircleSize(size); 49 | // Setting initial position of countdown 50 | setInitialPosition(initialPosition); 51 | // Starting animation (setting transition, color and duration) 52 | animationStart(color, transition, duration); 53 | }; 54 | 55 | const renderSeconds = (duration) => { 56 | timeSpan.innerHTML = duration; 57 | const secondsCountdown = setInterval(() => { 58 | duration--; 59 | timeSpan.innerHTML = duration; 60 | if (duration === 0) { 61 | clearInterval(secondsCountdown); 62 | timeSpan.innerHTML = ``; 63 | } 64 | }, 1000); 65 | }; 66 | 67 | const adjustFontSize = (size) => { 68 | timeSpan.style.fontSize = `${size / 5}px`; 69 | }; 70 | 71 | const adjustCircleSize = (size) => { 72 | progressWrapper.style.width = size; 73 | progressWrapper.style.height = size; 74 | }; 75 | 76 | const setInitialPosition = (initialPosition) => { 77 | if (initialPosition === "up") { 78 | progressWrapper.style.transform = "rotate(270deg)"; 79 | } else if (initialPosition === "left") { 80 | progressWrapper.style.transform = "rotate(180deg)"; 81 | } else if (initialPosition === "down") { 82 | progressWrapper.style.transform = "rotate(90deg)"; 83 | } 84 | }; 85 | 86 | const animationStart = (color, transition, duration) => { 87 | let length = progress.getTotalLength(); 88 | progress.style.stroke = color; 89 | progressWrapper.style.strokeDasharray = length; 90 | progressWrapper.style.animation = `progress ${transition} ${duration}s forwards`; 91 | }; 92 | 93 | const initCountdown = () => { 94 | circularCountdown(options); 95 | }; 96 | 97 | initCountdown(); 98 | --------------------------------------------------------------------------------