Рисование круга с траекторией дуги SVG

147

Короткий вопрос: используя путь SVG, мы можем нарисовать 99,99% круга, и он появляется, но если он равен 99,999999999% круга, то круг не будет отображаться. Как это можно исправить?

Следующий путь SVG может нарисовать 99,99% круга: (попробуйте это на http://jsfiddle.net/DFhUF/1381/ и посмотрите, видите ли вы 4 дуги или только 2 дуги, но обратите внимание, что если это IE, это отображается в VML, а не в SVG, но имеет похожую проблему)

M 100 100 a 50 50 0 1 0 0.00001 0

Но когда это 99,999999999% от круга, то вообще ничего не покажется?

M 100 100 a 50 50 0 1 0 0.00000001 0    

И то же самое со 100% круга (это все еще дуга, не так ли, просто очень полная дуга)

M 100 100 a 50 50 0 1 0 0 0 

Как это можно исправить? Причина в том, что я использую функцию, чтобы нарисовать процент дуги, и если мне нужно «особый случай», дуга 99,9999% или 100%, чтобы использовать функцию круга, это было бы глупо.

Опять же, тестовый пример для jsfiddle с использованием RaphaelJS находится по адресу http://jsfiddle.net/DFhUF/1381/
(и если это VML в IE 8, даже второй круг не будет показан ... вы должны изменить его на 0.01)


Обновить:

Это потому, что я выставляю дугу для оценки в нашей системе, поэтому 3,3 балла получают 1/3 круга. 0,5 получает полукруга, а 9,9 балла - 99% круга. Но что, если в нашей системе есть 9,99 баллов? Должен ли я проверить, находится ли он близко к 99,999% круга, и использовать arcфункцию или circleфункцию соответственно? Тогда как насчет 9.987? Какой использовать? Смешно знать, какие оценки будут отображаться в «слишком полный круг» и переключаться на функцию круга, а когда это «определенный 99,9%» от круга или 9,9997, тогда использовать функцию дуги ,

太極 者 無極 而 生
источник
@minitech, конечно, это работает ... тогда это 99% круга (грубо говоря). Дело в том, что он может нарисовать 98%, 99%, 99,99% круга, но не 99,99999999% или 100%
неполярность
1
Обе эти ссылки ведут к одной и той же вещи, и она отлично работает в Safari.
Марк Бесси
да, та же ссылка, я просто хочу, чтобы люди увидели тестовый пример раньше, поэтому я добавляю ссылку в начале вопроса. Правильное Safari сделает это, как хорошо ... Chrome и Firefox не сделают ... своего рода странное, потому что Safari и Chrome оба являются Webkit ... но зависит ли движок SVG от Webkit?
неполярность
@ Марчин выглядит хорошо, как? Вы видите 4 дуги или 2 дуги? ты вообще смотрел на код?
неполярность
2
обновленный jsfiddle с Raphael, включенным в качестве библиотеки, чтобы обойти перекрестную ошибку при загрузке raphael.js: jsfiddle.net/DFhUF/1381
ericsoco

Ответы:

35

То же самое для дуги XAML. Просто закройте дугу 99,99%, Zи вы получите круг!

Тодд Майн
источник
так что вы можете закрыть третий случай в образце jsfiddle и заставить его работать?
неполярность
с понижением значения до 0,0001, да. Звучит проблема, связанная с реализацией WebKit в различных браузерах, а не с самим SVG. Но именно так вы делаете круг в компоненте SVG / XAMLs Arc.
Тодд Майн
Аааа, измена, всегда лучшее решение. По-прежнему нужно обрабатывать 100% -ный случай, но просто «округляя» до 99,999%, а не с помощью отдельной отдельной ветви кода.
SimonR
Любая демоверсия? Этот ответ не выполняется
Медет Тлеукабилулы
@MedetTleukabiluly у вас есть дуга выше в вопросе, добавьте zв конце и все готово. Возможно, вам придется удалить нули, например, в последней версии Chrome, которую мне пришлось написать, 0.0001чтобы она работала. Если вы ищете, куда поместить строку пути, посмотрите на Пути в MDN .
TWiStErRob
416

Я знаю, что в игре немного поздно, но я вспомнил этот вопрос, когда он был новым, и у меня была похожая дилемма, и я случайно нашел «правильное» решение, если кто-то все еще ищет его:

<path 
    d="
    M cx cy
    m -r, 0
    a r,r 0 1,0 (r * 2),0
    a r,r 0 1,0 -(r * 2),0
    "
/>

Другими словами, это:

<circle cx="100" cy="100" r="75" />

может быть достигнуто как путь с этим:

  <path 
        d="
        M 100, 100
        m -75, 0
        a 75,75 0 1,0 150,0
        a 75,75 0 1,0 -150,0
        "
  />

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

Причина, по которой это не может быть выполнено в виде полного круга в одной дуге (и я просто размышляю), заключается в том, что вы сказали бы ему нарисовать дугу из себя (скажем, 150 150) в себя (150 150), которую он отображает. как "о, я уже там, дуга не нужна!"

Преимущества предлагаемого мной решения:

  1. легко перевести из круга прямо в путь, и
  2. нет перекрытия двух дуговых линий (что может вызвать проблемы, если вы используете маркеры или шаблоны и т. д.). Это чистая сплошная линия, хотя нарисованная на две части.

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

Демонстрация jsfiddle: http://jsfiddle.net/crazytonyi/mNt2g/

Обновить:

Если вы используете путь для textPathссылки и хотите, чтобы текст отображался по внешнему краю дуги, вы должны использовать точно такой же метод, но измените флаг развертки с 0 на 1, чтобы он обрабатывал внешнюю часть путь как поверхность, а не изнутри (представьте, 1,0что кто-то сидит в центре и рисует вокруг себя круг, 1,1а кто-то идет вокруг центра на расстоянии радиуса и тащит мел рядом с собой, если это поможет). Вот код, как указано выше, но с изменением:

<path 
    d="
    M cx cy
    m -r, 0
    a r,r 0 1,1 (r * 2),0
    a r,r 0 1,1 -(r * 2),0
    "
/>
Энтони
источник
1
+1, спасибо за формулы. Конечно, первые две команды могут быть объединены.
Джон Гитцен
+1 для ясности и практичности решения в целом, но особенно для «элементов формы, таких как круг, технически не имеют« начальной »точки» - такой ясный способ выразить проблему.
Дэвид Джон Валлийский
11
Я думаю, что проблема здесь не в том, что «о, я уже там, дуга не нужна!», Так как, указав 1 как флаг большой дуги, вы явно указываете движку, что хотите, чтобы он пошел дальше. Реальная проблема заключается в том, что существует бесконечно много кругов, которые полностью удовлетворяют условиям прохождения через вашу точку. Это объясняет, почему, когда вы хотите 360 * дуги, то двигатель не знает, что делать. Причина, по которой он не работает для 359,999, заключается в том, что это численно нестабильные вычисления, и хотя технически существует ровно один круг, который соответствует запросу, он, вероятно, в любом случае не будет тем, который вам нужен
qbolec
@qbolec - Ты прав, я думал о том, что большая дуга и развертка отключены, а у дуги нет места назначения. Или, по крайней мере, даже при включенной большой дуге и развертке существовал какой-то фундаментальный проект, который определял дугу как кривую между двумя точками (так, чтобы при использовании одной точки дважды она рассматривалась как свернутая единственная точка). видение дуги, потенциально рисующей 360 кругов вокруг одной точки, имеет смысл, хотя оно будет разрушено до одного круга, если будет определен центр, что поднимает вопрос о том, может ли одна дуга образовать полный круг, если центр действительно существует?
Энтони
1
"элементы формы, такие как круг, технически не имеют" начальной "точки". Это не совсем так. Спецификация SVG абсолютно определяет, где находится начальная точка всех форм. Это необходимо для того, чтобы массивы штрихов работали с фигурами.
Пол ЛеБо
17

В отношении решения Энтони, вот функция для получения пути:

function circlePath(cx, cy, r){
    return 'M '+cx+' '+cy+' m -'+r+', 0 a '+r+','+r+' 0 1,0 '+(r*2)+',0 a '+r+','+r+' 0 1,0 -'+(r*2)+',0';
}
Антон Матиашевич
источник
10

Совершенно другой подход:

Вместо того, чтобы возиться с путями, чтобы указать дугу в svg, вы также можете взять элемент круга и указать штрих-черту в псевдокоде:

with $score between 0..1, and pi = 3.141592653589793238

$length = $score * 2 * pi * $r
$max = 7 * $r  (i.e. well above 2*pi*r)

<circle r="$r" stroke-dasharray="$length $max" />

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

Дуга начинается в крайней правой точке и может быть смещена с помощью поворота.

Примечание: в Firefox есть странная ошибка, когда повороты на 90 градусов и более игнорируются. Итак, чтобы начать дугу сверху, используйте:

<circle r="$r" transform="rotate(-89.9)" stroke-dasharray="$length $max" />
MVDS
источник
Имейте в виду, что это решение применимо только в тех случаях, когда вы не используете какой-либо тип заливки и вам нужен только сегмент дуги без отклонений. В тот момент, когда вы хотите нарисовать секторы, вам даже понадобится новый узел пути SVG, который в противном случае мог бы быть обработан одним тегом пути.
mxfh
@mxfh не совсем, если вы берете ширину обводки вдвое больше значения r, вы получите идеальные сектора.
mvds
С stroke-dasharray="0 10cm 5cm 1000cm"его помощью можно избежать трансформации
stenci
@stenci да, но недостатком является то, что вы не можете иметь один параметр в панели инструментов для масштабирования от 0% до 100% заполнения (что было одной из моих целей)
mvds
5

Adobe Illustrator использует кривые Безье, такие как SVG, а для кругов создает четыре точки. Вы можете создать круг с помощью двух команд эллиптической дуги ... но тогда для круга в SVG я бы использовал <circle />:)

