是否可以在HTML5画布中的路径上绘制图像?

时间:2016-10-04 19:02:11

标签: javascript html5 canvas

例如,假设我有以下路径。

<canvas id="main" width="500" height="250"></canvas>

var canvas = document.getElementById("main");
var ctx = canvas.getContext("2d");
ctx.beginPath();
ctx.moveTo(20,20);
ctx.lineTo(100,20);
ctx.arcTo(150,20,150,70,50); 
ctx.lineTo(150,120);
ctx.lineWidth = 3;
ctx.stroke();

是否可以在线的弧线上绘制图像?如果是这样,怎么样?

2 个答案:

答案 0 :(得分:2)

这是对类似问题的回答:

你可以在绘制循环中实现一个“线条绘制算法”,它不会精确地绘制一条线,而是在该点所在的位置绘制一个项目。除此之外,在此替换线算法来代替绘制弧。

function line(x0, y0, x1, y1){
     var dx = Math.abs(x1-x0);
     var dy = Math.abs(y1-y0);
     var sx = (x0 < x1) ? 1 : -1;
     var sy = (y0 < y1) ? 1 : -1;
     var err = dx-dy;

     while(true){ // put draw loop here.
         drawImage(image,x0,y0);//setPixel(x0,y0);  // Do what you need to for this

         if ((x0==x1) && (y0==y1)) break;
             var e2 = 2*err;
             if (e2 >-dy){ err -= dy; x0  += sx; }
             if (e2 < dx){ err += dx; y0  += sy; }
    }
}

代码取自:Javascript中的Bresenham算法

我建议使用像p5.js这样的库来做这样的事情。 http://p5js.org

答案 1 :(得分:1)

切割图像以在曲线上绘制。

是的,这是可能的,但理想情况下,这将是WebGL的工作。下一个最佳解决方案是扫描线渲染,但这可能会导致很多CPU负载,以便管理糟糕的Javascript。

下一个 best 我的意思是&#34;好的。&#34;选项是一个小图像切片。

您只需在圆弧周围以薄片绘制图像。 2D渲染器并不完美,并尝试尽可能地绘制半像素。结果是沿着每个切片边缘的一些噪声,您可以看到。为了克服这个问题,我将每个切片拉得更宽,以掩盖任何孔。

如果你需要高质量,在屏幕外画布上将其全部渲染为两倍,然后缩小到屏幕画布(不要忘记平滑)将使大多数人认为它是这样绘制的。

由于弧的内边缘和外边缘具有不同的圆周,因此一些图像必须被压扁或拉伸。在演示中,我将图像的内边缘保持在正确的宽度并拉伸外边缘。它很容易改变,但要确保你使用外边缘来锻炼要绘制的切片数量。

警告给定的半径是内边缘。它被审查以阻止for循环变得太长并阻止页面。您可能希望限制半径,以使内圆周与图像宽度相同。 radius = radius < img.width / (Math.PI * 2) ? img.width / (Math.PI * 2) : radius;

很容易适应线条和曲线。您所需要的只是切线或曲线法线(应该是单位矢量,即长度为1)使用此向量来设置变换ctx.setTransform(nx,ny,tx,ty,px,py)。前两个值从图像底部指向顶部,接下来的两个数字沿着从左到右的切线。最后两个是绘制切片的曲线上的点。

&#13;
&#13;
// creates a blank image with 2d context
var createImage=function(w,h){var i=document.createElement("canvas");i.width=w;i.height=h;i.ctx=i.getContext("2d");return i;}

// create a canvas and add to dom
var can = createImage(512,512);
document.body.appendChild(can);
var ctx = can.ctx;

// create a image (canvas) to draw on the arc.
const textToDisplay = "<<Image on arc>>"

ctx.font = "64px arial";
var w = ctx.measureText(textToDisplay).width + 8;
var text = createImage(w + 64,84);
text.ctx.fillStyle = "#F90";
text.ctx.strokeStyle = "black";
text.ctx.lineWidth = 16;
text.ctx.fillRect(0,0,text.width,text.height);
text.ctx.strokeRect(0,0,text.width,text.height);
text.ctx.font = "64px arial";
text.ctx.fillStyle = "#0F0";
text.ctx.strokeStyle = "Black";
text.ctx.lineWidth = 4;
text.ctx.strokeText(textToDisplay,38,58);
text.ctx.fillText(textToDisplay,38,58);



// draws image on arc
// img image to render
// x,y  center of arc
// radius the inner edge (bottom of image) radius
// fromAng The angle to start drawing the image in radians
// toAng (optional if not given image width will be used to get toAng)
// returns undefined
function drawArcImage(img,x,y,radius,fromAng,toAng){
    
    // WARNING if you let the radius get to small the ratio between the inner and out circumference
    // gets very large. This will result in the image being stretched over a quintabazzilon pixels.
    // so must vet the radius or you will block the page and upset the browser gods.
    
    radius = Math.abs(radius); // only positive
    radius = radius < img.height / 8 ? img.height / 8 : radius;
    var outRad = radius + img.height;
    var cir = Math.PI * 2 * radius; // get inner circumference
    if(toAng === undefined){
        var toAng = (img.width / cir) * Math.PI * 2 ; // get the angle the image will cover
    }
    var cirOut = toAng * outRad;      // get the out edge distance in pixels
    var imgStep = img.width / cirOut; // the image step per slice
    var imgX = 0;                     // track the image line to draw
    var angStep = toAng / cirOut;     // the angle steps
    // For each pixel on the out edge draw a slice
    for(var i = 0; i < toAng; i += angStep){
        var dx = Math.cos(fromAng + i);
        var dy = Math.sin(fromAng + i);
        // set up the transform to draw a slice from the inner to outer edges
        ctx.setTransform(dy,-dx,-dx,-dy,dx * radius + x,dy * radius + y); 
        // get and draw the slice. I stretch it a little (2pix) to cover imperfect rendering
        ctx.drawImage(img,imgX,0,imgStep,img.height,-1,-img.height,2,img.height);
        // move to next slice
        imgX += imgStep;
    }
    ctx.setTransform(1,0,0,1,0,0);  // reset the transform
}


// animate the image to prove it is real.. LOL
var animTick = 0;
var animRate = 0.01;
var pos = 0;
// update function call via RAF
function update(){
    animTick += animRate;   // update tick
    // random anim sin waves.
    var rad = Math.sin(animTick) * (256-text.height - 20) + 20;
    pos += Math.sin(animTick*10) * 0.02;
    pos += Math.sin(animTick/ 3) * 0.02;
    pos += Math.sin(animTick/ 7) * 0.05;
    // clear
    ctx.clearRect(0,0,can.width,can.height)
    // draw
    drawArcImage(text,256,256,rad,pos)
    // do again and again and again
    requestAnimationFrame(update);
}
update();
&#13;
&#13;
&#13;