如何在画布中填充图案并沿着形状弯曲?

时间:2013-09-15 09:57:55

标签: javascript canvas html5-canvas

我有一个像image like this这样的图像,我想填充像like this这样的模式来生成像like this这样的结果。

我可以使用以下代码填充图案,但我不知道如何沿着衣领形状正确地弯曲图案,因为它应该看起来像真实但我的结果变得像这样。 like this

这是我的示例脚本

$(function(){
drawCanvas("body","collar","images/collar.png", 180);

function drawCanvas(overlayType, canvasID, imageSource, degreeRotate){
    var canvas=document.getElementById(canvasID);
    var ctx=canvas.getContext("2d");

    var imgBody=new Image();
    var imgPattern=new Image();
    imgPattern.onload=function(){

        imgBody.onload=function(){
            start();
        }

        imgBody.src=imageSource;
    }
    imgPattern.src="images/pattern.png";

    function start(){
        ctx.drawImage(imgBody,0,0);
        if(overlayType=="body"){
            ctx.globalCompositeOperation="source-atop";
            ctx.globalAlpha=.85;

            var pattern = ctx.createPattern(imgPattern, 'repeat');
            ctx.fillStyle = pattern;
            ctx.rect(0, 0, canvas.width, canvas.height);
            ctx.rotate(degreeRotate * Math.PI/180);

            ctx.fill();
            ctx.translate(150,0);

            ctx.globalAlpha=.1;     
            ctx.drawImage(imgBody,150,0);   
        }
    }
}});

有人可以指导我如何管理图案沿着侧领形状曲线看起来像真的吗?

2 个答案:

答案 0 :(得分:1)

您可以对多边形进行三角测量,然后弯曲网格。然后你可以用图案填充网格。以下是Java中三角剖分的示例:How does this code for delaunay triangulation work?。这是一个三角测量的例子和一些删除长边的工作。这是一个2d点集的凹形船体。

enter image description here

enter image description here

答案 1 :(得分:1)

您可以通过简单地垂直切割和偏移图案来实现这一目标

原创“领”形象:

enter image description here

衣领饰有弧形格纹纹理

enter image description here

**方法:*

  • 使用方格纹理创建图像。
  • 将该纹理图像绘制到临时画布上。
  • 获取临时画布的图像数据。
  • 对于每列像素,将整个列偏离衣领的曲线。
  • 二次曲线很好地拟合你的项圈曲线,所以我在例子中用它来确定Y偏移量。
  • 将图像数据放回临时画布上。
  • (你现在有一个弯曲的方格图案适用于你的衣领图像)。
  • 在主画布上绘制衣领图像。
  • 设置context.globalCompositeOperation =“source-atop”
  • 将纹理从临时画布绘制到主画布上。
  • (合成模式仅在衣领上绘制纹理 - 而不是背景)

这是代码和小提琴:http://jsfiddle.net/m1erickson/hdXyk/

<!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; padding:20px; }
    canvas{border:1px solid red;}
</style>