Phrogz
источник
«Вы можете создать круг с помощью двух команд эллиптической дуги»? что вы имеете в виду? Вы не можете просто использовать один? По крайней мере, сделав переход от (0,0) к (0,01, 0), чтобы он что-то отображал. Для использования circle, пожалуйста, см. Обновление в вопросе вместо этого.
неполярность
Нет, я не думаю, что вы можете использовать только один. Вы показали, что не можете, и у вас есть похожая проблема с дугами HTML5 Canvas.
Phrogz
Вы имеете в виду, используя arcдля создания полного круга, используя левую дугу и правую дугу? Таким образом, дуга не может нарисовать полный круг ... для этого arcнужны два пути
неполярность
2
@ 動靜 能量 Правильно, нужны две траектории дуги (или уродливый взлом одной дуги, проходящей большую часть пути, а затем команда closepath для прямой линии до конца).
Phrogz
1
Иллюстратор отстой. Вы не можете сделать круг с кривыми Безье. Только что-то близко к кругу, но все же не круг.
Adius
4

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

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

cuixiping
источник
5
Основным ограничением svg является то, что элементы формы не могут использоваться для текстовых путей. Это, вероятно, основная мотивация для всех, кто посещает этот вопрос.
Энтони
1
@ Энтони, теперь они могут. Firefox поддерживает textPath для любой фигуры. Я подозреваю, что Chrome тоже.
Роберт Лонгсон
@RobertLongson - Можете ли вы привести пример или ссылки на пример? Любопытно, как он решает, где начинается текстовый путь и находится ли он снаружи или внутри (и т. Д.), Когда он рисуется без традиционной отправной точки.
Энтони
@Anthony См. Спецификацию SVG 2, например, w3.org/TR/SVG2/shapes.html#CircleElement , а также новый атрибут стороны
Роберт Лонгсон,
4

Опираясь на ответы Энтони и Антона, я включил возможность вращать сгенерированный круг, не влияя на его внешний вид. Это полезно, если вы используете путь для анимации и вам нужно контролировать, где она начинается.

function(cx, cy, r, deg){
    var theta = deg*Math.PI/180,
        dx = r*Math.cos(theta),
        dy = -r*Math.sin(theta);
    return "M "+cx+" "+cy+"m "+dx+","+dy+"a "+r+","+r+" 0 1,0 "+-2*dx+","+-2*dy+"a "+r+","+r+" 0 1,0 "+2*dx+","+2*dy;
}
оборота россковар
источник
Это именно то, что мне было нужно. Я использовал плагин greensock morph, чтобы трансформировать круговую траекторию в прямоугольную, и мне нужно было немного повернуть ее, чтобы она выглядела правильно.
RoboKozo
3

Для таких как я, которые искали атрибуты эллипса для преобразования пути:

const ellipseAttrsToPath = (rx,cx,ry,cy) =>
`M${cx-rx},${cy}a${rx},${ry} 0 1,0 ${rx*2},0a${rx},${ry} 0 1,0 -${rx*2},0`
йог
источник
2

Написано как функция, это выглядит так:

function getPath(cx,cy,r){
  return "M" + cx + "," + cy + "m" + (-r) + ",0a" + r + "," + r + " 0 1,0 " + (r * 2) + ",0a" + r + "," + r + " 0 1,0 " + (-r * 2) + ",0";
}
Гарри Стивенс
источник
2
Это отличный ответ! Просто небольшое дополнение: этот путь нарисован Восток, Север, Запад, Юг. Если вы хотите, чтобы круг вести себя так же , как <circle>вы должны изменить направление на восток, юг, запад, север: "M" + cx + "," + cy + "m" + (r) + ",0" + "a" + r + "," + r + " 0 1,1 " + (-r * 2) + ",0" + "a" + r + "," + r + " 0 1,1 " + (r * 2) + ",0" Преимущество заключается в том, что stroke-dashoffsetи в startOffsetнастоящее время работает таким же образом.
loominade
2

Эти ответы слишком сложны.

Более простой способ сделать это без создания двух дуг или преобразования в разные системы координат.

Это предполагает, что ваша область холста имеет ширину wи высоту h.

`M${w*0.5 + radius},${h*0.5}
 A${radius} ${radius} 0 1 0 ${w*0.5 + radius} ${h*0.5001}`

Просто используйте флаг "длинная дуга", чтобы заполнить полный флаг. Затем сделайте дуги на 99,9999% полным кругом. Визуально это то же самое. Избегайте флага развертки, просто начав круг в крайней правой точке круга (один радиус прямо горизонтально от центра).

Изучение статистики на примере
источник
1

Другим способом было бы использовать две кубические кривые Безье. Это для пользователей iOS, использующих pocketSVG, который не распознает параметр svg arc.

C x1 y1, x2 y2, xy (или c dx1 dy1, dx2 dy2, dx dy)

Кубическая кривая Безье

Последний набор координат здесь (x, y) - это то место, где должна заканчиваться линия. Два других являются контрольными точками. (x1, y1) является контрольной точкой для начала вашей кривой, и (x2, y2) для конечной точки вашей кривой.

<path d="M25,0 C60,0, 60,50, 25,50 C-10,50, -10,0, 25,0" />
ha100
источник
1

я сделал jsfiddle, чтобы сделать это здесь:

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;       
}
console.log(describeArc(255,255,220,134,136))

ссылка на сайт

все, что вам нужно сделать, это изменить ввод в console.log и получить результат в консоли

Джавад Бат
источник
Это было именно то, что я искал. Лучший ответ здесь! возразить этому парню
Måns Dahlström