Радарная диаграмма на холсте

Canvas

Недавнее требование, один из сценариев - нарисовать радарную диаграмму, искать круг, кажетсяAntVвнизF2Очень подходит для принятия учения:

Но затем, учитывая проект, я не проект визуализации. Большая вероятность не будет иметь эту таблицу визуализации, просто чтобы ввести библиотеку визуализации для одного или двух диаграмм в одной потребности, производительность затрат немного низкая. После тяжелой работы то громкость кода, только потому, что избыточная база кода возвращается к освобождению, то это правда (хотяF2уже достаточно лаконично)

Плюс я просто уважаюcanvasМеня это немного интересует, и еще менее вероятно, что я пропущу тренировочную телефонную встречу, доставленную прямо к моей двери.Документация для F2Это немного больно, я мог бы просто прочитать это, когда у меня будет время, чтобы прочитать этот документCanvasРоднойApiНу вот и решил сделать сам

нарисовать многоугольник

Эта радарная диаграмма кажется очень простой, но на самом деле это немного дверной проем Я подумал и разделил ее на три части:

  • Правильный многоугольник
  • Текст в вершинах правильных многоугольников
  • радиолокационная зона

Многоугольник показан следующим образом (здесь в качестве примера используется правильный пятиугольник):

Этот граф на самом деле состоит из нескольких правильных пятиугольников разных размеров, вложенных друг в друга.5Линия соединяет диагональные вершины правильного пятиугольника. Ключ в том, чтобы знать координаты пяти вершин правильного пятиугольника. На самом деле это решение геометрической математической задачи.

Как показано на рисунке, поверните правильный пятиугольник так, чтобы его центр находился в начале оси координат, а его крайняя левая сторона была параллельнаyОсь в этом состоянииxТочка с наибольшей координатой (т.е. крайняя правая точка) находится вxНа оси, а затем нарисуйте описанную окружность этого правильного пятиугольника, и тогда вы можете решить его

Причина, по которой правильный пятиугольник здесь повернут, состоит в том, чтобы упростить решение координат.Конечно, вы также можете сделать одну сторону правильного пятиугольника параллельнойxось или любое другое вращение для решения, если можно получить относительные координаты каждой вершины правильного многоугольника

очевидныйБог настолько очевиден, Координаты каждой вершины правильного пятиугольника:

(radius * cosθ, radius * sinθ)

здесьradius- радиус описанной окружности правильного пятиугольника,θэто линия между вершиной и началом координат иxугол

вradiusЭто наши собственные правила, толькоθРешение, согласно вышеуказанному рисунку, если самая крайняя крайняя сторона обычного многоугольника (т.е.xВершина с наибольшей координатой) — это первая вершина, а вращение против часовой стрелки — вторая, третья...n

Очевидно, этоθЗначение на самом деле составляет половину угла внутренних углов правильного пятиугольника и правильного многоугольника (n) внутренний угол (mAngle)дляMath.PI * 2 / n, то первыйnКоординаты точки:

(radius * cos(θ * (n - 1)), radius * sin(θ * (n - 1)))

Вот просто пример правильного пятиугольника, расслабьте его в положительную сторонуnВот что такое границы

Как только вершины получены, правильные многоугольники легко рисовать, но есть еще одна вещь, на которую следует обратить внимание.прямойто есть нижний край иxпараллельны, а правильный многоугольник, построенный по координатам вершин, полученным методом решения, здесь является стороной, аyПараллельно, поэтому необходимо сделать определенное отображение координат полученного положительного многоугольника, и преобразовать его впрямойиз

canvasrotatecanvasПоверните систему координат , тогда нарисованный многоугольник визуальнопрямойда

function drawPolygon () {
  // #region 绘制多边形
  const r = mRadius / polygonCount
  let currentRadius = 0
  for (let i = 0; i < polygonCount; i++) {
    bgCtx.beginPath()
    currentRadius = r * (i + 1)
    for (let j = 0; j < mCount; j++) {
      const x = currentRadius * Math.cos(mAngle * j)
      const y = currentRadius * Math.sin(mAngle * j)
      // 记录最外层多边形各个顶点的坐标
      if (i === polygonCount - 1) {
        polygonPoints.push([x, y])
      }
      j === 0 ? bgCtx.moveTo(x, y) : bgCtx.lineTo(x, y)
    }
    bgCtx.closePath()
    bgCtx.stroke()
  }
  // #endregion

  // #region 绘制多边形对角连线
  for (let i = 0; i < polygonPoints.length; i++) {
    bgCtx.moveTo(0, 0)
    bgCtx.lineTo(polygonPoints[i][0], polygonPoints[i][1])
  }
  bgCtx.stroke()
  // #endregion
}

Текст в вершинах правильных многоугольников

