A reusable function to clip images into polygons using html5 canvas

家住魔仙堡 提交于 2020-01-01 14:07:07

问题


Guess the title of the post may need editing, but for now I don't know where the problems are. I have read pages and answers to similar questions, here and elsewhere. One Stack Overflow answer is especially close, but I don't understand it.

I want a function, to draw polygons on canvas at desired coordinates and to fill them with some background image loaded from a file (large enough that no tiling is needed). Triangles would be fine for a test. Apparently I should use drawImage and clip, and to give the polygon a border, I can resuse the same path for the clip and the stroke. Also apparently I should keep the order of

- define path
- save
- clip
- drawImage
- restore
- stroke.

Also read somewhere that it is enough to load the image once. (If uou want me to quote sources for all these assumptions, I will look for where I saw them. Most of them on Stack Overflow)

The HTML is an otherwise empty

<body onload = "main ();"></body>

First approach, pretending that the browser will wait for the picture to load:

var ctx, img;
var image_path = 'bg.jpg';

function main () {

    var CANVAS_SIZE = 600;
    var view_field_cnv = document.createElement ('canvas');
    view_field_cnv.width  = CANVAS_SIZE;
    view_field_cnv.height = CANVAS_SIZE;
    view_field_cnv.style.border = "1px solid";
    document.body.appendChild (view_field_cnv);
    ctx = view_field_cnv.getContext ('2d');

    img = document.createElement ('img');
    img.src = image_path;

    place_triangle (0, 0);
    place_triangle (300, 300);
    place_triangle (500, 500);
    place_triangle (0, 0);

}

function place_triangle (x, y) {

    console.log (x, y);

    ctx.beginPath ();
    ctx.moveTo (x + 10, y);
    ctx.lineTo (x + 110, y);
    ctx.lineTo (x + 60, y + 40);
    ctx.closePath ();

    img = document.createElement ('img');
    img.src = image_path;

    ctx.save ();
    ctx.clip ();
    ctx.drawImage (img, x, y);
    ctx.restore ();
    ctx.stroke ();


}

That draws all three triangles but no clipped images.

Second try, with drawImage inside image.onload:

var ctx;
var image_path = 'bg.jpg';

function main () {

    var CANVAS_SIZE = 600;
    var view_field_cnv = document.createElement ('canvas');
    view_field_cnv.width  = CANVAS_SIZE;
    view_field_cnv.height = CANVAS_SIZE;
    view_field_cnv.style.border = "1px solid";
    document.body.appendChild (view_field_cnv);
    ctx = view_field_cnv.getContext ('2d');

    place_triangle (0, 0);
    place_triangle (300, 300);
    place_triangle (500, 500);
    place_triangle (0, 0);

}

function place_triangle (x, y) {

    console.log (x, y);

    var img;

    ctx.beginPath ();
    ctx.moveTo (x + 10, y);
    ctx.lineTo (x + 110, y);
    ctx.lineTo (x + 60, y + 40);
    ctx.closePath ();

    img = document.createElement ('img');
    img.src = image_path;
    img.onload = function () {

        ctx.save ();
        ctx.clip ();
        ctx.drawImage (img, x, y);
        ctx.restore ();
        ctx.stroke ();
    }

}

This one does draw the clipped image, but only one triangle, the last one. Just commenting out save and restore doesn't help.

So, I don't understand loading images, saving, restoring and probably a million other things. Where be the bugs?


回答1:


I see you already understand the basics of clipping:

  • save context, define path, clip, drawImage, restore context.

  • you can stroke after restore if you want the stroke to slightly overlap the clipped image.

  • you can stroke before clipping if you don't want the stroke to overlap the clipped image.

Here's example code and a Demo: http://jsfiddle.net/m1erickson/p0fup425/

<!doctype html>
<html>
<head>
<link rel="stylesheet" type="text/css" media="all" href="css/reset.css" /> <!-- reset css -->
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>
<style>
    body{ background-color: ivory; }
    canvas{border:1px solid red;}
</style>
<script>
$(function(){

    var canvas=document.getElementById("canvas");
    var ctx=canvas.getContext("2d");

    // image loader
    // put the paths to your images in imageURLs[]
    var imageURLs=[];  
    // push all your image urls!
    imageURLs.push("https://dl.dropboxusercontent.com/u/139992952/multple/norwayFlag.jpg");
    imageURLs.push("https://dl.dropboxusercontent.com/u/139992952/multple/swedishFlag.jpg");

    // the loaded images will be placed in images[]
    var imgs=[];

    var imagesOK=0;
    loadAllImages(start);

    function loadAllImages(callback){
        for (var i=0; i<imageURLs.length; i++) {
            var img = new Image();
            imgs.push(img);
            img.onload = function(){ 
                imagesOK++; 
                if (imagesOK>=imageURLs.length ) {
                    callback();
                }
            };
            img.onerror=function(){alert("image load failed");} 
            img.crossOrigin="anonymous";
            img.src = imageURLs[i];
        }      
    }

    function start(){

        // the imgs[] array now holds fully loaded images
        // the imgs[] are in the same order as imageURLs[]

        // clip image#1
        clippingPath([10,70,50,10,90,70],imgs[0],10,10);

        // clip image#2
        clippingPath([10,170,50,110,90,170],imgs[1],10,110);

        // append the original images for demo purposes
        document.body.appendChild(imgs[0]);
        document.body.appendChild(imgs[1]);

    }

    function clippingPath(pathPoints,img,x,y){

        // save the unclipped context
        ctx.save();

        // define the path that will be clipped to
        ctx.beginPath();
        ctx.moveTo(pathPoints[0],pathPoints[1]);
        // this demo has a known number of polygon points
        // but include a loop of "lineTo's" if you have a variable number of points
        ctx.lineTo(pathPoints[2],pathPoints[3]);
        ctx.lineTo(pathPoints[4],pathPoints[5]);
        ctx.closePath();    

        // stroke the path
        // half of the stroke is outside the path
        // the outside part of the stroke will survive the clipping that follows
        ctx.lineWidth=2;
        ctx.stroke();

        // make the current path a clipping path
        ctx.clip();

        // draw the image which will be clipped except in the clipping path
        ctx.drawImage(img,x,y);

        // restore the unclipped context (==undo the clipping path)
        ctx.restore();
    }


}); // end $(function(){});
</script>
</head>
<body>
    <p>Images clipped inside triangular canvas paths</p>
    <canvas id="canvas" width=150 height=200></canvas>
    <p>Original Images</p>
</body>
</html>


来源:https://stackoverflow.com/questions/25701798/a-reusable-function-to-clip-images-into-polygons-using-html5-canvas

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!