2013-09-08 5 views
13

following code создает круг в HTML 5 Canvas с помощью JQuery:Handdrawn моделирования круга в HTML 5 холста

Код:

//get a reference to the canvas 
var ctx = $('#canvas')[0].getContext("2d"); 

DrawCircle(75, 75, 20); 

//draw a circle 
function DrawCircle(x, y, radius) 
{ 
    ctx.beginPath(); 
    ctx.arc(x, y, radius, 0, Math.PI*2, true); 
    ctx.fillStyle = 'transparent'; 
    ctx.lineWidth = 2; 
    ctx.strokeStyle = '#003300'; 
    ctx.stroke(); 
    ctx.closePath(); 
    ctx.fill(); 
} 

Я пытаюсь имитировать любого из следующих типов круги:

examples

Я изучаю и обман. article, но не смог его применить.

Я хотел бы, чтобы круг был нарисован, а не просто появился.

Есть ли лучший способ сделать это? Я чувствую, что будет задействовано много математики :)

P.S. Мне нравится простота PaperJs, может быть, это был бы самый простой подход с использованием упрощенных путей?

ответ

11

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

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

Это здесь решение создает круги, такие как эти:

Snapshot

Вы можете изменить цвет, толщину и т.д., установив strokeStyle, lineWidth и т.д., как обычно.

Чтобы нарисовать круг просто позвонить:

handDrawCircle(context, x, y, radius [, rounds] [, callback]); 

(callback предоставляется как анимация делает функцию асинхронно).

код разделяется на два сегмента:

  1. Генерировать точки
  2. Анимируйте точек

Низкоур.инициализ:

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(); 
} 

(включен в демо связаны выше) ,

круг будет выглядеть следующим образом:

Snapshot tilted

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, но вам все равно нужно настроить затем немного (обновленный с верхней холст, обратный вызов, динамический толерантности).

+0

Вау! Фантастический ответ. Это именно то, что я искал. Два вопроса, возможно, вы могли бы пролить свет на: 1) Я замечаю, когда я рисую меньший круг, это более ошибочно. 2) Когда я пытаюсь дважды вызвать функцию (чтобы нарисовать 2 или более круга), скрипт сходит с ума. http://jsfiddle.net/8w2GZ/4/ – user1477388

+1

@ user1477388 привет и спасибо! Я обновил свой ответ с помощью возможных решений (поле комментариев получилось слишком крошечным :-)). – K3N

+0

Спасибо за обновление. Это более математически, чем большинство приложений, которые я создаю (я строю бизнес-системы, которые, как правило, более логичны, чем математические). Я хотел бы быть хорошим в обоих, так есть ли ссылки, которые вы предложили бы? Например, что-то, что прольет свет на цели «ix, iy, rx, ry» и т. Д. И как вы пришли к разработке этого решения? – user1477388

3

Ваша задача, кажется, 3 требования:

  1. Ручной обращается форма.
  2. «Органический», а не «сверхточный» штрих.
  3. Выявление круга пошагово, а не по-разному.

Чтобы начать работу, ознакомьтесь с этой замечательной демонстрацией на целевом демо Andrew Trice.

Этот удивительный круг рисованной мной (вы можете смеяться сейчас ...!) Демо

My amazing circle created with Andrew's technique

Эндрю делает шаги 1 и 2 из ваших требований.

Это позволяет вам нарисовать круг (или любую форму), используя органический «эффект кисти» вместо обычных сверхточных линий, обычно используемых в холсте.

Это достигается «эффект кисти» по повторным рисуя изображение кисти между рисованными точками

Вот демо:

http://tricedesigns.com/portfolio/sketch/brush.html#

И код доступен на GitHub:

https://github.com/triceam/HTML5-Canvas-Brush-Sketch

Демо-версия Эндрю Триса рисует и-забывает линии, которые делают по кругу.

Ваша задача будет заключаться в impliment ваше третье требование (запоминание ходов):

  • Рука нарисовать круг ваших,
  • Сохранить в каждой строке сегмент, который делает свой круг в массиве,
  • «Сыграй» эти отряды с помощью стилизованной кисти Эндрю.

Результаты: рисованный и стилизованный круг, который появляется постепенно, а не сразу.

У вас есть интересный проект ... Если вы чувствуете себя щедрым, поделитесь своими результатами!

7

Вот некоторые основы я создал для этого ответа:

http://jsfiddle.net/Exceeder/TPDmn/

В основном, когда вы рисуете круг, вы должны учитывать ручное несовершенство. Таким образом, в следующем коде:

var img = new Image(); 
img.src="data:image/png;base64,..."; 

var ctx = $('#sketch')[0].getContext('2d'); 
function draw(x,y) { 
    ctx.drawImage(img, x, y); 
} 

for (var i=0; i<500; i++) { 
    var radiusError = +10 - i/20; 
    var d = 2*Math.PI/360 * i; 
    draw(200 + 100*Math.cos(d), 200 + (radiusError+80)*Math.sin(d)); 
} 

Обратите внимание, как вертикальный радиус изменяется при увеличении угла (и положения). Вы можете играть с этой скрипкой, пока не почувствуете, какой компонент что-то делает. Например. было бы целесообразно ввести еще один компонент в radiusError, который эмулирует «нестационарную» руку, медленно меняя ее мои случайные суммы.

Существует много разных способов сделать это. Я выбираю триггерные функции для простоты моделирования, поскольку скорость здесь не является фактором.

Update:

Это, например, сделает его менее совершенным:

var d = 2*Math.PI/360 * i; 
var radiusError = +10 - i/20 + 10*Math.sin(d); 

Очевидно, что центр окружности находится в точке (200,200), как формула для рисования круга (а , многоточие с вертикальным радиусом RY и горизонтальным радиусом RX) с тригонометрическими функциями является

x = centerX + RX * cos (angle) 
y = centerY + RY * sin (angle) 
+0

+1 изобретательских использование с помощью радиуса «ошибки», чтобы ввести разнообразие в круг! – markE

+0

Это отличный ответ. Как работает png-часть? Где я могу просмотреть его? Я хотел бы изменить размер штриха (и, возможно, цвет). Кроме того, мне изначально хотелось оживить рисунок круга (а не просто появляться на холсте). Еще раз спасибо за отличный ответ на этот вопрос! – user1477388

+2

@ user1477388 Я создал анимированную версию (возможно, не самая чистая): http://jsfiddle.net/TPDmn/2/ – ComFreek

1

See live demo here. Также доступен как gist.

<div id="container"> 
    <svg width="100%" height="100%" viewBox='-1.5 -1.5 3 3'></svg> 
</div> 

#container { 
    width:500px; 
    height:300px; 
} 
path.ln { 
    stroke-width: 3px; 
    stroke: #666; 
    fill: none; 
    vector-effect: non-scaling-stroke; 
    stroke-dasharray: 1000; 
    stroke-dashoffset: 1000; 
    -webkit-animation: dash 5s ease-in forwards; 
    -moz-animation:dash 5s ease-in forwards; 
    -o-animation:dash 5s ease-in forwards; 
    animation:dash 5s ease-in forwards; 
} 

@keyframes dash { 
    to { stroke-dashoffset: 0; } 
} 

function path(δr_min,δr_max, el0_min, el0_max, δel_min,δel_max) { 

    var c = 0.551915024494; 
    var atan = Math.atan(c) 
    var d = Math.sqrt(c * c + 1 * 1), r = 1; 
    var el = (el0_min + Math.random() * (el0_max - el0_min)) * Math.PI/180; 
    var path = 'M'; 

    path += [r * Math.sin(el), r * Math.cos(el)]; 
    path += ' C' + [d * r * Math.sin(el + atan), d * r * Math.cos(el + atan)]; 

    for (var i = 0; i < 4; i++) { 
     el += Math.PI/2 * (1 + δel_min + Math.random() * (δel_max - δel_min)); 
     r *= (1 + δr_min + Math.random()*(δr_max - δr_min)); 
     path += ' ' + (i?'S':'') + [d * r * Math.sin(el - atan), d * r * Math.cos(el - atan)]; 
     path += ' ' + [r * Math.sin(el), r * Math.cos(el)]; 
    } 

    return path; 
} 

function cX(λ_min, λ_max, el_min, el_max) { 
    var el = (el_min + Math.random()*(el_max - el_min)); 
    return 'rotate(' + el + ') ' + 'scale(1, ' + (λ_min + Math.random()*(λ_max - λ_min)) + ')'+ 'rotate(' + (-el) + ')'; 
} 

function canvasArea() { 
    var width = Math.floor((Math.random() * 500) + 450); 
    var height = Math.floor((Math.random() * 300) + 250); 
    $('#container').width(width).height(height); 
} 
d3.selectAll('svg').append('path').classed('ln', true) .attr('d', path(-0.1,0, 0,360, 0,0.2)).attr('transform', cX(0.6, 0.8, 0, 360)); 

setTimeout(function() { location = '' } ,5000) 
+1

Хорошая работа! Спасибо, что поделились этим. С точки зрения программирования я обычно не люблю видеть специальные символы, такие как λ и δ в коде, хотя :( – user1477388

+1

Спасибо. Большая часть кредита принадлежит [Patrick Surry] (https://gist.github.com/patricksurry/11087975), которых я разветвил, я просто улучшил их код. У вас есть точка, я буду об этом задуматься, сделайте заметку, чтобы приспособить это (может дойти до этого сегодня или когда-нибудь на этой неделе). Спасибо, что проверили это. – davidcondrey

1

, столкнувшись с подобными задачами, я создал рисунок библиотеку мультяшного стиля JS для SVG или HTML5 Canvas. Он работает как плагин для Raphael.js, D3.js или SVG.js или как lib для Canvas. Он называется comic.js и может быть найден на github. Среди других форм он может рисовать похожие круги, которые вы просили. Он основан на значении article.

Это то, что он может производить:

comic.js screenshot

+0

Хороший вклад, спасибо за обмен. Кроме того, вы поддерживали D3. Однако ссылка на версию D3 не работает в Chrome (на холсте ничего не нарисовано). – user1477388

+0

Спасибо! Я не могу воспроизвести вашу проблему . Для меня с использованием версии Chrome версии 39.0.2171.95 (64-разрядная версия) на Ubuntu 14.04 работает со всеми тремя примерами.Можете ли вы переустановить Chrome и, если он все еще терпит неудачу, можете ли вы дать мне версию Chrome и версию ОС? –

+0

Сейчас, кажется, работает правильно! Я был в Windows 7. – user1477388