HTML canvas art, generate coordinates data from sk

2019-01-26 19:23发布

问题:

I use HTML5 canvas to draw a heart shape:

<!DOCTYPE HTML>
<html>
  <head>
    <style>
      body {
        margin: 0px;
        padding: 0px;
      }
    </style>
  </head>
  <body>
    <canvas id="myCanvas" width="1366" height="600"></canvas>
    <script>
     var heart = [
        [707,359],[707,359],[707,359],[707,359],[708,358],[711,354],[713,352],[714,348],[716,345],[720,335],
        [721,334],[722,333],[724,332],[725,330],[727,327],[731,322],[734,320],[737,318],[740,314],[743,312],
        [745,311],[749,309],[753,308],[757,306],[761,303],[764,302],[767,302],[771,301],[774,301],[778,301],
        [783,301],[786,301],[790,300],[796,300],[801,300],[805,300],[809,300],[811,300],[815,302],[817,303],
        [820,305],[822,306],[824,307],[827,309],[830,311],[834,313],[836,314],[838,316],[841,318],[844,321],
        [845,324],[847,326],[849,328],[852,332],[853,334],[855,336],[857,337],[858,339],[860,341],[862,345],
        [863,349],[863,352],[864,356],[864,359],[865,362],[866,364],[868,368],[869,372],[870,377],[871,381],
        [872,384],[872,388],[872,392],[872,395],[872,399],[872,401],[872,405],[872,409],[871,412],[870,417],
        [869,419],[869,422],[867,427],[866,429],[865,434],[863,438],[862,442],[861,443],[860,445],[857,448],
        [854,451],[852,454],[849,456],[846,459],[843,460],[836,466],[835,466],[833,467],[821,475],[820,477],
        [819,478],[817,481],[815,483],[811,486],[808,487],[803,491],[802,491],[800,492],[795,493],[791,497],
        [789,498],[786,498],[780,502],[772,507],[770,510],[767,511],[762,516],[758,520],[756,524],[753,527],
        [750,529],[746,532],[741,534],[736,537],[732,538],[731,539],[735,537],[735,537],[735,537],[730,543],
        [729,546],[727,551],[726,553],[723,555],[721,558],[715,568],[714,570],[714,572],[713,575],[708,585],
        [708,586],[707,586],[704,583],[704,359],[704,359],[700,356],[698,352],[697,350],[696,345],[694,343],
        [693,340],[690,335],[688,335],[687,334],[683,332],[681,329],[677,326],[675,323],[672,319],[669,314],
        [668,312],[663,310],[660,310],[656,310],[653,309],[647,309],[644,308],[642,308],[637,307],[632,303],
        [628,301],[624,297],[621,297],[619,297],[616,297],[616,298],[616,299],[614,300],[620,302],[620,302],
        [620,302],[618,302],[612,302],[605,302],[598,302],[596,303],[594,305],[592,307],[590,309],[586,310],
        [583,313],[582,315],[579,319],[576,320],[573,323],[571,325],[569,327],[563,329],[560,333],[558,336],
        [557,338],[556,341],[555,343],[551,346],[549,349],[549,354],[549,357],[547,361],[546,367],[543,372],
        [542,375],[541,380],[540,381],[540,382],[540,382],[540,382],[540,382],[540,384],[540,386],[540,389],
        [539,391],[539,394],[539,398],[539,404],[539,408],[539,412],[539,416],[540,423],[542,428],[544,433],
        [549,436],[552,439],[555,442],[557,445],[560,448],[562,452],[564,459],[565,461],[560,449],[560,449],
        [561,450],[571,458],[580,466],[584,469],[587,473],[589,476],[591,477],[575,466],[575,466],[575,466],
        [576,466],[582,471],[587,475],[590,477],[594,481],[598,484],[601,489],[604,491],[607,495],[611,496],
        [617,498],[622,500],[626,501],[629,504],[633,508],[636,510],[642,515],[650,522],[651,525],[655,528],
        [657,529],[660,530],[663,530],[667,533],[671,534],[676,536],[679,537],[681,539],[683,540],[676,536],
        [676,536],[676,536],[679,539],[684,546],[687,548],[689,551],[692,554],[696,558],[698,563],[701,567],
        [702,571],[704,574],[705,577],[706,579],[707,580],[708,582],[709,586]
    ];
    var canvas = document.getElementById('myCanvas');
    var context = canvas.getContext('2d');

    context.beginPath();
    context.moveTo(707, 359);
    for(var i = 1; i < (heart.length - 1) ; i++){
        context.lineTo(heart[i][0], heart[i][1])

    }
    // line 1


    context.lineWidth = 2;
    context.strokeStyle = 'blue';
    context.stroke();
    </script>
  </body>
</html>

It looks like this:

Is there easier way to generate the coordinates data for other shapes? I hope there is some tool let me draw a shape sketch and generate coordinates data for me.

回答1:

Recorder

