Если мы прочитаем статью в Википедии о сквиках , мы увидим, что это просто невзвешенная функция эллипса, использующая степени 2 или выше, что означает, что мы можем довольно легко вычислить значения "y", заданные "x«оценивать и рисовать вещи таким образом, но это даст нам крайне неравные сегменты: небольшие изменения в x
приведут к ОГРОМНЫМ изменениям в y
в начальной и конечной точках и крошечные изменения в y
в средней точке.
Вместо этого давайте смоделируем сквикл как параметрическую функцию, чтобы мы изменили одно управляющее значение и получили разумно равномерно распределенные интервалы для работы.Мы можем найти это объяснение в статье в Википедии о функции суперэллипса :
x = |cos(t)^(2/n)| * sign(cos(t))
y = |sin(t)^(2/n)| * sign(sin(t))
для t
от 0 до 2π, а радиусы зафиксированы в 1 (поэтому они исчезают из умножений).
Если мы реализуем это, то мы можем добавить радужную раскраску почти в качестве запоздалой мысли, рисуя каждый сегмент пути отдельно, с помощью раскраски strokeStyle
, которая использует цвета HSL, где значения оттенков смещаются на основе нашего t
значение:
// alias some math functions so we don't need that "Math." all the time
const abs=Math.abs, sign=Math.sign, sin=Math.sin, cos=Math.cos, pow=Math.pow;
// N=2 YIELDS A CIRCLE, N>2 YIELDS A SQUIRCLE
const n = 4;
function coord(t) {
let power = 2/n;
let c = cos(t), x = pow(abs(c), power) * sign(c);
let s = sin(t), y = pow(abs(s), power) * sign(s);
return { x, y };
}
function drawSegmentTo(t) {
let c = coord(t);
let cx = dim + r * c.x; // Here, dim is our canvas "radius",
let cy = dim + r * c.y; // and r is our circle radius, with
ctx.lineTo(cx, cy); // ctx being our canvas context.
// stroke segment in rainbow colours
let h = (360 * t)/TAU;
ctx.strokeStyle = `hsl(${h}, 100%, 50%)`;
ctx.stroke();
// start a new segment at the end point
ctx.beginPath();
ctx.moveTo(cx, cy);
}
Затем мы можем использовать это в сочетании с некоторым стандартным кодом API Canvas2D:
const PI = Math.PI,
TAU = PI * 2,
edge = 200, // SIZE OF THE CANVAS, IN PIXELS
dim = edge/2,
r = dim * 0.9,
cvs = document.getElementById('draw');
// set up our canvas
cvs.height = cvs.width = edge;
ctx = cvs.getContext('2d');
ctx.lineWidth = 2;
ctx.fillStyle = '#004';
ctx.strokeStyle = 'black';
ctx.fillRect(0, 0, edge, edge);
И после того, как все настройки завершены, код отрисовки действительно прямой.вперед:
// THIS DETERMINES HOW SMOOTH OF A CURVE GETS DRAWN
const segments = 32;
// Peg our starting point, which we know is (r,0) away from the center.
ctx.beginPath();
ctx.moveTo(dim + r, dim)
// Then we generate all the line segments on the path
for (let step=TAU/segments, t=step; t<=TAU; t+=step) drawSegmentTo(t);
// And because IEEE floats are imprecise, the last segment may not
// actually reach our starting point. As such, make sure to draw it!
ctx.lineTo(dim + r, dim);
ctx.stroke();
Выполнение этого приведет к следующей короткой последовательности:
С jsbin, чтобы вы могли играть с числами: https://jsbin.com/haxeqamilo/edit?js,output
Конечно, вы также можете пойти совершенно другим путем: создать элемент SVG (поскольку SVG является частью HTML5) с элементом <path>
и соответствующим образом установить ширину, высоту и поле просмотра, а затем сгенерировать d
атрибути градиентный цвет, но это определенно гораздо более привередливый .