Положение копии на самом деле рядом с вершиной, и можно сделать определенное смещение по координатам вершины, но как было сказано ранее, т.к.canvasСистема координат прошлаrotateОн был повернут, и текст, который я хочу нарисовать здесь,прямой, вам нужно снова повернуть систему координат

Кроме того, обратите внимание на то, как нарисован текст.textAlignМожно решить:

function drawVertexTxt () {
  bgCtx.font = 'normal normal lighter 16px Arial'
  bgCtx.fillStyle = '#333'
  // 奇数多边形,距离设备顶边最近的点(即最高点的那一点),需要专门设置一下 textAlign
  const topPointIndex = mCount - Math.round(mCount / 4)
  for (let i = 0; i < polygonPoints.length; i++) {
    bgCtx.save()
    bgCtx.translate(polygonPoints[i][0], polygonPoints[i][1])
    bgCtx.rotate(rotateAngle)
    let indentX = 0
    let indentY = 0
    if (i === topPointIndex) {
      // 最高点
      bgCtx.textAlign = 'center'
      indentY = -8
    } else {
      if (polygonPoints[i][0] > 0 && polygonPoints[i][1] >= 0) {
        bgCtx.textAlign = 'start'
        indentX = 10
      } else if (polygonPoints[i][0] < 0) {
        bgCtx.textAlign = 'end'
        indentX = -10
      }
    }
    // 如果是正四边形,则需要单独处理最低点
    if (mCount === 4 && i === 1) {
      bgCtx.textAlign = 'center'
      indentY = 10
    }
    // 开始绘制文案
    mData[i].titleList.forEach((item, index) => {
      bgCtx.fillText(item, indentX, indentY + index * 20)
    })
    bgCtx.restore()
  }
}

радиолокационная зона

Район радара - это район в красной рамке на картинке статьи.Этот район тоже многоугольник, но он не положительный, а строгость координат аналогична положительному многоугольнику, только надо согласовывать, согласовывать Параметры будут масштабироваться, и это соотношение представляет собой пропорцию фактического значения и общего значения, представляющего вершину (например, 100 точек заполнены, первая точка - только 80 точек, затем80%)

Если это просто статичное изображение, то пока нечего сказать.Найдите координаты каждой точки в зоне радара, затем соедините путь, замкните путь, а затем обведите контур, но если вы хотите сделать радар в начале статьи Область динамически заполняется, что немного более хлопотно

Моя первоначальная идея состоит в том, чтобы решить динамические координаты каждой вершины каждой области кадра радара, забыть на долгое время, а позже найти слишком много проблем, как вытащить столько математических формул, которые должны быть в состоянии выяснить производительность не намного лучше Бар

canvasEстьclipштука, так я и думал, лишь бы нужная область радара обрезалась первой(clip) Хорошо, значит, разреза достаточно, чтобы покрыть площадь круга, вырезанного на поверхности динамического веера, а не добиться этого?

for (let i = 0; i < mCount; i++) {
  // score不能超过 fullScore
  score = Math.min(mData[i].score, mData[i].fullScore)
  const x = Math.cos(mAngle * i) * score / mData[i].fullScore
  const y = Math.sin(mAngle * i) * score / mData[i].fullScore
  i === 0 ? ctx.moveTo(x, y) : ctx.lineTo(x, y)
}
ctx.closePath()
ctx.clip()
// ...
ctx.moveTo(0, 0)
ctx.arc(0, 0, canvasMaxSize, 0, currentAngle)
ctx.closePath()
ctx.fill()

Эффект показан на рисунке:

Вроде выполнимо, но по сравнению с картинкой в ​​начале статьи обнаруживается, что ее все-таки немного не хватает.Радиолокационная область изображения заголовка имеет красную обводку, а после полной прорисовки остается представляют собой маленькие красные кружки в каждой вершине зоны обзора радара.

Красные точки в вершинах просты в обращении, координаты вершин известны, это не что иное, как рисование маленького круга в вершинах, но обводка немного хлопотна

Длина штриха тесно связана с ходом рисования области радара, что требует знания координат каждой вершины области радара в каждом кадре. Ну, не делайте так много расчетов по формуле.

Позже я снова подумал об этом, если заранее нарисовать конечное состояние карты радара, затем покрыть его маской, а затем открыть маску динамически, не сработает ли это?

посмотрел еще разcanvas, нашел файл с именемglobalCompositeOperationизAPI, Это оно

Чтобы облегчить рисунок, я снова разделил его и использовал всего три.canvas

ПервыйcanvasВ качестве холста для окончательного эффекта рендеринга второй используется для рисования области статического радара полной версии, а третий используется для рисования маски, используемой для маскирования области статического радара полной версии.canvas

резюме

Live Demoа такжеобразец кода