Fiddle demo

You need a way to record your points so you can reproduce them as a string, array etc.

This will record anything you draw onto the canvas:

var points = [],    /// holds strokes and points
    isDown = false, /// for draw mode
    prevX, prevY;   /// for draw mode

At the first click, record the first point:

canvas.onmousedown = function(e) {

    /// adjust mouse position (see below)
    var pos = getXY(e);

    /// this is used to draw a line
    prevX = pos.x;
    prevY = pos.y;

    /// add new stroke
    points.push([]);

    /// record point in this stroke
    points[points.length - 1].push([pos.x, pos.y]);

    /// we are in draw mode   
    isDown = true;
}

Then it's simply a matter of pushing all the points drawn to the current stroke:

canvas.onmousemove = function(e) {

    if (!isDown) return;

    var pos = getXY(e);

    /// draw a line from previous point to this        
    ctx.beginPath();
    ctx.moveTo(prevX, prevY);
    ctx.lineTo(pos.x, pos.y);
    ctx.stroke();

    /// set previous to this point
    prevX = pos.x;
    prevY = pos.y;

    /// record to current stroke
    points[points.length - 1].push([pos.x, pos.y]);
}

And on mouse up; in the demo it produces the points as string which can be copied directly into your JS code:

canvas.onmouseup = function() {
    isDown = false;
    coords.innerHTML = JSON.stringify(points);
}

We adjust the mouse position so it becomes relative to canvas:

function getXY(e) {
    var r = canvas.getBoundingClientRect();
    return {x: e.clientX - r.left, y: e.clientY - r.top}
}

Render

Now that we have the points recorded we need a way to reproduce them.

This snippet is able to render the strokes and points we just recorded - for the demo I set red color to differentiate between rendered strokes and drawn:

function renderPoints(points) {

    ctx.clearRect(0, 0, canvas.width, canvas.height);
    ctx.strokeStyle = '#f00';

    /// get a stroke
    for(var i = 0, t, p, pts; pts = points[i]; i++) {

        /// render stroke
        ctx.beginPath();
        ctx.moveTo(pts[0][0], pts[0][2]);
        for(t = 1; p =pts[t]; t++) {
            ctx.lineTo(p[0], p[1]);
        }
        ctx.stroke();
    }
    ctx.strokeStyle = '#000';
}

Conclusion

This heart (?!):

produces these points for you:

[[[103,99],[103,100],[103,98],[105,93],[115,79],[129,61],[139,52],[145,49],[149,49],[152,51],[154,54],[155,57],[156,65],[154,79],[152,89],[149,99],[145,110],[139,124],[131,142],[118,163],[108,176],[101,184],[97,185],[94,187],[87,187],[76,180],[65,166],[60,156],[54,145],[46,119],[41,98],[39,84],[39,81],[39,79],[39,77],[39,76],[40,75],[42,74],[43,72],[46,69],[47,67],[48,67],[49,67],[50,67],[51,68],[52,69],[54,70],[61,75],[71,87],[82,101],[84,105],[87,108],[89,111],[89,112],[89,113],[89,114],[89,115],[89,116],[90,118]]]

They can be given directly to the render method, or the stroke/point array can be made more sophisticated by that you prefix it with some code to allow you to copy-and-paste the produced string directly, for example:

var code = '    var points = ' + JSON.stringify(points) + ';';

Fiddle demo



回答2:

Yes...

How

Apps like Adobe Illustrator and Inkscape will let you draw shapes by "hand" (mouse) using lines and curves. They have a line tool that creates lines and a pen tool that creates curves.

Illustrator is probably the design industry "gold standard" but is not free. Inkscape has less features (and a few more bugs) but is free.

Since your drawing is made up of lines and curves rather than pixels, your drawing is called a "vector" drawing as opposed to a "pixel/raster" drawing.

Why

The importance of your drawing being made up of lines/curves is that you can export your drawing into a file format called SVG. If you look in that exported SVG file, you will see commands that you can fairly easily translate into html5 canvas drawing commands. There are even translators that will automatically convert your Illustrator/Inkscape drawing into html5 canvas.

An example

Here is your heart drawn with just 4 curves.

Demo: http://jsfiddle.net/m1erickson/8Ja8A/

Here's the code for your heart with 4 curves:

context.beginPath();
context.moveTo(150,150);
context.bezierCurveTo( 150,120, 100,120, 100, 150);
context.bezierCurveTo( 100,180, 150,185, 150, 210 );
context.bezierCurveTo( 150,185, 200,180, 200, 150 );  
context.bezierCurveTo( 200,120, 150,120, 150, 150 );
context.lineWidth=2; 
context.stroke();

From here...

You can create your heart (or any shape you desire!) like this:

  • Draw a heart shape in Illustrator/Inkscape using the pen (curve) tool.
  • Export your heart to SVG.
  • Use the curve coordinates in the SVG file to create html5 canvas curve commands.