Skip to content

Instantly share code, notes, and snippets.

@philipcdavis
Last active January 9, 2020 20:46
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save philipcdavis/7526aab13bc2cb08ec4ac7384087546e to your computer and use it in GitHub Desktop.
Save philipcdavis/7526aab13bc2cb08ec4ac7384087546e to your computer and use it in GitHub Desktop.
Rocket Progress Indicator

A rocket progress indicator made using svg + canvas.

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Quest</title>
<link rel="stylesheet" href="style.css"/>
<link rel="stylesheet" href="https://unpkg.com/tachyons@4.7.0/css/tachyons.min.css"/>
</head>
<body class="sans-serif dark-red-bg">
<div class="m0">
<div class="mw8">
<div class="fl w100 w-third">
<div id="canvas01" class="spaceship-container">
<svg id="rocket-flames" width="11px" height="38px" viewBox="0 0 11 38">
<g id="flames">
<polygon id="rocket-flame-light" fill="#9D0000" opacity="0.642266757" points="5.5 0.71875 11 38 0 38"></polygon>
<polygon id="rocket-flame-dark" class="pulse pulse-delay" fill="#FF2020" opacity="0.813462409" points="5.5 0.71875 9 38 2 38"></polygon>
</g>
</svg>
<svg id="rocket-container" width="11px" height="55px" viewBox="0 0 11 55">
<g id="rocket">
<polygon class="flame-move" id="Path-2" fill="#FFB914" points="4.33113325 30.9988397 5.52162438 54.7288392 6.61158311 30.9988397"></polygon>
<polygon id="Rectangle-8" fill="#86240C" points="3 29 8 29 7.66344736 31 3.38228707 31"></polygon>
<path d="M3.29538202,24.0004897 C3.29538202,24.0004897 0.724655534,25.40952 0.222970933,26.512686 C-0.278713667,27.6158519 0.222970933,30.756486 0.222970933,30.756486 C0.222970933,30.756486 1.07942895,29.7177413 1.64769101,29.2611171 C2.21595307,28.8044929 3.29538202,28.287665 3.29538202,28.287665 L3.29538202,24.0004897 Z" id="Path-2-Copy-3" fill="#546B7C"></path>
<path d="M10.9475559,24.0004897 C10.9475559,24.0004897 8.37682945,25.40952 7.87514485,26.512686 C7.37346025,27.6158519 7.87514485,30.756486 7.87514485,30.756486 C7.87514485,30.756486 8.73160286,29.7177413 9.29986492,29.2611171 C9.86812698,28.8044929 10.9475559,28.287665 10.9475559,28.287665 L10.9475559,24.0004897 Z" id="Path-2-Copy-4" fill="#546B7C" transform="translate(9.299865, 27.378488) scale(-1, 1) translate(-9.299865, -27.378488) "></path>
<path d="M3.04746713,29.546875 C2.3947312,26.5484437 1.98939014,22.2246713 1.98939014,17.0412693 C1.98939014,6.84589693 5.49202694,0.232183562 5.49202694,0.232183562 C5.49202694,0.232183562 8.99466373,6.84589693 8.99466373,17.0412693 C8.99466373,22.2246713 8.58932267,26.5484437 7.93658674,29.546875 L3.04746713,29.546875 Z" id="Combined-Shape-Copy" fill="#FFFFFF"></path>
</g>
</svg>
</div>
</div>
<div class="fl w-100 w-two-thirds dt">
<div class="dtc v-mid white" style="height: 496px;">
<div class="menlo hot-red hot-red-text-glow b">Rocket Progress Bar</div>
<p class="menlo white o-80">Click the button below to generate random data values</p>
<button class="btn btn-red b pointer pull-right mt1" onclick="spaceship()">To Infinity</button>
</div>
</div>
</div>
</div>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="spaceship.js"></script>
<script src="stars.js"></script>
<script>
var height = 400;
var width = 200;
var padding = {top: 40, bottom: 61, right: 20, left: 20 };
var selector = "#canvas";
generateStars({
height: height * 2,
width: width * 2,
padding: padding,
selector: selector + "01"
});
var spaceship = generateSpaceship({
height: height,
width: width,
padding: padding,
selector: selector + "01"
})
</script>
</body>
</html>
function generateSpaceship(config) {
var data = {
current: 0,
total: 20000
};
var height = config.height;
var width = config.width;
var rocketHeight = 55;
var rocketWidth = 11;
var padding = config.padding;
var heightPadded = height + padding.top + padding.bottom;
var widthPadded = width + padding.left + padding.right;
var yScale = d3.scaleLinear()
.domain([0, data.total])
.range([0, height])
var axisScale = d3.scaleLinear()
.domain([0, data.total])
.range([height, 0])
var flameWidthScale = d3.scaleLinear()
.domain([0, data.total])
.range([1, 4])
var labelColorScale = d3.scaleLinear().domain([0,4]).range([d3.rgb("#FF2525"), d3.rgb('#FFFFFF')]);
var rocket = d3.select(config.selector + " #rocket-container")
.attr("style", "position: absolute; left: calc(50% - 5px); bottom: 0;");
var rocketFlame = d3.select(config.selector + " #rocket-flames")
.attr("style", "position: absolute; left: calc(50% - 5px); bottom: 0; ");
var numberAxis = d3.axisRight(axisScale)
.ticks(5, "s");
var circleAxis = d3.axisRight(axisScale)
.ticks(60)
.tickFormat("");
d3.select(config.selector)
.attr("style", "width:" + widthPadded + "px; height: "+ heightPadded +"px;")
d3.select(config.selector)
.append("svg")
.attr("class", "axisSvg")
.attr("width", width + padding.left + padding.right)
.attr("height", height + padding.top + padding.bottom)
.append("g")
.attr("transform", "translate("+ padding.left +","+ padding.top +")")
.attr("class", "number-axis")
.call(numberAxis)
.append("g")
.attr("class", "circle-axis")
.call(circleAxis);
var numberTicks = d3.selectAll(".number-axis .tick");
numberTicks
.each(function(d, i) {
d3.select(this).select("text")
.attr("fill", labelColorScale(i));
})
var circleTicks = d3.selectAll(config.selector + " .circle-axis .tick");
circleTicks.each(function() {
d3.select(this)
.append("circle")
.attr("r", 3)
.attr("fill", "rgba(142,52,52,0.4)");
});
d3.selectAll("line").remove();
d3.selectAll(".domain").remove();
function updateRocket() {
data.current = Math.random() * 20000;
var flameCurrent = yScale(data.current) + 50;
circleTicks.selectAll(config.selector + " circle")
.transition()
.duration(1000)
.attr("fill", function(d) {
return d <= data.current ? "#FF2020" : "rgba(142,52,52,0.4)"
});
var flameContainer = rocketFlame.transition()
.duration(1000)
.attr("height", flameCurrent)
.attr("style", "position: absolute; left: calc(50% - 5px); bottom: 0; transform: scaleX(" + flameWidthScale(data.current) + ") ")
.attr("viewBox", "0 0 11 " + flameCurrent);
flameContainer.select(config.selector + " #rocket-flame-light")
.attr("points", "5.5 0.71875 11 " + flameCurrent + " 0 " + flameCurrent + "");
flameContainer.select(config.selector + " #rocket-flame-dark")
.attr("points", "5.5 0.71875 9 " + flameCurrent + " 2 " + flameCurrent + "");
rocket.transition()
.duration(1000)
.attr("style", "position: absolute; left: calc(50% - 5px); bottom: "+ yScale(data.current)+"; ");
}
setTimeout(function() {
updateRocket()
}, 500)
return updateRocket;
}
function generateStars(config) {
var height = (config.height + config.padding.top + config.padding.bottom) * 2;
var width = (config.width + config.padding.left + config.padding.right) * 2;
var x = d3.scaleLinear()
.domain([0, 100])
.range([0, config.width]);
var y = d3.scaleLinear()
.domain([0, 100])
.range([0, config.height]);
var colors = ["#9E5555", "#FF7F7F", "#FFDADA"];
var colorScale = d3.scaleQuantize().domain([0,1]).range(colors);
var starData = d3.range(80).map(function() {
var random = Math.random()
var dataObject = {
x: Math.random() * 100,
y: Math.random() * 100,
yv: random < 0.90 ? random / 10 : random,
size: (random * 3),
color: colorScale(random)
};
return dataObject;
});
var canvas = d3.select(config.selector).insert("canvas", ":first-child")
.attr("width", config.width)
.attr("height", config.height)
.attr("id", "star-canvas");
// node returns first dom element in a selection
var context = canvas.node().getContext("2d");
d3.timer(function() {
// context.clearRect(0, 0, width, height);
context.fillStyle = 'rgba(0,0,0,0.5)';
context.fillRect(0, 0, config.width, config.height);
starData.forEach(function(d) {
d.y += d.yv;
// Recycle old circles
if(d.y > (100 + d.size) ) {
d.y = 0 - d.size;
d.x = Math.random() * 100;
}
context.fillStyle = d.color;
context.beginPath();
context.arc(x(d.x), y(d.y), d.size, 0, 2 * Math.PI);
context.fill();
});
});
}
.hot-red {
color: #FF2020;
}
.hot-red-text-glow {
text-shadow: 0 0 3px rgba(255,32,32, 0.5);
}
.dark-red-bg {
background-color: #201818;
}
.red-border {
border: 1px solid #FF2020;
}
.ddc {
font-family: "DDC", sans-serif;
}
.menlo {
font-family: Menlo, monospace;
}
.btn {
border-radius: 20px;
font-size: 14px;
padding: 10px 25px;
text-decoration: none;
outline: none;
}
.btn-red {
background-color: #FF2020;
color: white;
box-shadow: 0 0 7px #FF2020;
border: none;
}
.bb-red {
border-bottom: 1px solid #FF2020;
}
.bt-red {
border-top: 1px solid #FF2020;
}
.spaceship-container {
background-image: linear-gradient(0deg, #630202 -40%, rgba(68,42,42,0.1) 100%);
position: relative;
border-bottom: 1px solid #FF2020;
border-right: 1px solid #FF2020;
}
@keyframes pulse {
0% {
opacity: 0.3;
}
100% {
opacity: 0.5;
}
}
@keyframes twitch {
0% {
transform: translateY(-20px);
}
100% {
transform: translateY(0);
}
}
canvas {
width: 100%;
height: 100%;
position: absolute;
z-index: -1;
}
.pulse {
animation-duration: 1s;
animation-name: pulse;
animation-iteration-count: infinite;
animation-direction: alternate;
}
.flame-move {
animation-duration: 1s;
animation-name: twitch;
animation-iteration-count: infinite;
animation-direction: alternate;
}
.tick text {
font-weight: bold;
font-family: "Menlo", monospace;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment