Handdrawn circle simulation in HTML 5 canvas

13,008

Solution 1

Here are some basics I created for this answer:

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

Basically, when you draw a circle, you need to account for hand imperfections. So, in the following code:

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

Pay attention how vertical radiusError changes when the angle (and the position) grows. You are welcome to play with this fiddle until you get a "feel" what component does what. E.g. it would make sense to introduce another component to radiusError that emulates "unsteady" hand by slowly changing it my random amounts.

There are many different ways to do this. I choose trig functions for the simplicity of the simulation, as speed is not a factor here.

Update:

This, for example, will make it less perfect:

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

Obviously, the center of the circle is at (200,200), as the formula for drawing a circle (rather, ellipsis with vertical radius RY and horizontal radius RX) with trigonometric functions is

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

Solution 2

Your task seems to have 3 requirements:

  1. A hand-drawn shape.
  2. An “organic” rather than “ultra-precise” stroke.
  3. Revealing the circle incrementally instead of all-at-once.

To get started, check out this nice on-target demo by Andrew Trice.

This amazing circle is hand drawn by me (you can laugh now...!)

My amazing circle created with Andrew's technique

Andrew's demo does steps 1 and 2 of your requirements.

It lets you hand draw a circle (or any shape) using an organic looking “brush effect” instead of the usual ultra-precise lines normally used in canvas.

It achieves the “brush effect” by by repeated drawing a brush image between hand drawn points

Here’s the demo:

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

And the code is available on GitHub:

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

Andrew Trice’s demo draws-and-forgets the lines that make up your circle.

Your task would be to impliment your third requirement (remembering strokes):

  • Hand draw a circle of your own,
  • Save each line segment that makes up your circle in an array,
  • “Play” those segements using Andrew’s stylized brush technique.

Results: A hand-drawn and stylized circle that appears incrementally instead of all at once.

You have an interesting project…If you feel generous, please share your results!

Solution 3

See live demo here. Also available as a 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)
Share:
13,008
user1477388
Author by

user1477388

Authored open-source project PHP One, an MVC framework for PHP designed like Microsoft's ASP.NET MVC. https://github.com/DominicArchual/Php-One

Updated on June 14, 2022

Comments

  • user1477388
    user1477388 almost 2 years

    The following code creates a circle in HTML 5 Canvas using jQuery:

    Code:

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

    I am trying to simulate any of the following types of circles:

    examples

    I have researched and found this article but was unable to apply it.

    I would like for the circle to be drawn rather than just appear.

    Is there a better way to do this? I'm sensing there's going to be a lot of math involved :)

    P.S. I like the simplicity of PaperJs, maybe this would be the easiest approach using it's simplified paths?

  • markE
    markE over 10 years
    +1 Inventive use of using radius "errors" to introduce variety to the circle!
  • user1477388
    user1477388 over 10 years
    This is an excellent answer. How does the png part work? Where can I view it? I would like to change the size of the stroke (and maybe the color). Also, I was originally wanting to animate the drawing of the circle (not just have it appear on canvas). Thanks again for a great answer to this question!
  • ComFreek
    ComFreek over 10 years
    @user1477388 I've created an animated version (may not be the cleanest though): jsfiddle.net/TPDmn/2
  • user1477388
    user1477388 over 9 years
    Nice work! Thanks for sharing that. From a programming perspective, I typically don't like to see special characters such as λ and δ in the code though :(
  • davidcondrey
    davidcondrey over 9 years
    Thanks. The majority of the credit goes to Patrick Surry whom I forked. I just improved upon their code. You have a point, I will def. make a note to adjust that (may get to it this evening or sometime this week). Thanks for checking it out.