如何在带有背景图的画布上绘制一条直线

3
我想在canvas中画一条线(我可以做到),但我想使用背景图案在线上重复出现(除非有其他方法在canvas中将重复的背景图像放在线上?)。如何绘制带有背景图像的线?我了解剪切的概念,但它似乎只适用于形状...而不是描边。有什么想法吗?这是我尝试的jsfiddle链接:http://jsfiddle.net/Z9cd7/
    function (callback) {
        window.setTimeout(callback, 1000 / 60);
    };
})();

var radius = 50;
var x = 100;
var dx = 10;
var y = 100;
var dy = 10;
var delay = 10;
var img = new Image();
img.onload = function () {
    var canvas1 = document.getElementById("image");
    var ctxImg = canvas1.getContext("2d");
    ctxImg.drawImage(img, 0, 0, img.width, img.height, 0, 0, canvas.width, canvas.height);

    /*
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    ctx.save();
    ctx.beginPath();
    ctx.arc(100, 100, radius, 0, 2 * Math.PI, false);
    ctx.clip();
    ctx.drawImage(img, 0, 0, img.width, img.height, 0, 0, canvas.width, canvas.height);
    ctx.restore();
    */
    ctx.moveTo(0,0)
    ctx.lineTo(100,100)
    ctx.lineWidth = 10;
    ctx.stroke()

    ctx.clip();
    ctx.drawImage(img, 0, 0, img.width, img.height, 0, 0, canvas.width, canvas.height);
    ctx.restore();

    //animate();
}
img.src = "http://lh3.ggpht.com/_Z-i7eF_ACGI/TRxpFywLCxI/AAAAAAAAAD8/ACsxiuO_C1g/house%20vector.png";
4个回答

6

虽然我有点晚了,但是既不需要使用裁剪也不需要计算向量,您可以考虑使用内置的支持(更快)来设置一个带有图像模式样式的描边样式:

var pattern = ctx.createPattern(image, 'repeat');  /// create pattern
ctx.strokeStyle = pattern;                         /// set as stroke style

ctx.moveTo(10, 10);
ctx.lineTo(200, 200);
ctx.lineWidth = 10;
ctx.stroke();                           /// strokes with the image as background

这里是示例 1

用线条做图案

如果您想改变图案的大小,只需更改画布 ("image") 的大小即可,例如:

<canvas id="image" width=100 height=100></canvas>

并且在您的onload处理程序中进行修改,以便图像按比例缩放以适应画布:

ctxImg.drawImage(img, 0, 0, canvas1.width, canvas1.height);

这里有演示2

较小的图案

如果您需要调整绘制时图案的位置,可以使用translate(),使用先前翻译的增量值,然后从要使用图案绘制的线的位置中减去 - 这将保持线条的相同位置,同时移动图案本身:

ctx.translate(dx, dy);
ctx.moveTo(x1 - dx, y1 - dy);
ctx.lineTo(x2 - dx, y2 - dy);
...

是的,我总是忘记模式的有用性——我的记忆真糟糕 :) 在你的演示#2中,“delay”是否最终用于控制重复模式之间的间距? - markE
1
@markE 噢,delay ++ 只是 OP 的原始小提琴中剩下的东西。我只是在必要的部分周围切了一点,使它更清晰(就像答案中一样) - 我没有清除其他部分(以使代码在某种程度上可识别为 OP)。 - user1693593
这对我来说非常有效,但是你漏掉了一行代码:在Demo 1中,在ctx.moveTo(10, 10);之前加上cxt.beginPath();。如果没有这行代码,如果你多次调用它,它将重新绘制所有先前的绘图操作,而不仅仅是最新的线条。 - Shane O Sullivan

3
我看到有两种解决方案:

代码如下,基本上你希望使用w1 = w2(相同的起始/结束厚度)的varLine:

// varLine : draws a line from A(x1,y1) to B(x2,y2)
// that starts with a w1 width and ends with a w2 width.
// relies on fillStyle for its color.
// ctx is a valid canvas's context2d.
function varLine(ctx, x1, y1, x2, y2, w1, w2) {
    var dx = (x2 - x1);
    var dy = (y2 - y1);
    w1 /= 2; // we only use w1/2 and w2/2 for computations.
    w2 /= 2;
    // length of the AB vector
    var length = Math.sqrt(sq(dx) + sq(dy));
    if (!length) return; // exit if zero length
    var shiftx = -dy * w1 / length; // compute AA1 vector's x
    var shifty = dx * w1 / length; // compute AA1 vector's y
    ctx.beginPath();
    ctx.moveTo(x1 + shiftx, y1 + shifty);
    ctx.lineTo(x1 - shiftx, y1 - shifty); // draw A1A2
    shiftx = -dy * w2 / length; // compute BB1 vector's x
    shifty = dx * w2 / length; // compute BB1 vector's y
    ctx.lineTo(x2 - shiftx, y2 - shifty); // draw A2B1
    ctx.lineTo(x2 + shiftx, y2 + shifty); // draw B1B2
    ctx.closePath(); // draw B2A1
    ctx.fill();
}
  • 第二种解决方案非常快:使用globalCompositeOperation模式为您完成剪辑。 例如,绘制线条,使用“source-in”,然后在线条上方绘制图像。
    这非常方便,但问题在于只有在绘制线条之前清除画布时才能起作用。如果可以自由选择绘制顺序,则不是问题,否则,您将不得不在临时画布上绘制线条,然后在主画布上绘制画布。

