console
const SVG_NS = "http://www.w3.org/2000/svg";
const XLink_NS = "http://www.w3.org/1999/xlink";
const svg = document.querySelector("svg");
const point = document.querySelector("#point");
const text = document.querySelector("text");
const rad = Math.PI / 180;
let requestId = null;
let t = { x: 25, y: 25 };
let mouseAngle = Math.PI / 4;
let deltaAngle = mouseAngle;
let spring = 3 * rad - deltaAngle / 120;
const friction = 0.80;
class Point {
constructor(angle, elmt) {
this.a = 0;
this.elmt = elmt;
this.angle = angle;
this.x = 10 * Math.cos(this.angle);
this.y = 10 * Math.sin(this.angle);
this.vel = 0;
}
draw() {
this.elmt.setAttributeNS(null, "cx", this.x);
this.elmt.setAttributeNS(null, "cy", this.y);
text.setAttributeNS(null, "x", this.x);
text.setAttributeNS(null, "y", this.y);
text.textContent = ~~getAngleInPercents(mouseAngle);
}
updateAngle(target) {
this.dist = target - this.a;
this.acc = this.dist * spring;
this.vel += this.acc;
this.vel *= friction;
this.a += this.vel;
}
getAngle() {
this.angle = Math.atan2(this.y, this.x);
}
rotate() {
let cos = Math.cos(this.vel);
let sin = Math.sin(this.vel);
let p = { x: this.x, y: this.y };
this.x = p.x * cos - p.y * sin;
this.y = p.x * sin + p.y * cos;
}
}
let p = new Point(0, A);
function Draw() {
requestId = window.requestAnimationFrame(Draw);
p.updateAngle(deltaAngle);
p.rotate();
p.draw();
}
Draw();
svg.addEventListener(
"click",
function(e) {
mouseAngle = getMouseAngle(e, t);
number.value = getAngleInPercents(mouseAngle);
onEvent();
},
false
);
number.addEventListener(
"input",
function(e) {
mouseAngle = map(number.value, 0, 100, 0, 360) * rad;
onEvent();
},
false
);
function onEvent() {
if (requestId) {
cancelAnimationFrame(requestId);
requestId = null;
}
p.getAngle();
if (p.angle < mouseAngle - Math.PI) {
p.angle = p.angle + 2 * Math.PI;
}
if (p.angle > mouseAngle + Math.PI) {
p.angle = p.angle - 2 * Math.PI;
}
deltaAngle = mouseAngle - p.angle;
spring = 3 * rad - Math.abs(deltaAngle / 120);
p.a = 0;
p.dist = 0;
p.vel = 0;
p.acc = 0;
Draw();
}
function oMousePosSVG(e) {
let p = svg.createSVGPoint();
p.x = e.clientX;
p.y = e.clientY;
let ctm = svg.getScreenCTM().inverse();
p = p.matrixTransform(ctm);
return p;
}
function transformedMousePos(e, t) {
let m = oMousePosSVG(e);
return {
x: m.x - t.x,
y: m.y - t.y
};
}
function getMouseAngle(e, t) {
let m = transformedMousePos(e, t);
return Math.atan2(m.y, m.x);
}
function getAngleInPercents(angle) {
let A = angle < 0 ? (angle + 2 * Math.PI) / rad : angle / rad;
return map(A, 0, 360, 0, 100);
}
function map(n, a, b, _a, _b) {
var d = b - a;
var _d = _b - _a;
var u = _d / d;
return _a + n * u;
}
<div id="cont"><input id="number" type="range" value="12" min="0" max="100"></div>
<svg viewBox="0 0 50 50">
<g transform="translate(25 25)">
<circle r="10" />
<circle class="point" id="A" r="2" />
<text>100</text>
</g>
</svg>
body {
background: #333;
font-family: Arial, sans-serif;
}
svg {
height: 100vmin;
margin: 0 auto;
display: block;
}
circle {
stroke: #1da1f2;
fill: none;
stroke-width: 0.5;
}
.point {
fill: #1da1f2;
}
#test {
stroke: red;
}
text {
dominant-baseline: central;
text-anchor: middle;
font-size: 2px;
pointer-events: none;
fill: #fff;
}
svg text::selection {
background: none;
}
div {
padding: 0;
width: 100vmin;
height: 100vmin;
pointer-events: none;
display: grid;
position: absolute;
left: calc(50% - 50vmin);
top: 0;
}
input[type="range"] {
width: 24%;
margin: auto;
pointer-events:auto;
}
input[type="range"]:focus {
outline: none;
}
input[type="range"],
input[type="range"]::-webkit-slider-runnable-track,
input[type="range"]::-webkit-slider-thumb {
-webkit-appearance: none;
}
input[type="range"]::-webkit-slider-thumb {
background-color: #1da1f2;
width: 20px;
height: 20px;
border: 3px solid #333;
border-radius: 50%;
margin-top: -9px;
}
input[type="range"]::-moz-range-thumb {
background-color: #1da1f2;
width: 15px;
height: 15px;
border: 3px solid #333;
border-radius: 50%;
}
input[type="range"]::-ms-thumb {
background-color: #1da1f2;
width: 20px;
height: 20px;
border: 3px solid #333;
border-radius: 50%;
}
input[type="range"]::-webkit-slider-runnable-track {
background-color: #1da1f2;
height: 3px;
}
input[type="range"]:focus::-webkit-slider-runnable-track {
outline: none;
}
input[type="range"]::-moz-range-track {
background-color: #1da1f2;
height: 3px;
}
input[type="range"]::-ms-track {
background-color: #1da1f2;
height: 3px;
}
input[type="range"]::-ms-fill-lower {
background-color: #1da1f2;
}
input[type="range"]::-ms-fill-upper {
background-color: #1da1f2;
}
@media only screen and (max-width: 480px) {
div {
position: static;
display: block;
margin: 2em 0;
width: 100%;
height: auto;
}
#cont input {
position: static;
display: block;
width: 50%;
margin: 0 auto;
}
}