есть уже хорошие решения, представленные здесь. Я хотел бы добавить варианты того, что уже было представлено - вариантов некоторых тригонометрии не так много, если вы хотите имитировать рисованные круги.
Я бы рекомендовал на самом деле записать реальный рисованный круг. Вы можете записывать точки, а также timeStamp
и воспроизводить точный чертеж в любое время позже. Вы могли бы объединить это с алгоритмом сглаживания линии.
Это здесь решение создает круги, такие как эти:
Вы можете изменить цвет, толщину и т.д., установив strokeStyle
, lineWidth
и т.д., как обычно.
Чтобы нарисовать круг просто позвонить:
handDrawCircle(context, x, y, radius [, rounds] [, callback]);
(callback
предоставляется как анимация делает функцию асинхронно).
код разделяется на два сегмента:
- Генерировать точки
- Анимируйте точек
Низкоур.инициализ:
function handDrawCircle(ctx, cx, cy, r, rounds, callback) {
/// rounds is optional, defaults to 3 rounds
rounds = rounds ? rounds : 3;
var x, y, /// the calced point
tol = Math.random() * (r * 0.03) + (r * 0.025), ///tolerance/fluctation
dx = Math.random() * tol * 0.75, /// "bouncer" values
dy = Math.random() * tol * 0.75,
ix = (Math.random() - 1) * (r * 0.0044), /// speed /incremental
iy = (Math.random() - 1) * (r * 0.0033),
rx = r + Math.random() * tol, /// radius X
ry = (r + Math.random() * tol) * 0.8, /// radius Y
a = 0, /// angle
ad = 3, /// angle delta (resolution)
i = 0, /// counter
start = Math.random() + 50, /// random delta start
tot = 360 * rounds + Math.random() * 50 - 100, /// end angle
points = [], /// the points array
deg2rad = Math.PI/180; /// degrees to radians
В главном цикле мы не 't отскакивает случайно, но увеличивает t со случайным значением и затем линейно увеличивать с этим значением, отмените его, если мы находимся на границах (толерантность).
for (; i < tot; i += ad) {
dx += ix;
dy += iy;
if (dx < -tol || dx > tol) ix = -ix;
if (dy < -tol || dy > tol) iy = -iy;
x = cx + (rx + dx * 2) * Math.cos(i * deg2rad + start);
y = cy + (ry + dy * 2) * Math.sin(i * deg2rad + start);
points.push(x, y);
}
И в последнем сегменте мы просто визуализируем то, что у нас есть.
Скорость определяется da
(угол дельта) в предыдущем шаге:
i = 2;
/// start line
ctx.beginPath();
ctx.moveTo(points[0], points[1]);
/// call loop
draw();
function draw() {
ctx.lineTo(points[i], points[i + 1]);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(points[i], points[i + 1]);
i += 2;
if (i < points.length) {
requestAnimationFrame(draw);
} else {
if (typeof callback === 'function')
callback();
}
}
}
(см комментарии ниже в разделе обновления)
Совет. Чтобы получить более реалистичный штрих, вы можете уменьшить globalAlpha
до, например, 0.7
.
Однако для правильной работы вам необходимо сначала нарисовать сплошное изображение на экранном холсте, а затем разбить этот экранный холст на основной холст (который имеет набор globalAlpha
) для каждого кадра, иначе штрихи будут перекрываться между каждой точкой (что выглядит не очень хорошо).
Для квадратов вы можете использовать тот же подход, что и для круга, но вместо использования радиуса и угла вы применяете вариации к линии. Сдвиньте дельты, чтобы линия не прямая.
Я немного изменил значения, но не стесняйтесь настраивать их больше, чтобы получить лучший результат.
Чтобы сделать круг «наклона» немного вы можете сначала поворачивать холст немного:
rotate = Math.random() * 0.5;
ctx.save();
ctx.translate(cx, cy);
ctx.rotate(-rotate);
ctx.translate(-cx, -cy);
и когда заканчивается цикл:
if (i < points.length) {
requestAnimationFrame(draw);
} else {
ctx.restore();
}
(включен в демо связаны выше) ,
круг будет выглядеть следующим образом:
Update
Для решения вопросов, упомянутых (комментарий поля слишком мал :-)): это на самом деле немного сложнее делать анимированные линии, особенно в таком случае, где вы круговое движение, а также случайная граница.
Реф. точка комментария 1: допуск тесно связан с радиусом, поскольку он определяет максимальное колебание. Мы можем изменить код, чтобы принять допуск (и ix/iy
, поскольку они определяют, как «быстрый» он будет меняться) на основе радиуса. Это то, что я подразумеваю под настройкой, чтобы найти это значение/сладкое пятно, которое хорошо работает со всеми размерами. Чем меньше круг, тем меньше вариации. Необязательно укажите эти значения в качестве аргументов функции.
Пункт 2: поскольку мы анимируем круг, функция становится асинхронной. Если мы рисуем два круга сразу после друг друга, они испортят холст, поскольку новые точки добавляются к пути из обоих кругов, которые затем поглаживаются перекрестно.
Мы можем обойти эту проблему, предоставляя механизм обратного вызова:
handDrawCircle(context, x, y, radius [, rounds] [, callback]);
, а затем, когда анимация завершены:
if (i < points.length) {
requestAnimationFrame(draw);
} else {
ctx.restore();
if (typeof callback === 'function')
callback(); /// call next function
}
Других вопросы один будет работать в с кодом, как есть (помните, что код подразумевается как пример, а не полное решение :-)) с толстыми линиями:
Когда мы рисуем сегмент по сегменту отдельно, холст не знает, как рассчитать угол стыка линии по отношению к предыдущему сегменту. Это часть концепции пути. Когда вы поглаживаете путь с несколькими сегментами, холст знает, по какому углу будет прикладом (конец линии). Таким образом, мы можем либо провести линию от начала до текущей точки, либо сделать промежуток между ними или только небольшими значениями lineWidth
.
Когда мы используем clearRect
(что сделает линию гладкой, а не «неровной», как если бы мы не использовали прозрачный промежуток времени, а просто рисовали сверху) нам нужно было бы рассмотреть возможность создания верхнего холста для анимации с и когда анимация заканчивается, мы рисуем результат на основном холсте.
Теперь мы начинаем видеть часть «сложности». Это, конечно, потому, что холст «низкоуровневый» в том смысле, что нам нужно обеспечить всю логику для всего. Мы в основном строим системы каждый раз, когда делаем что-то большее с холстом, чем просто рисуем простые фигуры и изображения (но это также дает большую гибкость).
Обращаюсь пару вопросов в this updated fiddle, но вам все равно нужно настроить затем немного (обновленный с верхней холст, обратный вызов, динамический толерантности).
Вау! Фантастический ответ. Это именно то, что я искал. Два вопроса, возможно, вы могли бы пролить свет на: 1) Я замечаю, когда я рисую меньший круг, это более ошибочно. 2) Когда я пытаюсь дважды вызвать функцию (чтобы нарисовать 2 или более круга), скрипт сходит с ума. http://jsfiddle.net/8w2GZ/4/ – user1477388
@ user1477388 привет и спасибо! Я обновил свой ответ с помощью возможных решений (поле комментариев получилось слишком крошечным :-)). – K3N
Спасибо за обновление. Это более математически, чем большинство приложений, которые я создаю (я строю бизнес-системы, которые, как правило, более логичны, чем математические). Я хотел бы быть хорошим в обоих, так есть ли ссылки, которые вы предложили бы? Например, что-то, что прольет свет на цели «ix, iy, rx, ry» и т. Д. И как вы пришли к разработке этого решения? – user1477388