更新的fiddle在此处: http://jsfiddle.net/gamealchemist/Z9cd7/1/

enter image description here

编辑:带重复图像的fiddle: http://jsfiddle.net/gamealchemist/Z9cd7/2/

enter image description here


谢谢!这似乎有效。有没有办法让图像在x和y轴上重复? - Bill
不客气。你在谈论第二种解决方案,对吧:没错:可以使用基于图像的样式填充矩形而不是使用drawImage(在图像加载时只需创建一次该图像即可(创建样式会进行复制,因此不要在循环/动画中创建样式))。 - GameAlchemist
@Bill:我刚刚为重复操作做了一个小例子。 - GameAlchemist
谢谢...从你的评论中得到了它...只是需要一个正确方向的指引! - Bill
好的。我只是想要百分之百确定这个建议是正确的,并分享这张图片! :-) - GameAlchemist

1

[ 使用图片绘制线条 ]

您可以使用此实用程序函数确定沿着线段的任何百分比点:

    function getLineXYatPercent(startPt,endPt,percent) {
        var dx = endPt.x-startPt.x;
        var dy = endPt.y-startPt.y;
        var X = startPt.x + dx*percent;
        var Y = startPt.y + dy*percent;
        return( {x:X,y:Y} );
    }

之后只需要进行一些简单的数学计算,就可以沿着每条线段重复地定位你的图像:

    var dx=points[i].x-points[i-1].x;
    var dy=points[i].y-points[i-1].y;
    var length=Math.sqrt(dx*dx+dy*dy);
    var pctImage=imgWidth/length;
    for(var pct=pctImage/2;pct<1.00;pct+=pctImage){
         var pos=getLineXYatPercent(points[i-1],points[i],pct);
         ctx.drawImage(img,0,0,img.width,img.height,pos.x,pos.y,imgWidth,imgHeight);
    }   

演示:http://jsfiddle.net/m1erickson/R278u/

enter image description here

代码:
<!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: lightgray; }
    #canvas{border:1px solid red;}
</style>
<script>
$(function(){

    var canvas=document.getElementById("canvas");
    var ctx=canvas.getContext("2d");
    var $canvas=$("#canvas");
    var canvasOffset=$canvas.offset();
    var offsetX=canvasOffset.left;
    var offsetY=canvasOffset.top;
    var scrollX=$canvas.scrollLeft();
    var scrollY=$canvas.scrollTop();
    var isDown=false;

    var points=[];
    points.push({x:0,y:0});
    points.push({x:100,y:125});
    points.push({x:200,y:75});
    points.push({x:300,y:100});

    imgWidth=20;
    imgHeight=20;

    var img=new Image();
    img.onload=function(){
        draw();
    }
    img.src="https://dl.dropboxusercontent.com/u/139992952/multple/house%20vector.png";

    function draw(){
        ctx.clearRect(0,0,canvas.width,canvas.height);
        ctx.save();
        ctx.beginPath();
        ctx.moveTo(points[0].x,points[0].y);
        for(var i=1;i<points.length;i++){
            var p=points[i];
            var dx=points[i].x-points[i-1].x;
            var dy=points[i].y-points[i-1].y;
            var length=Math.sqrt(dx*dx+dy*dy);
            var pctImage=imgWidth/length;
            for(var pct=pctImage/2;pct<1.00;pct+=pctImage){
                var pos=getLineXYatPercent(points[i-1],points[i],pct);
                ctx.drawImage(img,0,0,img.width,img.height,pos.x,pos.y,imgWidth,imgHeight);
            }   
        }
    }

    function getLineXYatPercent(startPt,endPt,percent) {
        var dx = endPt.x-startPt.x;
        var dy = endPt.y-startPt.y;
        var X = startPt.x + dx*percent;
        var Y = startPt.y + dy*percent;
        return( {x:X,y:Y} );
    }

}); // end $(function(){});
</script>
</head>
<body>
    <canvas id="canvas" width=300 height=300></canvas>
</body>
</html>

1
基于插值的尾随线看起来很不错! - user1693593

0

你不是只用第二个drawImage调用在所有东西上绘制吗?

编辑:是的,但这不是你的意思,尝试开始和关闭路径。

 ctx.beginPath();
 ctx.moveTo(0,0)
 ctx.lineTo(100,100)
 ctx.lineTo(100,0);
 ctx.closePath()
 ctx.lineWidth = 10;
 ctx.stroke();
 ctx.clip();

 ctx.drawImage(img, 0, 0, img.width, img.height, 0, 0, canvas.width, canvas.height);

1
这里没有什么大问题,但是你的帖子应该是一个评论而不是一个答案。 - GameAlchemist
为什么?我提供了一个可能的答案,即第二个drawImage调用正在整个画布上绘制。因此,我提供了一个答案而不是发表评论。 - ChrisIPowell
在你编辑之前,这只是一个单行帖子,仅此而已。尝试提供代码和一个fiddle,我试图让你的想法起作用失败了... - GameAlchemist

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接