<div class="wrapper">
<div class="figure">
<div class="rect"></div>
<div class="vertical fixed"></div>
<div class="diagonal"></div>
<div class="circle"></div>
<div class="topline"></div>
<div class="bottomline"></div>
<div class="vertical"></div>
<div class="percent zero">0%</div>
<div class="percent fifty">50%</div>
<div class="percent hundred">100%</div>
<svg class="arc" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 160 160">
<path class="arc__arc" id="arc1" fill="none" stroke="#fff" stroke-width="10" opacity="0.5"/>
<rect id="arcAngleNumber" x="60" y="60" width="24" height="10" fill="#fff" rx="5" opacity="0.5" />
<text id="arcAngleNumberText" text-anchor="middle" x="60" y="110" fill="#003">A°</text>
</svg>
</div>
<div class="controls">
<h2 class="controls__headline">
Playground:
</h2>
<div class="angle-control">
<input type="range"
id="angle-control"
min="0"
max="360"
step="0.5"
value="20"
list="angles">
<span id="angle-result" class="result">20.0 deg</span>
</div>
<datalist id="angles">
<!--
<option value="45">
<option value="63.5">
-->
<option value="90">
<!--
<option value="116.5">
<option value="135">
-->
<option value="180">
<!--
<option value="225">
<option value="243.5">
-->
<option value="270">
<!--
<option value="296.5">
<option value="315">
-->
</datalist>
</div>
</div>
:root {
--angle: 20deg;
--width: min(880px, 80vw, 60vh);
--side: var(--width);
--top: var(--width);
--diagonal-angle: 45deg;
--length: 20px;
--circle-multiplier: 0.707;
--circle-width: calc(var(--circle-multiplier) * var(--width));
@media (min-aspect-ratio: 1/1) {
--width: min(880px, 80vw, 90vh);
--top: var(--width);
--side: calc(var(--width) / 2);
--diagonal-angle: 63.5deg;
--circle-multiplier: 0.559;
}
}
* {
box-sizing: border-box;
}
body {
background: #003;
overflow-x: hidden;
color: #fff;
font-family: 'Raleway', sans-serif;
min-height: 100vh;
margin-bottom: 4em;
}
.wrapper {
min-height: calc(100vh - 4em);
display: flex;
flex-direction: column;
justify-content: space-between;
overflow: hidden;
margin-bottom: 2vh;
padding-top: 100px
}
.figure {
width: var(--top);
height: var(--side);
position: relative;
z-index: 2;
margin: 1em auto 8em;
order: 1;
@media screen and (min-width: 30em) {
order: 0;
}
}
.rect {
width: 100%;
height: 100%;
background: #f09;
background-image:
linear-gradient(var(--angle),
#f09 0%,
#3023AE 50%,
#0ff 100%);
border: 1px solid #fff;
}
.vertical {
width: 2px;
height: var(--length);
background: #fff;
position: absolute;
left: calc(50% - 1px);
top: calc(50% - (var(--length) / 2));
transform: rotate(var(--angle));
&:before,
&:after {
content: "";
position: absolute;
width: 0.75em;
height: 0.75em;
background: #0ff;
border: 2px solid #fff;
border-radius: 50%;
transform: translate(-50%, -50%);
top: 0;
left: 0;
box-shadow: 0 0 0.5em #000;
}
&:after {
top: 100%;
background: #f09;
}
&.fixed {
transform: none;
background: none;
border-left: 1px dashed #fff;
height: calc(var(--side) * 1.4 / 2);
top: calc(50% - (var(--side) * 0.7));
top: 50%;
&:before,
&:after {
display: none;
}
}
}
.diagonal {
width: 1px;
height: calc(var(--width) * var(--circle-multiplier) * 2.2);
border-right: 1px dashed #fff;
position: absolute;
left: calc(50% - 1px);
top: calc(50% - (var(--width) * var(--circle-multiplier) * 1.1));
transform: rotate(var(--diagonal-angle));
transition: transform 0.5s ease-out;
.second &,
.fourth & {
transform: rotate(calc(180deg - var(--diagonal-angle)));
}
}
.circle {
width: calc(var(--width) * 0.559);
width: var(--circle-width);
height: calc(var(--width) * 0.559);
height: var(--circle-width);
position: absolute;
right: 0px;
top: calc(var(--circle-width) / -2);
border: 1px dashed #fff;
border-radius: 50%;
transform-origin: right center;
transform: rotate( calc((90deg - var(--diagonal-angle)) * -1) );
transition: top 0.5s ease-out, right 0.5s ease-out, transform-origin 0.5s ease-out, transform 0.5s ease-out;
.second & {
top: calc(var(--side) - (var(--circle-width) / 2));
transform: rotate( calc((90deg - var(--diagonal-angle))) );
}
.third & {
top: calc(var(--side) - (var(--circle-width) / 2));
right: calc(var(--top) - var(--circle-width));
transform: rotate( calc((90deg - var(--diagonal-angle)) * -1) );
transform-origin: left center;
}
.fourth & {
right: calc(var(--top) - var(--circle-width));
transform-origin: left center;
transform: rotate( calc((90deg - var(--diagonal-angle)) * 1) );
}
}
.topline,
.bottomline {
width: calc(var(--width) * 1.125) ;
height: 2px;
border-bottom: 2px solid #0ff;
position: absolute;
left: calc(100% - ((var(--width) * 1.125) / 2));
top: 0;
transform: rotate(var(--angle));
transition: top 0.3s ease-out, left 0.3s ease-out;
}
.topline {
.second & {
top: calc(100% - 2px);
}
.third & {
top: calc(100% - 2px);
left: calc( -1 * ((var(--width) * 1.125) / 2) );
}
.fourth & {
left: calc( -1 * ((var(--width) * 1.125) / 2) );
top: 0;
}
}
.bottomline {
top: calc(100% - 2px);
left: calc( -1 * ((var(--width) * 1.125) / 2) );
border-color: #f09;
.second & {
top: 0;
}
.third & {
left: calc(100% - ((var(--width) * 1.125) / 2));
top: 0;
}
.fourth & {
left: calc(100% - ((var(--width) * 1.125) / 2));
top: calc(100% - 2px);
}
}
.percent {
position: absolute;
background: #fff;
color: #003;
padding: 0.25em 0.5em;
line-height: 1;
border-radius: 2em;
font-weight: 700;
font-size: 0.875em;
transition: top 0.4s ease-out, bottom 0.4s ease-out, left 0.4s ease-out, right 0.4s ease-out;
border: 2px solid #fff;
box-shadow: 0 0 0.25em rgba(0,0,0,0.5);
}
.zero {
left: 0;
bottom: 0;
transform: translate(-50%, 50%);
background: #f09;
color: #fff;
.second &,
.third & {
bottom: 100%;
}
.third &,
.fourth & {
left: 100%;
}
}
.fifty {
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
background: #3023AE;
color: #fff;
}
.hundred {
right: 0;
top: 0;
transform: translate(50%, -50%);
background: #0ff;
.second &,
.third & {
top: 100%;
}
.third &,
.fourth & {
right: 100%;
}
}
.arc {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
z-index: 40;
width: calc(var(--width) / 2);
height: calc(var(--width) / 2);
&__arc {
transform-origin: 80px 80px;
transform: rotate(180deg);
}
text {
font-size: 6px;
font-family: 'Raleway', sans-serif;
font-weight: 700;
}
}
/* ---------------------------------
Interactive Controls
--------------------------------- */
.controls {
font-size: Min(calc(0.5em + 2vw), 1em);
color: #003;
background: rgba(255,255,255,0.9);
z-index: 5;
border-radius: 0.25em;
width: Min(var(--width), 34em);
max-width: 90%;
margin: 0 auto;
&__headline {
color: #003;
margin: 0.75em 1.125em 0.625em;
font-size: 1em;
text-align: center;
font-weight: 700;
}
}
.angle-control {
padding: 0.5em 0 0.75em;
margin: 0 1em;
font-size: 1em;
border-top: 2px solid #003;
display: flex;
> * {
vertical-align: middle;
}
span {
display: inline-block;
min-width: 8ch;
margin-left: 1em;
font-variant-numeric: tabular-nums;
}
input {
width: 8em;
flex-shrink: 1;
@media screen and (min-width: 30em) {
width: 21em;
flex-grow: 1;
}
}
}
.result {
text-align: right;
font-weight: 700;
}
[hidden] {
display: none;
}
footer {
position: fixed;
bottom: 0;
left: 0;
width: 100%;
text-align: center;
background: #004;
z-index: 10;
border-top: 1px solid rgba(255,255,255,0.1);
p {
font-size: 0.875em;
font-weight: 700;
margin: 0.75em;
}
a {
color: #fff;
text-decoration-color: #f09;
&:hover {
color: #f09;
}
}
}
View Compiled
const angleRange = document.getElementById('angle-control');
const angleResult = document.getElementById('angle-result');
const root = document.querySelector('HTML');
angleRange.addEventListener("input", function() {
// Show current angle
angleResult.innerHTML = parseFloat(angleRange.value).toFixed(1) + " deg";
// Update custom property
document.documentElement.style.setProperty('--angle', angleRange.value + 'deg');
// draw the arc describing the current angle
let arcAngle = angleRange.value;
if (arcAngle == 360) { arcAngle = 359.9; }
document.getElementById("arc1").setAttribute("d", describeArc(80, 80, 50, 0, arcAngle));
// Position the Angle Number
let arcNumber = document.getElementById("arcAngleNumber");
let arcNumberText = document.getElementById("arcAngleNumberText");
let numberAngle = angleRange.value / 2;
let numberPosition = polarToCartesian(80, 80, 30, numberAngle + 180);
arcNumber.setAttribute("x", numberPosition.x - 12);
arcNumber.setAttribute("y", numberPosition.y - 5);
arcNumberText.textContent = (angleRange.value + "°");
arcNumberText.setAttribute("x", numberPosition.x);
arcNumberText.setAttribute("y", numberPosition.y + 2);
// Apply classes to root depending on current quadrant
if (angleRange.value > 90 && angleRange.value <= 180) {
root.className = 'second';
}
else if (angleRange.value > 180 && angleRange.value <= 270 ) {
root.className = 'third';
}
else if (angleRange.value > 270 && angleRange.value <= 360 ) {
root.className = 'fourth';
}
else {
root.className = '';
}
// Update Length of the line showing the gradient axis
getLength();
}, false);
function getLength() {
const bodyStyles = window.getComputedStyle(document.body);
const diagonalAngle = parseFloat(bodyStyles.getPropertyValue('--diagonal-angle'), 10);
// calculate the Hypotenuse
const rect = document.querySelector('.rect');
const rectWidth = Math.max(rect.offsetWidth, rect.offsetHeight);
const circleMultiplier = bodyStyles.getPropertyValue('--circle-multiplier');
const c = rectWidth * circleMultiplier;
// calculate alpha depending on the current quadrant
let angle = angleRange.value;
let quadrant = parseInt(angle / 90);
let alpha = Math.abs(diagonalAngle - (angle - 90 * quadrant));
if (quadrant % 2 !== 0) {
alpha = Math.abs(90 - diagonalAngle - (angle - 90 * quadrant));
}
// calculate adjacent leg
const alphaRadians = alpha * Math.PI / 180.0;
const adjacentLeg = Math.cos(alphaRadians) * c;
// update custom property
document.documentElement.style.setProperty('--length', (adjacentLeg * 2) + 'px');
}
// Stuff to draw the arc for the current Angle
function polarToCartesian(centerX, centerY, radius, angleInDegrees) {
var angleInRadians = (angleInDegrees-90) * Math.PI / 180.0;
return {
x: centerX + (radius * Math.cos(angleInRadians)),
y: centerY + (radius * Math.sin(angleInRadians))
};
}
function describeArc(x, y, radius, startAngle, endAngle){
var start = polarToCartesian(x, y, radius, endAngle);
var end = polarToCartesian(x, y, radius, startAngle);
var largeArcFlag = endAngle - startAngle <= 180 ? "0" : "1";
var d = [
"M", start.x, start.y,
"A", radius, radius, 0, largeArcFlag, 0, end.x, end.y
].join(" ");
return d;
}
// Initial call
document.getElementById("arc1").setAttribute("d", describeArc(80, 80, 50, 0, 20));
// Position the Angle Number
let arcNumber = document.getElementById("arcAngleNumber");
let arcNumberText = document.getElementById("arcAngleNumberText");
let numberAngle = angleRange.value / 2;
let numberPosition = polarToCartesian(80, 80, 30, 200);
arcNumber.setAttribute("x", numberPosition.x - 12);
arcNumber.setAttribute("y", numberPosition.y - 5);
arcNumberText.textContent = (angleRange.value + "°");
arcNumberText.setAttribute("x", numberPosition.x);
arcNumberText.setAttribute("y", numberPosition.y + 2);
getLength();
// Update diagonal-axis after resizing the window
window.addEventListener('resize', getLength);
This Pen doesn't use any external CSS resources.
This Pen doesn't use any external JavaScript resources.