HTML5画布的多重效果 - 锯齿边缘

3
我们公司的网站上有一个“随机碎片生成器”,使用Flash构建,可以在网站页头下方随机生成多个彩色重叠的碎片图形。

http://www.clarendonmarketing.com

我正在尝试使用HTML5复制这种效果,虽然我可以轻松生成随机碎片,但重叠混合(在Adobe术语中为乘法)却是一个挑战。
我的解决方案基本上是在每个碎片绘制之前创建所有画布像素数据的数组,然后在每个碎片绘制之后创建另一个数组与画布像素数据。然后它比较两个数组,并在第一个数组中找到一个非透明像素,其对应的像素在第二个数组中匹配当前选择的填充颜色时,重新绘制它,并通过“乘法”函数(topValue * bottomValue / 255)确定新的颜色值。
一般来说,这很好用并实现了期望的效果,除了重叠碎片的边缘产生了锯齿状的效果。
我认为这与浏览器的抗锯齿有关。我已经尝试复制计算像素的原始像素的alpha通道值,但似乎没有帮助。
JavaScript:
// Random Shard Generator v2 (HTML5)

var theCanvas;
var ctx;

var maxShards = 6;
var minShards = 3;

var fillArray = new Array(
    [180,181,171,255], 
    [162,202,28,255], 
    [192,15,44,255], 
    [222,23,112,255], 
    [63,185,127,255], 
    [152,103,158,255], 
    [251,216,45,255], 
    [249,147,0,255], 
    [0,151,204,255]
);

var selectedFill;

window.onload = function() {

    theCanvas = document.getElementById('shards');
    ctx = theCanvas.getContext('2d');
    //ctx.translate(-0.5, -0.5)

    var totalShards = getRandom(maxShards, minShards);

    for(i=0; i<=totalShards; i++) {
        //get snapshot of current canvas
        imgData = ctx.getImageData(0,0,theCanvas.width,theCanvas.height);
        currentPix = imgData.data
        //draw a shard
        drawRandomShard();
        //get snapshot of new canvas
        imgData = ctx.getImageData(0,0,theCanvas.width,theCanvas.height);
        pix = imgData.data;
        //console.log(selectedFill[0]+','+selectedFill[1]+','+selectedFill[2]);
        //alert('break')

        //CALCULATE THE MULTIPLIED RGB VALUES FOR OVERLAPPING PIXELS

        for (var j = 0, n = currentPix.length; j < n; j += 4) {
            if (    
                //the current pixel is not blank (alpha 0)
                (currentPix[j+3]>0)
                    && //and the new pixel matches the currently selected fill colour
                (pix[j]==selectedFill[0] && pix[j+1]==selectedFill[1] && pix[j+2]==selectedFill[2])
            ) { //multiply the current pixel by the selected fill colour
                //console.log('old: '+currentPix[j]+','+currentPix[j+1]+','+currentPix[j+2]+','+currentPix[j+3]+'\n'+'new: '+pix[j]+','+pix[j+1]+','+pix[j+2]+','+pix[j+3]);
                pix[j] = multiply(selectedFill[0], currentPix[j]); // red
                pix[j+1] = multiply(selectedFill[1], currentPix[j+1]); // green
                pix[j+2] = multiply(selectedFill[2], currentPix[j+2]); // blue
            }
        }
        //update the canvas
        ctx.putImageData(imgData, 0, 0);        
    }


};

function drawRandomShard() {

    var maxShardWidth = 200;
    var minShardWidth = 30;
    var maxShardHeight = 16;
    var minShardHeight = 10;
    var minIndent = 4;
    var maxRight = theCanvas.width-maxShardWidth;
    //generate a random start point
    var randomLeftAnchor = getRandom(maxRight, 0);
    //generate a random right anchor point
    var randomRightAnchor = getRandom((randomLeftAnchor+maxShardWidth),(randomLeftAnchor+minShardWidth));
    //generate a random number between the min and max limits for the lower point
    var randomLowerAnchorX = getRandom((randomRightAnchor - minIndent),(randomLeftAnchor + minIndent));
    //generate a random height for the shard
    var randomLowerAnchorY = getRandom(maxShardHeight, minShardHeight);
    //select a fill colour from an array
    var fillSelector = getRandom(fillArray.length-1,0);
    //console.log(fillSelector);
    selectedFill = fillArray[fillSelector];

    drawShard(randomLeftAnchor, randomLowerAnchorX, randomLowerAnchorY, randomRightAnchor, selectedFill);

}

function drawShard(leftAnchor, lowerAnchorX, lowerAnchorY, rightAnchor, selectedFill) {

    ctx.beginPath();
    ctx.moveTo(leftAnchor,0);
    ctx.lineTo(lowerAnchorX,lowerAnchorY);
    ctx.lineTo(rightAnchor,0);
    ctx.closePath();
    fillColour = 'rgb('+selectedFill[0]+','+selectedFill[1]+','+selectedFill[2]+')';
    ctx.fillStyle=fillColour;
    ctx.fill();

};

function getRandom(high, low) {
    return Math.floor(Math.random() * (high-low)+1) + low;
}

function multiply(topValue, bottomValue){
    return topValue * bottomValue / 255;
};

工作演示: http://www.clarendonmarketing.com/html5shards.html


出于好奇...你为什么要把它改成基于Javascript的解决方案?苹果设备兼容性的问题吗? - Ricardo Souza
是的,目前非Flash备选方案是显示静态图像。我希望它能在iOS上运行,并且通常不需要第三方插件。 - jacob
我认为你可以实现一个抗锯齿滤波器:http://pt.scribd.com/doc/6912465/Efficient-antialiasing-algorithm-for-computer-generated-images - Ricardo Souza
1个回答

5

你真的需要乘法运算吗?为什么不使用低透明度混合?

演示 http://jsfiddle.net/wk3eE/

ctx.globalAlpha = 0.6;
for(var i=totalShards;i--;) drawRandomShard();

编辑: 如果你真的需要乘法运算,那么最好请专业人员完成,因为带透明度的乘法模式有点棘手:

演示2: http://jsfiddle.net/wk3eE/2/

<script type="text/javascript" src="context_blender.js"></script>
<script type="text/javascript">
  var ctx = document.querySelector('canvas').getContext('2d');

  // Create an off-screen canvas to draw shards to first
  var off = ctx.canvas.cloneNode(true).getContext('2d');

  var w = ctx.canvas.width, h = ctx.canvas.height;
  for(var i=totalShards;i--;){
    off.clearRect(0,0,w,h);        // clear the offscreen context first
    drawRandomShard(off);          // modify to draw to the offscreen context
    off.blendOnto(ctx,'multiply'); // multiply onto the main context
  }
</script>

谢谢,但是我们的品牌指南规定了颜色;在白色背景上给它们60%的不透明度基本上会使它们都成为粉彩色调。 - jacob
谢谢 - 我确实看了你的库,但不确定如何实现第二个画布。Demo 2看起来不错,只是似乎影响了整个重叠碎片的颜色,而不仅仅是重叠部分? - jacob
@jacob 我刚刚更新了演示,修复了一个愚蠢的错误(我忘记在绘制额外碎片之前擦除屏幕外上下文)。现在看起来更好了吗? - Phrogz

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