1.原始问题:

我正在做一个画布动画“缩放”效果,该效果会产生一些令人讨厌的视觉伪像。这个简化的示例显示了255帧后的输出。在每一帧中,都会发生两件事:(1)drawArc()在画布的中心制作一个彩色的(灰色)光盘; (2)imageDraw()以稍微放大的比例重画画布。我希望看到一个平滑的径向渐变从中心向外扩展。相反,这是我看到的:



令我烦恼的是(a)沿垂直和水平轴(在较小程度上,对角线)的明亮“星爆”,以及(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;
    }
}


这是运行中的快照:
 

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

最佳答案

缺陷的确是由于结垢。一旦在画布上绘制路径,路径信息就会丢失,因此您正在调整图像的光栅化版本的大小。这就是为什么您的圆最终看起来像多边形的原因。

您可以改为使用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是一个小提琴的例子。您可以随心所欲地使用内圆半径,并且可以通过定时功能控制颜色停止以获得所需的结果。

关于javascript - 如何避免drawImage()爆炸伪像?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/35138002/

10-09 17:37