在画布

时间:2015-11-17 22:28:18

标签: javascript canvas

我有一个简单的JS动画,图像的大小会改变到3个小数位,并且会对图像的某些部分造成恼人的闪烁效果。我发现某些图像可以创造出其他人没有的效果。

我想知道动画师如何解决此问题以消除视觉效果,尤其是在Javascript中。

您需要在JSFiddle上看到效果。我在这里创建动画以查看建筑物的视觉效果: 的 http://jsfiddle.net/qerwsn07/

function draw() {
    debug.innerHTML = 'Scale: '+scale;
    if(!dir){ 
        scale += 0.001; 
        if(scale > 1.09){ 
            dir = true;
        }
    } else if(dir) {
        scale -= 0.001;
        if(scale < 1){
            dir = false;   
        }
    }
    ctx.clearRect(0, 0, element.width, element.height);
    ctx.save()
    ctx.translate(element.width/2 - (camera.x / 500),element.height/2 - (camera.y / 500));      
    var x       =  - ((img.width * scale) / 2);
    var y       =  - ((img.height* scale) / 2);
    var width   = img.width * scale;
    var height  = img.height*scale;

    ctx.drawImage(img,x,y,img.width*scale,img.height*scale);
    ctx.restore();
    requestAnimationFrame(draw);
}
draw();

是否有办法删除或减少效果?

1 个答案:

答案 0 :(得分:1)

虽然我现在在机器上看不到这个,但我知道你的问题是什么。有两种方法可以减少它。

首先问题是由像素采样引起的。当缩放图像时,屏幕像素(设备显示器上的物理像素)与图像中的像素之间不再存在一对一的匹配。缩小(使图像变小)时,图像中的像素数可能比显示的多。当您放大(使图像变大)像素分布在多个像素上时。

最基本的解决方案称为最近像素采样。硬件计算每个设备显示像素的缩放图像的位置,然后选择图像中该点的最近像素。

最常见的替代方案是双线性插值。在此方法中,硬件在放大时使用最近的4个像素(使图像变大)并通过左右之间的简单线性插值计算颜色,然后在顶部和底部计算颜色并显示平均颜色。缩小时,它会平均显示像素下的像素。这种方法可以大大减少闪烁,但并不能完全消除闪烁。

画布允许您在最近的像素和硬件的平滑模式之间进行选择。用于平滑的内容取决于硬件配置。

要使用平滑方法,请将2D上下文的imageSmoothingEnabled属性设置为true

ctx.imageSmoothingEnabled = true;

将其关闭

ctx.imageSmoothingEnabled = false;

但默认情况下,画布的“真实”。所以我认为这不会马上解决你的问题。

如果您使用的是PC,那么您的图形驱动程序可能附带了一个实用程序来调整渲染性能。这些实用程序通常可以在Windows控制面板中找到。根据硬件和您使用的驱动程序,选项会有所不同。通常他们有一个性能面板,您可以在其中设置硬件的性能或质量。性能设置将覆盖画布平滑选项,并且仅使用最近的像素(或设备最快的任何设置)。如果你有这个设置为最佳质量,看看是否会改变闪烁。

此外,其中一些实用程序可让您为每个应用程序设置质量设置。因此,请检查图形设备是否具有该选项,以及浏览器是否在列表中。如果是,那么将其改为最佳质量。

某些驱动程序允许您设置单独的图形选项。因此,请查看并更改适合您的像素过滤。您可能必须通过将像素过滤设置为应用程序设置来强制浏览器使用它。 (如果您使用的是Mac,请参阅帮助)

如果一切设置正确但仍然存在的闪烁困扰你,那么你将不得不使用其他方法来减少问题。

图像模糊;

缩小时使用。 Mip映射的修改。您需要在内存中创建图像的第二个副本并稍微模糊该图像。缩小渲染时模糊图像。您可能还希望在模糊图像和原始图像之间进行小的过渡,以便在缩放从放大切换到缩小时停止注意。这可以通过使用ctx.globalAlpha将一个图像淡入另一个图像来完成。

Mip mapping;

此方法涉及创建图像的许多副本。每一个在它之前的分辨率的一半。你必须使用一个好的羽绒采样器(photoshop,Gimp,Paint.net)都有下采样选项。或者你可以自己写一个并用代码完成。

当显示缩小的图像时,您绘制最接近缩放尺寸的两个图像,两者之间的淡入淡出取决于缩放与下采样图像面的接近程度。

无论你做什么,缩放时仍然会有一些文物。每种方法都有其优点和缺点。为了获得最佳质量,您可以从最大变焦的像素分辨率的两倍开始。然后在渲染时使用您喜欢的采样方法在代码中对图像进行采样(Javascript不符合2D上下文的工作,您需要使用GPU和webGL)

EDIT添加了浏览器平滑测试

我添加了一个小测试,以查看平滑机是否被机器上的图形设备驱动程序覆盖。左测试图案已平滑,另一个已平滑。如果两者看起来相同,则浏览器无法控制平滑。

// this code is not intended as an answer but as a way of checking 
// device smoothing is avalible or not.
var canvas = document.getElementById("canV");
var ctx = canvas.getContext("2d");

// not commented as not a code answer.
var currentSurface = ctx;
var createImage = function (w, h) {
    var image = document.createElement("canvas");  
    image.width = w;
    image.height = h !== undefined?h:w; 
    currentSurface = image.ctx = image.getContext("2d"); 
    return image;
}  


var img = createImage(256,256);
var imageDat = img.ctx.getImageData(0,0,255,255);
var dat = imageDat.data;
var ind = 0;
var lines = 2;
var grow = 2;
var black = 0
var white = 255
for(var y = 0; y < 256; y++){
    for(var x = 0; x < 256; x++){
        if(x%lines < lines-1){
            dat[ind++] = black;
            dat[ind++] = black;
            dat[ind++] = black;
        }else{
            dat[ind++] = white;
            dat[ind++] = white;
            dat[ind++] = white;
        }
        dat[ind++] = 255;
    }
    if(y%lines < lines-1){
        black = 0;
        white = 255;
    }else{
        black = 255;
        white = 0;
    }
    grow += 0.04;
    lines = Math.floor(grow);
}
img.ctx.putImageData(imageDat,0,0);
var count = 1000;
var tick = 0;
function update(){
    tick += 0.01;
    zoom = Math.sin(tick)*64;
    ctx.imageSmoothingEnabled = false;
    ctx.drawImage(img,64-zoom,64-zoom,zoom*2+128,zoom*2+128,0,0,200,200)
    ctx.imageSmoothingEnabled = true;
    ctx.drawImage(img,64-zoom,64-zoom,zoom*2+128,zoom*2+128,205,0,200,200)
    
    requestAnimationFrame(update)
}
update();
#canV {
  width:405px;
  height:200px;
}
.smallText {
  font-size:small;  
}
b {
  color:blue;
}
<div class="smallText">Image on the left is <b>ctx.imageSmoothingEnabled = false;</b> and image on the right is <b>ctx.imageSmoothingEnabled = true;</b>. If they both look the same then the divice driver is overwriting the browser smoothing.</div>
<canvas id="canV" width=405 height=200></canvas>