使用 html5 画布将图像剪辑成多边形的可重用函数

Posted

技术标签:

【中文标题】使用 html5 画布将图像剪辑成多边形的可重用函数【英文标题】:A reusable function to clip images into polygons using html5 canvas 【发布时间】:2014-09-06 15:27:54 【问题描述】:

猜测帖子的标题可能需要编辑,但现在我不知道问题出在哪里。我已经在这里和其他地方阅读了类似问题的页面和答案。一个 Stack Overflow 的答案特别接近,但我不明白。

我想要一个函数,在画布上以所需坐标绘制多边形,并用从文件加载的一些背景图像填充它们(足够大,不需要平铺)。三角形适合测试。显然我应该使用drawImage和clip,并给多边形一个边框,我可以为clip和笔画重新使用相同的路径。显然我应该保持顺序

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

还在某处读到一次加载图像就足够了。 (如果你想让我引用所有这些假设的来源,我会寻找我在哪里看到的。大部分在 Stack Overflow 上)

html 是空的

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

第一种方法,假装浏览器会等待图片加载:

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



这会绘制所有三个三角形,但没有裁剪图像。

第二次尝试,在 image.onload 中使用 drawImage:

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


这个确实绘制了剪裁的图像,但只有一个三角形,最后一个。只是注释掉保存和恢复并没有帮助。

所以,我不明白加载图像、保存、恢复以及可能还有一百万种其他事情。错误在哪里?

【问题讨论】:

【参考方案1】:

我看到你已经了解了剪辑的基础知识:

保存上下文、定义路径、剪辑、drawImage、恢复上下文。

如果您希望笔触与剪切的图像略微重叠,您可以在恢复后进行笔触。

如果您不希望笔划与剪切的图像重叠,您可以在剪切之前进行笔划。

这是示例代码和演示: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; 
    canvasborder: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>

【讨论】:

暂时谢谢。我将不得不处理它:) 现在我得到了 2px 宽度的线,其中一半可以承受剪裁。但是我必须先阅读回调,然后我才能自己尝试并正确感谢您:) 看起来 start () 是主要的。我们刚刚把所有的程序都放到了一个图像加载器中?请这个问题是一个真正的问题,而不是一个修辞问题。 现在当然可以了。谢谢你。但是我这个回调的事情是一件丑陋的事情。应该调用那些调用函数 loadImagesAndThenContinueIfAllRight () 什么? :d 是的,图像加载器需要时间来完成加载过程,然后它会调用 start()。所以,是的,start() 就像 main() 函数。传统上,您 (1) 在图像加载器之前创建全局变量,(2) 执行图像加载器,(3) 在 start() 中,设置先前创建的全局变量,(4) 现在一切都已配置,执行代码剪裁/绘图。【参考方案2】:

@markE

示例中的图像已消失。 我从上面的屏幕截图中剪下它们并上传到我们自己的网站并克隆/更新了小提琴:http://jsfiddle.net/6gawtudc/

由于缺乏声誉点,我无法对之前的答案发表评论。 请有人更新答案或重新上传图片

imageURLs.push("https://images.v3.webhome.nl/flags_for_polygon/norway.png");
imageURLs.push("https://images.v3.webhome.nl/flags_for_polygon/sweden.png");

【讨论】:

以上是关于使用 html5 画布将图像剪辑成多边形的可重用函数的主要内容,如果未能解决你的问题,请参考以下文章

在 HTML5 画布中剪切多个图像

javascript - 如何使用 drawImage/putImageData 进行剪辑

svg 剪辑图像并显示中风

如何创建中间有孔的画布 html5 剪辑区域?

如何在 HTML5 画布中剪辑 INSIDE 形状?

如何在 Flash html5 画布中更改影片剪辑的宽度?