如何避免drawImage()starburst工件?

时间:2016-02-01 18:48:05

标签: javascript image-processing canvas

1。原始问题:

我正在做画布动画"缩放"产生一些恼人的视觉伪影的效果。此简化示例显示255帧后的输出。在每个帧中,发生两件事:(1)drawArc()在画布的中心制作一个彩色(灰色)磁盘; (2)imageDraw()以略微放大的比例重绘画布。我希望看到一个平滑的径向渐变从中心向外扩展。相反,这就是我所看到的:

zzz

让我烦恼的是(a)明亮的" starbursts"沿垂直和水平轴(在较小程度上,在对角线上)和(b)模糊的菱形图案。

我认为这是drawImage()缩放算法的某种神器。我已经尝试调整所有参数(deltastrokeWidth,圆弧半径),但总有一些版本的星爆效果。关于如何避免 - 或至少减少 - 这种影响的任何建议?

这是我的代码:

var cv = document.getElementById("testCanvas");
var ctx = cv.getContext('2d');
var cvHeight = cv.height;
var cvWidth = cv.width;
for (var j=0;j<255;j++) {
    var delta = 4;

    // redraw the canvas, enlarged by delta pixels
    ctx.drawImage(cv, 0, 0, cvWidth, cvHeight, 
        -delta/2, -delta/2, cvWidth+delta, cvHeight+delta);

    // draw a new circle at the center, in a different color
    ctx.beginPath();
    ctx.strokeStyle = "rgb("+ j + "," + j + "," + j +")";
    ctx.strokeWidth = 10;
    ctx.arc(cvWidth/2, cvHeight/2, 10, 0, Math.PI*2.0);    
    ctx.stroke();   
}

此处the JSFiddle

2。进一步阐述

正如@Igor Raush恰当地观察到的那样,我给出的示例图像可以使用createRadialGradient()更好地呈现(无爆炸)。但我的示例代码过于简单和误导:我的目标不是生成径向颜色渐变,而是通过连续调用imageDraw()来扩大画布来创建缩放动画效果。在上面的例子中,静态白色圆盘放置在画布的中心。在我的实际应用中,每个帧中都会在画布的中心放置一个新图像。问题在于,无论最初放置在画布中心的哪种图像,那些恼人的星爆图案都会在某种程度上出现。

这是一个使用随机数据和真实动画的更好的例子。 (为了简单起见,我在这里使用随机数据。我的应用程序中的实际数据不是随机的,但效果是相同的。)在每个帧中,绘制一个随机着色点的环(ctx.arc())画布的中心; imageDraw()然后稍微展开画布。

这里是JS Fiddle,以下是代码:

var cv = document.getElementById("testCanvas");
var ctx = cv.getContext('2d');
var cvHeight = cv.height;
var cvWidth = cv.width;
var delta = 10; // higher = faster "expansion" of the canvas
var data = new Array(100);

setInterval( function() { 
    FetchNewData(data); // fill the array with new data
    DrawTheRing(cv,data); // draw the data around a ring, as shades of gray, centered on the canvas
    ctx.drawImage(cv, 0, 0, cvWidth, cvHeight, -delta/2, -delta/2, cvWidth+delta, cvHeight+delta); // expand the canvas
} , 10);


// Load up the array with random data
function FetchNewData(data) {
    var nSamples = data.length;
    for (var k=0; k< nSamples; k++) {
        data[k] = Math.floor((Math.random() * 255));
    }
}

// draw a ring, centered on the canvas, containing the grayscale data
function DrawTheRing(cv,data) {
    var radius = 10;
    var thickness = 10;
    var ctx = cv.getContext('2d');
    var angle = 0;
    var dAngle = Math.PI*2.0/data.length;
    for ( var k = 0; k < data.length; k++) {
        var value = data[k];
        ctx.beginPath();
        ctx.lineWidth = thickness;
        ctx.strokeStyle = "rgb("+ value + "," + value + "," + value +")";    // a shade of gray
        ctx.arc(cvWidth/2, cvHeight/2, radius, angle, angle+dAngle);    
        ctx.stroke();    
        angle += dAngle;
    }
}

以下是其中的快照: zoooming with random data

关于如何避免垂直和水平轴以及对角线上的虚假尖锐线的任何想法?有没有更好的方法来实现这种缩放效果?

1 个答案:

答案 0 :(得分:2)

缺陷确实是由于缩放。只要在画布上绘制路径,路径信息就会丢失,因此您要调整图像的栅格化版本。这就是为什么你的圈子最终看起来像多边形。

您可以改为使用ctx.createRadialGradient为颜色停止设置动画,并在每一帧重绘画布。

var s = 0.99;
var x = cvWidth / 2,
    y = cvHeight / 2,
    r = cvWidth / 2;

// generate gradient
var gradient = ctx.createRadialGradient(x, y, r, x, y, 0);
gradient.addColorStop(0, 'black');
gradient.addColorStop(s, 'black');
gradient.addColorStop(1, 'white');

Here就是一个例子。您可以使用内圆半径和控制颜色停止的计时功能来实现您想要的结果。