Ответ 1
Здесь уже есть хорошие решения. Я хотел бы добавить варианты того, что уже было представлено - вариантов некоторых тригонометрии не так много, если вы хотите имитировать рисованные круги.
Сначала я бы рекомендовал записать реальный ручной кружок. Вы можете записывать точки, а также 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
В основном цикле мы не скатываемся случайным образом, а увеличиваем со случайным значением, а затем линейно увеличиваем его с этим значением, отбрасываем его, если мы находимся на границах (допуск).
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();
}
(включены в демонстрацию, указанную выше).
Круг будет выглядеть следующим образом:
Обновить
Чтобы справиться с упомянутыми проблемами (поля комментариев слишком маленькие :-)): на самом деле это немного сложнее для создания анимированных строк, особенно в таком случае, где вы круговое движение, а также случайная граница.
Ссылка точка комментария 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
(который сделает линию гладкой, а не "дряблой", поскольку, когда мы не используем прозрачную между ними, а просто делаем сверху), нам нужно будет рассмотреть возможность создания верхнего холста для анимации с анимацией и при анимации заканчивается, мы рисуем результат на основном холсте.
Теперь мы начинаем видеть часть "сложности". Это, конечно, потому, что холст "низкоуровневый" в том смысле, что нам нужно обеспечить всю логику для всего. Мы в основном строим системы каждый раз, когда делаем что-то большее с холстом, чем просто рисуем простые фигуры и изображения (но это также дает большую гибкость).
Я рассматриваю пару проблем в этой обновленной скрипке, но вам все равно нужно немного подкорректировать (обновлено с верхним холстом, обратным вызовом, динамическим допуском).