Created
November 20, 2019 04:15
-
-
Save b0c0de/405771c0c0f4a755f6f2dfd2ae41bd1b to your computer and use it in GitHub Desktop.
Pomodoro Clock by BoCode
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
.container | |
.header | |
%svg#gradient{"style" => "width: auto;"} | |
%defs | |
%linearGradient#linearGradient{ "x1"=>"0%", "y1"=>"0%", "x2"=>"100%", "y2"=>"100%" } | |
%stop{ "offset"=>"0%", "stop-color"=>"#B8D087" } | |
%stop{ "offset"=>"100%", "stop-color" =>"#00996D" } | |
%text{"y"=>"36px", "fill-opacity"=>"1"} | |
Pomodoro Clock | |
.timer | |
.spinner-container | |
.spinner-mask | |
%svg.spinner{"width" => "240px", "height" => "240px", "viewBox" => "0 0 66 66", "xmlns" => "http://www.w3.org/2000/svg"} | |
%circle.path#spineroo{"fill" => "none", "stroke-width" => ".5", "stroke-cap" => "round", "cx" => "33", "cy" => "33", "r" => "30"} | |
.outer-circle | |
.inner-circle | |
.timer-display | |
%svg#gradient | |
%defs | |
%linearGradient#linearGradient{ "x1" => "0%", "y1" => "0%", "x2" => "100%", "y2" => "100%" } | |
%stop{ "offset" => "0%", "stop-color" => "#B8D087" } | |
%stop{ "offset" => "100%", "stop-color" =>"#00996D" } | |
%text#timer-display-time{"y"=>"36px", "fill-opacity"=>"1"} | |
25:00 | |
#status | |
#action-title | |
%small SET ME UP | |
.actions | |
.set-timer | |
session length | |
.set-display | |
%span#set-timer-display 25 | |
min | |
.minus-add | |
.setting-button#minus-timer - | |
.setting-button#add-timer + | |
.set-break | |
break length | |
.set-display | |
%span#set-break-display 5 | |
min | |
.minus-add | |
.setting-button#minus-break - | |
.setting-button#add-break + | |
.start#start-timer START ME UP ! | |
.start#reset-timer.hidden STOP !!! | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//progress bars overlay | |
const radius = 90, | |
padding = 50, | |
radians = 2 * Math.PI; | |
const dimension = (2 * radius) + (2 * padding), | |
points = 50, percentage = 0.62; | |
const angle = d3.scale.linear() | |
.domain([0, points-1]) | |
.range([0, radians]); | |
const line = d3.svg.line.radial() | |
.interpolate("basis") | |
.tension(0) | |
.radius(radius) | |
.angle((d, i) => { | |
if(i < (points*percentage +1)) { | |
return angle(i); | |
} | |
}); | |
const svg = d3.select(".outer-circle").append("svg") | |
.attr("width", dimension) | |
.attr("height", 200) | |
.append("g"); | |
svg.append("path").datum(d3.range(points)) | |
.attr("class", "line") | |
.attr("fill", "none") | |
.attr("stroke-dasharray", "7 3") | |
.attr("stroke-width", "25px") | |
.attr("stroke", "#1F2025") //#1F2025 | |
.attr("d", line) | |
.attr("transform", "translate(135,106) rotate(-110)"); | |
//progress bars | |
let elapsedPercent = 0; | |
const r = 100; | |
const pi = Math.PI; | |
const green1 = "#B8D087"; | |
const green2 = "#00996D"; | |
const data = { | |
upper: calcPercent(0), | |
lower: calcPercent(elapsedPercent) | |
}; | |
let progress = 0; | |
function calcPercent(percent) { | |
return [percent, 100-percent]; | |
} | |
const canvas = d3.select(".inner-circle").append("svg") | |
.attr("height", r + 100) | |
.attr("width", r + 150); | |
const group = canvas.append("g") | |
.attr("transform", "translate("+120+","+110+")"); | |
const arc = d3.svg.arc() | |
.innerRadius(r/1.2) | |
.outerRadius(r); | |
//returns objects based on data | |
const pie = d3.layout.pie() | |
.sort(null) | |
.value(data => data) | |
.startAngle(-110 * (pi/180)) | |
.endAngle(110 * (pi/180)); | |
const arcs = group.selectAll(".arc") | |
.data(pie(data.lower)) | |
.enter() | |
.append("g") | |
.attr("class", "arc"); | |
const defs = canvas.append("defs") | |
.append("linearGradient") | |
.attr("id", "greenGradient") | |
.attr("gradientUnits", "objectBoundingBox") | |
.attr("x1", "0") | |
.attr("y1", "0") | |
.attr("x2", "1") | |
.attr("y2", "1") | |
defs.append("stop").attr("offset", "0%").attr("stop-color", green1); | |
defs.append("stop").attr("offset", "100%").attr("stop-color", green2); | |
let path = arcs.append("path") | |
.attr("class", (data, index) => "progress-color" + index ) | |
.attr("d", arc); | |
/////////timer | |
const startButton = document.getElementById("start-timer"); | |
const resetButton = document.getElementById("reset-timer"); | |
const timeDisplay = document.getElementById("timer-display-time"); | |
const statusDisplay = document.getElementById("status"); | |
const spinner = document.getElementById("spineroo"); | |
let sessionTimer = parseInt(document.getElementById("set-timer-display").innerHTML, 10); | |
let breakTimer = parseInt(document.getElementById("set-break-display").innerHTML, 10); | |
elapsedPercent = 0; | |
let seconds = 60; | |
let minutes; | |
let timer; | |
let breakTime = false; | |
//start timer | |
startButton.addEventListener('click', () =>{ | |
timer = setInterval(timerFn,1000); | |
//console.log("button: " + startButton.innerHTML.trim() + " | session: " + sessionTimer + " | break: " + breakTimer); | |
startButton.classList.toggle("hidden"); | |
resetButton.classList.toggle("hidden"); | |
spinner.classList.toggle("spinning"); | |
statusDisplay.innerHTML = "In session!"; | |
minutes = sessionTimer-1; | |
function timerFn(){ | |
seconds--; | |
//console.log("time: " +minutes + ":" + seconds + " | percent: " + elapsedPercent); | |
progress(); | |
if (seconds < 10 && minutes < 10) { | |
timeDisplay.innerHTML = "0" +minutes + ":0" + seconds; | |
}else if(seconds < 10){ | |
timeDisplay.innerHTML = minutes + ":0" + seconds; | |
}else if(minutes < 10){ | |
timeDisplay.innerHTML = "0" + minutes + ":" + seconds; | |
}else{ | |
timeDisplay.innerHTML = minutes + ":" + seconds; | |
} | |
if(seconds === 0 && minutes === 0){ | |
if(breakTime === false){ | |
breakTime = true; | |
minutes = breakTimer-1; | |
seconds = 60; | |
elapsedPercent = 0; | |
statusDisplay.innerHTML = "Take a break!"; | |
console.log("break time"); | |
}else{ | |
breakTime = false; | |
minutes = sessionTimer-1; | |
seconds = 60; | |
elapsedPercent = 0; | |
statusDisplay.innerHTML = "In session!"; | |
console.log("work time"); | |
} | |
} | |
if(seconds === 0) { | |
minutes--; | |
seconds = 60; | |
} | |
}; | |
function progress(){ | |
let secondsElapsed = minutes*60 + seconds; | |
let totalSeconds; | |
if(breakTime === true){ | |
totalSeconds = breakTimer*60; | |
}else{ | |
totalSeconds = sessionTimer*60; | |
} | |
elapsedPercent = (1 - secondsElapsed/totalSeconds)*100; | |
path | |
.data(pie(calcPercent(elapsedPercent))) | |
.attr("d", arc); | |
} | |
}); | |
//reset timer | |
resetButton.addEventListener('click', () =>{ | |
startButton.classList.toggle("hidden"); | |
resetButton.classList.toggle("hidden"); | |
spinner.classList.toggle("spinning"); | |
clearInterval(timer); | |
seconds = 60; | |
minutes = sessionTimer -1; | |
elapsedPercent = 0; | |
timeDisplay.innerHTML = sessionTimer + ":00"; | |
statusDisplay.innerHTML = "Reset!"; | |
console.log("reset") | |
path | |
.data(pie(calcPercent(elapsedPercent))) | |
.attr("d", arc); | |
}); | |
//timer settings | |
const settingOptions = { | |
"add-break": ()=>{ | |
if(breakTimer < 51){ | |
let timer = breakTimer++; | |
document.getElementById("set-break-display").innerHTML = timer + 1; | |
} | |
}, | |
"minus-break": ()=>{ | |
if(breakTimer > 5){ | |
let timer = breakTimer--; | |
document.getElementById("set-break-display").innerHTML = timer - 1; | |
} | |
}, | |
"add-timer": ()=>{ | |
if(sessionTimer < 99){ | |
let timer = sessionTimer++; | |
document.getElementById("set-timer-display").innerHTML = timer + 1; | |
document.getElementById("timer-display-time").innerHTML = (timer + 1) + ":00"; | |
} | |
}, | |
"minus-timer": ()=>{ | |
if(sessionTimer > 25){ | |
let timer = sessionTimer--; | |
document.getElementById("set-timer-display").innerHTML = timer - 1; | |
document.getElementById("timer-display-time").innerHTML = (timer - 1) + ":00"; | |
} | |
} | |
}; | |
const buttonSettings = document.getElementsByClassName("setting-button"); | |
for(let i = 0; i < buttonSettings.length; i++){ | |
buttonSettings[i].addEventListener("click", ()=> { | |
let operation = buttonSettings[i].getAttribute("id"); | |
settingOptions[operation](); | |
startButton.classList.remove("hidden"); | |
resetButton.classList.add("hidden"); | |
spinner.classList.remove("spinning"); | |
clearInterval(timer); | |
seconds = 60; | |
minutes = sessionTimer -1; | |
elapsedPercent = 0; | |
console.log("setting up"); | |
}) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.12/d3.min.js"></script> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@import url(https://fonts.googleapis.com/css?family=Oxygen|Rubik:300); | |
$base-font: 'Rubik', Arial, sans-serif; | |
$base-font-size: 12px; | |
$white: #c4c4c5; | |
$black: #2f2f33; | |
$drk-gray: #121316; | |
$lt-gray: #2f313a; | |
$lt-green: #2c3120; | |
$green: #000000; | |
@mixin bg-gradient { | |
background: linear-gradient(135deg, $lt-green, $green); | |
} | |
@mixin shadow { | |
box-shadow: 0 16px 28px 0 rgba(0,0,0,.8),0 25px 55px 0 rgba(0,0,0,0.21); | |
} | |
html, | |
body { | |
width: 100%; | |
height: 100%; | |
box-sizing: border-box; | |
font-family: $base-font; | |
font-size: $base-font-size; | |
letter-spacing: $base-font-size/20; | |
} | |
*, *:before, *:after { | |
box-sizing: inherit; | |
} | |
body { | |
@include bg-gradient; | |
display: flex; | |
align-items: center; | |
justify-content: center; | |
flex-direction: column; | |
user-select: none; | |
} | |
.hidden { | |
display: none; | |
} | |
.container { | |
@include shadow; | |
width: 290px; | |
height: 420px; | |
background-color: $drk-gray; | |
color: $white; | |
} | |
.header { | |
text-align: center; | |
padding: 0 20px 20px 30%; | |
font-size: $base-font-size*1.3; | |
border-bottom: solid $black 1px; | |
} | |
.timer { | |
height: 200px; | |
display: flex; | |
align-items: center; | |
justify-content: center; | |
flex-direction: row; | |
} | |
.timer-display { | |
color: $lt-green; | |
font-size: $base-font-size*3; | |
margin-top: 60px; | |
margin-left: 10px; | |
text-align: center; | |
} | |
svg#gradient { | |
width: 100px; | |
height: 40px; | |
vertical-align: bottom; | |
pointer-events: none; | |
text { | |
fill: url(#linearGradient); | |
} | |
} | |
#status { | |
text-align: center; | |
height: 20px; | |
margin-top: -30px; | |
margin-bottom: 30px; | |
color: lighten($lt-gray, 20%); | |
} | |
.start { | |
@include bg-gradient; | |
@include shadow; | |
width: 290px; | |
text-align: center; | |
color: white; | |
padding: 20px 20px; | |
cursor: pointer; | |
&:hover { | |
background: $green; | |
} | |
&:active { | |
@include bg-gradient; | |
} | |
} | |
.inner-circle { | |
position: absolute; | |
top: 50%; | |
left: 50%; | |
transform: translate(-48%, -73.5%); | |
} | |
.outer-circle { | |
position: absolute; | |
top: 50%; | |
left: 50%; | |
transform: translate(-48%, -73.5%); | |
z-index: 1; | |
} | |
//progress bar styles | |
.progress-color0 { | |
fill: url(#greenGradient); | |
} | |
.progress-color1 { | |
fill: $lt-gray; | |
} | |
#action-title { | |
position: absolute; | |
font-size: $base-font-size/1.5; | |
left: 50%; | |
margin-top: 0; | |
transform: translate(-50%,-50%); | |
background: $drk-gray; | |
padding: 5px 20px; | |
border-radius: 20px; | |
border: solid lighten($lt-gray, 20%) 1px; | |
} | |
.actions { | |
display: flex; | |
align-items: center; | |
text-align: center; | |
color: lighten($lt-gray, 20%); | |
div { | |
padding-bottom: 0; | |
flex: 1; | |
} | |
} | |
.set-timer, | |
.set-break { | |
padding-top: 20px; | |
border: solid $black 1px; | |
} | |
.set-timer { | |
border-right: none; | |
border-left: none; | |
} | |
.set-break { | |
border-right: none; | |
} | |
.set-display { | |
color: $white; | |
font-size: $base-font-size*1.4; | |
padding-top: 10px; | |
} | |
.actions { | |
.minus-add { | |
font-size: $base-font-size*1.5; | |
display: flex; | |
padding: 0; | |
justify-content: center; | |
div { | |
cursor: pointer; | |
margin: 1px; | |
padding: 5px; | |
margin-bottom: 10px; | |
flex: .5; | |
&:hover { | |
color: $white; | |
} | |
&:active { | |
color: $lt-gray; | |
} | |
} | |
} | |
} | |
.spinner-container { | |
position: absolute; | |
z-index: 2; | |
left: 50%; | |
top: 50%; | |
margin-top: -40px; | |
transform: translate(-50%, -50%); | |
} | |
.spinner-mask { | |
position: absolute; | |
background-color: $drk-gray; | |
height: 45px; | |
width: 50px; | |
margin-top: 181px; | |
left: 200px; | |
transform: translate(-50%,-50%); | |
z-index: 2; | |
} | |
$offset: 187; | |
$duration: 1.2s; | |
.spinner { | |
transform: rotate(150deg); | |
animation: rotator $duration linear infinite; | |
} | |
@keyframes rotator { | |
0% { transform: rotate(150deg); } | |
20% { transform: rotate(200deg); } | |
100% { transform: rotate(390deg); } | |
} | |
.spinning { | |
stroke-dasharray: $offset; | |
stroke-dashoffset: 185; | |
transform-origin: center; | |
animation: | |
dash $duration infinite, | |
colors $duration infinite; | |
} | |
@keyframes colors { | |
0% { stroke: $lt-green; } | |
50% { stroke: $green; } | |
100% { stroke: $lt-green; } | |
} | |
@keyframes dash { | |
0% { | |
stroke-dashoffset: 185; | |
} | |
50% { | |
stroke-dashoffset: 175; | |
} | |
100% { | |
stroke-dashoffset: 185; | |
} | |
// 0% { stroke-dashoffset: $offset; } | |
// 50% { | |
// stroke-dashoffset: $offset/1.2; | |
// } | |
// 100% { | |
// stroke-dashoffset: $offset; | |
// } | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment