В следующем примере, который я написал, показано, как
Этот рабочий пример является автономным. Он будет определять простой объект запроса, который использует объект window XMLHttpRequest
для совершения вызовов. Он будет определять простую функцию, чтобы дождаться завершения кучи обещаний.
Контекст. В этом примере запрашивается конечная точка Spotify Web API для поиска объектов playlist
для заданного набора строк запроса:
[
"search?type=playlist&q=%22doom%20metal%22",
"search?type=playlist&q=Adele"
]
Для каждого элемента новый Promise запустит блок - ExecutionBlock
, проанализирует результат, заплатит новый набор обещаний на основе массива результатов, который представляет собой список объектов Spotify user
и выполняет новый HTTP-вызов в ExecutionProfileBlock
асинхронно.
Затем вы можете увидеть вложенную структуру Promise, которая позволяет вам генерировать множественные и полностью асинхронные вложенные HTTP-вызовы и присоединять результаты к каждому подмножеству вызовов через Promise.all
.
NOTE Recent Spotify search
API-интерфейсам потребуется указать токен доступа в заголовках запроса:
-H "Authorization: Bearer {your access token}"
Итак, вы должны запустить следующий пример, вам нужно поместить маркер доступа в заголовки запроса:
var spotifyAccessToken = "YourSpotifyAccessToken";
var console = {
log: function(s) {
document.getElementById("console").innerHTML += s + "
"
}
}
// Simple XMLHttpRequest
// based on https://davidwalsh.name/xmlhttprequest
SimpleRequest = {
call: function(what, response) {
var request;
if (window.XMLHttpRequest) { // Mozilla, Safari, ...
request = new XMLHttpRequest();
} else if (window.ActiveXObject) { // Internet Explorer
try {
request = new ActiveXObject('Msxml2.XMLHTTP');
}
catch (e) {
try {
request = new ActiveXObject('Microsoft.XMLHTTP');
} catch (e) {}
}
}
// State changes
request.onreadystatechange = function() {
if (request.readyState === 4) { // Done
if (request.status === 200) { // Complete
response(request.responseText)
}
else
response();
}
}
request.open('GET', what, true);
request.setRequestHeader("Authorization", "Bearer " + spotifyAccessToken);
request.send(null);
}
}
//PromiseAll
var promiseAll = function(items, block, done, fail) {
var self = this;
var promises = [],
index = 0;
items.forEach(function(item) {
promises.push(function(item, i) {
return new Promise(function(resolve, reject) {
if (block) {
block.apply(this, [item, index, resolve, reject]);
}
});
}(item, ++index))
});
Promise.all(promises).then(function AcceptHandler(results) {
if (done) done(results);
}, function ErrorHandler(error) {
if (fail) fail(error);
});
}; //promiseAll
// LP: deferred execution block
var ExecutionBlock = function(item, index, resolve, reject) {
var url = "https://api.spotify.com/v1/"
url += item;
console.log( url )
SimpleRequest.call(url, function(result) {
if (result) {
var profileUrls = JSON.parse(result).playlists.items.map(function(item, index) {
return item.owner.href;
})
resolve(profileUrls);
}
else {
reject(new Error("call error"));
}
})
}
arr = [
"search?type=playlist&q=%22doom%20metal%22",
"search?type=playlist&q=Adele"
]
promiseAll(arr, function(item, index, resolve, reject) {
console.log("Making request [" + index + "]")
ExecutionBlock(item, index, resolve, reject);
}, function(results) { // Aggregated results
console.log("All profiles received " + results.length);
//console.log(JSON.stringify(results[0], null, 2));
///// promiseall again
var ExecutionProfileBlock = function(item, index, resolve, reject) {
SimpleRequest.call(item, function(result) {
if (result) {
var obj = JSON.parse(result);
resolve({
name: obj.display_name,
followers: obj.followers.total,
url: obj.href
});
} //result
})
} //ExecutionProfileBlock
promiseAll(results[0], function(item, index, resolve, reject) {
//console.log("Making request [" + index + "] " + item)
ExecutionProfileBlock(item, index, resolve, reject);
}, function(results) { // aggregated results
console.log("All response received " + results.length);
console.log(JSON.stringify(results, null, 2));
}
, function(error) { // Error
console.log(error);
})
/////
},
function(error) { // Error
console.log(error);
});
Я подробно рассмотрел это решение здесь .
То же самое для дуги XAML. Просто закройте дугу 99.99% с помощью Z
, и у вас есть круг!
Основываясь на ответах Энтони и Антона, я включил способность поворачивать созданный круг, не затрагивая его общий внешний вид. Это полезно, если вы используете путь для анимации, и вам нужно контролировать, где он начинается.
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;
}
Я знаю, что это немного поздно в игре, но я вспомнил этот вопрос с того момента, когда он был новым, и у меня была подобная диллемма, и я случайно нашел «правильное» решение, если кто-то еще ищет его:
<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), которую он отображает как «о, я уже там, нужная дуга!».
Преимуществами предлагаемого решения являются:
Ничто из этого не имело бы значения, если бы они просто разрешали текстовым путям принимать фигуры. Но я думаю, что они избегают этого решения, поскольку элементы формы, такие как круг, технически не имеют точки начала.
jsfiddle demo: 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
"
/>
См. решение Anthony's здесь, это функция для получения пути:
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';
}
Хорошо, что с помощью двух команд дуги рисовать полный круг.
обычно, я использую элемент эллипса или круга для рисования полного круга.
Другой способ - использовать два кубических кривых Безье. Это для 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" />
Написанная как функция, она выглядит так:
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";
}
Эти примеры слишком сложны !!!
Просто сделайте это с помощью литералов шаблонов es6.
Простейший способ сделать это без создания двух дуг ..
Предполагается, что ваша область холста имеет ширину 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% полного круга. Визуально это одно и то же. Избегайте флага развертки, просто начиная круг в самой правой точке круга (один радиус прямо горизонтальный от центра).
я сделал 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))
все, что вам нужно сделать, это изменить ввод консоли .log и получить результат в консоли
Совершенно другой подход:
Вместо того, чтобы возиться с путями для указания дуги в svg, вы также можете взять элемент окружности и указать штрих-dasharray в псевдокоде:
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" />
Adobe Illustrator использует кривые безье, такие как SVG, а для кругов - четыре точки. Вы можете создать круг с двумя эллиптическими командами дуги ... но затем для круга в SVG я бы использовал <circle />
:)
Для таких, как я, которые искали атрибуты эллипса для преобразования пути:
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`