SOURCE

console 命令行工具 X clear

                    
>
console
const svg = document.querySelector("#svg");
const rad = Math.PI / 180;
const max = 200;

let requestId = null;
let t = {
  x: 25,
  y: 25
}; // translation
let mouseAngle = 0; // initial position of the bubble
let deltaAngle = mouseAngle; // angle between point and mouse pos
class Point {
  constructor(angle, elmt) {
    this.a = 0;
    this.elmt = elmt;
    this.angle = angle;
    this.x = 20 * Math.cos(this.angle);
    this.y = 20 * Math.sin(this.angle);
    this.vel = 0;
  }
  draw() {
    // elmt == the bubble
    this.elmt.setAttribute("cx", this.x);
    this.elmt.setAttribute("cy", this.y);
  }

  updateAngle(target) {
    let spring = 3 * rad - deltaAngle / 120;
    this.dist = target - this.a;
    this.acc = this.dist * spring;
    this.vel += this.acc;
    this.vel *= 0.80;
    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); // the bubble
function Draw() {
  requestId = window.requestAnimationFrame(Draw);

  p.updateAngle(deltaAngle);
  p.rotate();
  p.draw();
}
Draw();

svg.addEventListener("click", function(e) {
  // debugger;
  mouseAngle = getMouseAngle(e, t);
  number.value = getAngleInPercents(mouseAngle);
  contT.innerText = number.value;
  contT.innerText +=  ' X:' + e.clientX + ' Y:' + e.clientY;
  tinput.value = number.value;
  onEvent();
},
false);

number.addEventListener("input", function(e) {
  mouseAngle = map(number.value, 0, max, 0, 360) * rad;

  onEvent();
},
false);

function onEvent() {
  if (requestId) {
    cancelAnimationFrame(requestId);
    requestId = null;
  }
  p.getAngle(); // changes the p.angle
  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;
  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);
  //console.log("mouse: ",Math.atan2(m.y,m.x)/rad)
  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, max);
}

function map(n, a, b, _a, _b) {
  var d = b - a;
  var _d = _b - _a;
  var u = _d / d;
  return _a + n * u;
}
tinput.addEventListener("keyup", function(e){
  number.value = tinput.value || 0;
  mouseAngle = map(number.value, 0, max, 0, 360) * rad;
  onEvent();
});
svg.addEventListener("touchstart", function(e) {
  // mouseAngle = getMouseAngle(e, t);
  // number.value = getAngleInPercents(mouseAngle);
  // contT.innerText = number.value;
  // onEvent(); 
  //console.log(e);
});

svg.addEventListener("touchmove", function(e) {
  e = e.changedTouches[0];
  mouseAngle = getMouseAngle(e, t);
  number.value = getAngleInPercents(mouseAngle);
  contT.innerText = number.value;
  tinput.value = number.value;
  onEvent();
});
<div style="position:relative; width:220px;height:220px;margin:auto">
<div id="cont">
  <input id="number" type="range" value="0" min="0" max="200">
</div>
<div id="contT">
</div>
<svg id="svg" viewBox="0 0 50 50">
  <defs>
    <linearGradient x1="1" y1="0" x2="0" y2="0" id="gradient1">
      <stop offset="3%" stop-color="#5d6aff">
      </stop>
      <stop offset="98%" stop-color="#a3a8e7">
      </stop>
    </linearGradient>
    <linearGradient x1="1" y1="0" x2="0" y2="0" id="gradient2">
                        <stop offset="3%" stop-color="#f03e3e">
                        </stop>
                        <stop offset="98%" stop-color="#e7a3a3">
                        </stop>
                    </linearGradient>
                    <linearGradient x1="1" y1="0" x2="0" y2="0" id="gradient3">
                        <stop offset="3%" stop-color="#f29220">
                        </stop>
                        <stop offset="98%" stop-color="#e7cc7f">
                        </stop>
                    </linearGradient>
  </defs>
  <g transform="translate(25 25)">
    <circle r="20" stroke="url('#gradient1')" stroke-width="4" fill="none"
    fill-opacity="0.8" />
    <circle r="18.6" fill="url('#gradient1')" style="fill:url('#gradient2')" fill-opacity="0.8" />
    <circle class="point" id="A" r="2" />
    <text x="-7" y="-6" class="title" font-size="2.5">
      收缩压 (高压)
    </text>
    <text x="-12.5" y="8" class="title" font-size="2.5">
      标准值:90-140mmHg
    </text>
  </g>
</svg>
<input type="number" class="form-control tt-input" id="tinput">
</div>
#svg {
  width: 220px;
  height: 220px;
  margin: 0 auto;
  display: block;
}

.point {
  fill: #ffffff;
}

.title {
  fill: #fff;
}

.form-control {
    outline: none;
    text-align: center;
    border: 1px solid #ffffff;
    border-radius: 100px;
    width: 70px;
    height: 27px;
}

.tt-input {
    position: absolute;
    top: 170px;
    left: 80px;
    background: transparent;
    color: #fff;
}

#contT{
      min-height: 47px;
    display: block;
}