<script>
$(function(){

    // get canvas references (canvas=collar, canvas1=texture)
    var canvas=document.getElementById("canvas");
    var ctx=canvas.getContext("2d");
    var canvas1=document.getElementById("canvas1");
    var ctx1=canvas1.getContext("2d");

    // preload the texture and collar images before starting

    var textureImg,collarImg;
    var imageURLs=[];
    var imagesOK=0;
    var imgs=[];
    imageURLs.push("https://dl.dropboxusercontent.com/u/139992952/stackoverflow/checkered.png");
    imageURLs.push("https://dl.dropboxusercontent.com/u/139992952/stackoverflow/collar.png");
    loadAllImages();

    function loadAllImages(callback){
        for (var i = 0; i < imageURLs.length; i++) {
            var img = new Image();
            img.crossOrigin="anonymous";
            imgs.push(img);
            img.onload = function(){ 
                imagesOK++; 
                if (imagesOK==imageURLs.length ) {
                      textureImg=imgs[0];
                      collarImg=imgs[1];
                      start();
                }
            }; 
            img.src = imageURLs[i];
        }      
    }


    function start(){

        // set both canvas dimensions
        canvas.width=collarImg.width;
        canvas.height=collarImg.height+5;
        canvas1.width=textureImg.width;
        canvas1.height=textureImg.height;

        // draw the textureImg on canvas1
        ctx1.drawImage(textureImg,0,0,canvas1.width,canvas1.height);

        // curve the texture into a collar shaped curved 
        curveTexture(collarImg.width,collarImg.height);

        // draw the collarImg on canvas
        ctx.drawImage(collarImg,0,0);

        // set compositing to source-atop
        // any new drawing will ONLY fill existing non-transparent pixels
        ctx.globalCompositeOperation="source-atop";

        // draw the curved texture from canvas1 onto the collar of canvas
        // (the existing pixels are the collar, so only the collar is filled)
        ctx.drawImage(canvas1,0,0);



    }

    function curveTexture(w,h){

        // define a quadratic curve that fits the collar bottom
        // These values change if the collar image changes (+5,-32)
        var x0=0;
        var y0=h+5;
        var cx=w/2;
        var cy=h-32;
        var x1=w;
        var y1=h+5;

        // get a,b,c for quadratic equation
        // equation is used to offset columns of texture pixels
        // in the same shape as the collar
        var Q=getQuadraticEquation(x0,y0,cx,cy,x1,y1);

        // get the texture canvas pixel data
        // 2 copies to avoid self-referencing
        var imageData0 = ctx1.getImageData(0,0,w,h);
        var data0 = imageData0.data;
        var imageData1 = ctx1.getImageData(0,0,w,h);
        var data1 = imageData1.data;

        // loop thru each vertical column of pixels
        // Offset the pixel column into the shape of the quad-curve
        for(var y=0; y<h; y++) {
          for(var x=0; x<w; x++) {

              // the pixel to write
              var n=((w*y)+x)*4;
              // the vertical offset amount
              var yy=parseInt(y+h-(Q.a * x*x + Q.b*x + Q.c));
              // the offset pixel to read
              var nn=((w*yy)+x)*4;

              // offset this pixel by the quadCurve Y value (yy)
              data0[n+0]=data1[nn+0];
              data0[n+1]=data1[nn+1];
              data0[n+2]=data1[nn+2];
              data0[n+3]=data1[nn+3];
          }
        }
        ctx1.putImageData(imageData0,0,0);
    }


    // Quadratic Curve: given x coordinate, find y coordinate
    function getQuadraticY(x,Q){
        return(Q.a * x*x + Q.b*x + Q.c);
    }

    // Quadratic Curve: 
    // Given: start,control,end points
    // Find: a,b,c in quadratic equation ( y=a*x*x+b*x+c )
    function getQuadraticEquation(x0,y0,cx,cy,x2,y2){
        // need 1 more point on q-curve, so calc its midpoint XY
        // Note: since T=0.5 therefore TT=(1-T)=0.5 also [so could simplify]
        var T=0.50;
        var TT=1-T;
        var x1=TT*TT*x0+2*TT*T*cx+T*T*x2;
        var y1=TT*TT*y0+2*TT*T*cy+T*T*y2;
        var A = ((y1-y0)*(x0-x2) 
              + (y2-y0)*(x1-x0))/((x0-x2)*(x1*x1-x0*x0) 
              + (x1-x0)*(x2*x2-x0*x0));
        var B = ((y1-y0)-A*(x1*x1-x0*x0))/(x1-x0);
        var C = y0-A*x0*x0-B*x0;
        return({a:A,b:B,c:C});
    }


}); // end $(function(){});
</script>

</head>

<body>
    <p>"Curve" a texture by offsetting Y pixels based on Q-curve</p>
    <canvas id="canvas" width=300 height=300></canvas>
    <p>The temporary texture canvas</p>
    <canvas id="canvas1" width=300 height=300></canvas>
</body>
</html>