web-dev-qa-db-ja.com

キャンバス画像のマスキング/オーバーラップ

私のプロジェクトでは、キャンバスを使用して他の同じサイズとパターン画像に1つの異なるカラー画像を実装する必要があり、画像は円形または長方形ではありません。これらはすべて波形であり、すべてのonclick関数で複数のグラフィックを表示するために単一のメイン背景画像に適用されます。

重なった画像は、選択した別の色に変更する必要があります。私の質問キャンバスを使用する方法はありますか?キャンバスによって描画される画像の色を変更できるか、常に異なる画像を使用してCSS/jQueryで適用する必要がありますか?.

キャンバスの画像のマスキングとオーバーラップについて読みました。しかし、正方形または円形ではないため、私の画像では理解できません。最初に、単一の画像に複数の波形を描画する方法です。私が検索したことはわかりませんが、完璧なソリューションを検索できません。

私のニーズは、キャンバス上に1つの波の画像を描画し、クリック機能から色を変更し、さらに背景画像で別のdivを設定し、さらに2つ以上のキャンバスが重なることです。これは可能ですか?

(つまり、この機能は車で複数のグラフィックを作成または設定するためのものであり、各グラフィック画像はキャンバスに設定する必要があり、別のグラフィックはdivと最初のキャンバスにオーバーラップする必要があります)

28
Anup

コンテキスト合成を使用して、画像の一部を置き換えることができます。

たとえば、この青いロゴが既に画像としてある場合:

enter image description here

ロゴの上部を紫色にしたい場合:

enter image description here

合成を使用して、画像の上部の色を変更できます。

まず、お気に入りの画像エディターを使用して、色を変更したくない不要な部分を切り取ります。

残ったものはオーバーレイと呼ばれます。

画像のこのオーバーレイ部分は、プログラムで色を変更します。

enter image description here

このオーバーレイは、任意の色にプログラムで色を変更できます。

enter image description hereenter image description here

オーバーレイのプログラム上の色の変更方法:

  1. 空のキャンバスにオーバーレイを描画します。
  2. 合成モードを「ソースイン」に設定します。
  3. 効果:既存のピクセルのみが置き換えられます-透明ピクセルは透明のままです
  4. キャンバスを覆う任意の色の長方形を描画します
  5. (既存のオーバーレイのみが新しい色に置き換えられることに注意してください)

変更されたオーバーレイ色でロゴを完成させる方法

  1. 合成モードを「destination-atop」に設定します
  2. 効果:透明ピクセルのみが置き換えられます-既存のピクセルは変更されません
  3. 元のロゴを描きます
  4. (既存の色付きオーバーレイは置き換えられないことに注意してください)

この「デスティネーショントップ」の合成効果は、「ドローイングアンダー」とも呼ばれます。

このオーバーレイは、テクスチャに置き換えることもできます!

enter image description here

ここにコードとフィドルがあります: http://jsfiddle.net/m1erickson/bfUPr/

<!doctype html>
<html>
<head>
<link rel="stylesheet" type="text/css" media="all" href="css/reset.css" /> <!-- reset css -->
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>

<style>
    body{ background-color: ivory; padding:20px; }
    #canvas{border:1px solid red;}
</style>

<script>
$(function(){

    var canvas=document.getElementById("canvas");
    var ctx=canvas.getContext("2d");

    var truck,logo,overlay;
    var newColor="red";

    var imageURLs=[];
    var imagesOK=0;
    var imgs=[];
    imageURLs.Push("https://dl.dropboxusercontent.com/u/139992952/stackoverflow/boxTruck.png");
    imageURLs.Push("https://dl.dropboxusercontent.com/u/139992952/stackoverflow/TVlogoSmall.png");
    imageURLs.Push("https://dl.dropboxusercontent.com/u/139992952/stackoverflow/TVlogoSmallOverlay.png");
    loadAllImages();

    function loadAllImages(){
        for (var i = 0; i < imageURLs.length; i++) {
          var img = new Image();
          imgs.Push(img);
          img.onload = function(){ imagesOK++; imagesAllLoaded(); };
          img.src = imageURLs[i];
        }      
    }

    var imagesAllLoaded = function() {
      if (imagesOK==imageURLs.length ) {
         // all images are fully loaded an ready to use
         truck=imgs[0];
         logo=imgs[1];
         overlay=imgs[2];
         start();
      }
    };


    function start(){

        // save the context state
        ctx.save();

        // draw the overlay
        ctx.drawImage(overlay,150,35);

        // change composite mode to source-in
        // any new drawing will only overwrite existing pixels
        ctx.globalCompositeOperation="source-in";

        // draw a purple rectangle the size of the canvas
        // Only the overlay will become purple
        ctx.fillStyle=newColor;
        ctx.fillRect(0,0,canvas.width,canvas.height);

        // change the composite mode to destination-atop
        // any new drawing will not overwrite any existing pixels
        ctx.globalCompositeOperation="destination-atop";

        // draw the full logo
        // This will NOT overwrite any existing purple overlay pixels
        ctx.drawImage(logo,150,35);

        // draw the truck
        // This will NOT replace any existing pixels
        // The purple overlay will not be overwritten
        // The blue logo will not be overwritten
        ctx.drawImage(truck,0,0);

        // restore the context to it's original state
        ctx.restore();

    }


}); // end $(function(){});
</script>

</head>

<body>
    <canvas id="canvas" width=500 height=253></canvas>
</body>
</html>
45
markE

現状の問題は、少し不明確なIMOです。クリッピングが必要なシナリオに適用できるより一般的な答えを与えるには、(少なくとも)2つのアプローチを使用できます。

方法1-複合モードを使用してクリップする

コンポジットモードは最も簡単な方法ですが、クリッピングマスクを透明な背景(通常はPNG)の画像として事前に定義する必要があるため、柔軟性が最も低くなります。

画像の塗りつぶし部分を使用して、次に描かれたものをクリップするか、透明な領域を使用して塗りつぶすことができます。

次に、ソリッドパーツを使用して、次の描画されたシェイプ/イメージをクリップするアプローチを示します。

/// draw the shape we want to use for clipping
ctx1.drawImage(imgClip, 0, 0);

/// change composite mode to use that shape
ctx1.globalCompositeOperation = 'source-in';

/// draw the image to be clipped
ctx1.drawImage(img, 0, 0);

ここでglobalCompositeOperationsource-inに変更されます。これは、ソース画像(目的地の隣に描画する画像)が内部に描画されることを意味しますinside既存のソリッドデータ。透明な領域には何も描画されません。

クリッピングマスクが次のようになっている場合(ネットからのランダムfair-use):

Clip mask

そして、このようなイメージ:

Main image

結果は次のようになります。

Composited image

方法2-パスを使用してクリップする

クリッピングのパスを定義することもできます。必要に応じてパスを調整したりアニメートしたりできるため、これは非常に柔軟です。

注:ブラウザーでは、現在、Pathを使用したクリッピングは少し "壊れやすい"ため、ブラウザーとしてクリップパスを設定および使用する前後にsave()およびrestore()を使用することを検討してください。現時点ではクリップをリセットできません(restoreはデフォルトのクリップを復元します=キャンバス全体)。

単純なジグザグパスを定義してみましょう(これがあなたの場合の波になります)。

/// use save when using clip Path
ctx2.save();

ctx2.beginPath();
ctx2.moveTo(0, 20);
ctx2.lineTo(50,0);
/// ... more here - see demo
ctx2.lineTo(400, 20);
ctx2.lineTo(400, 100);
ctx2.lineTo(0, 100);
ctx2.closePath();

/// define this Path as clipping mask
ctx2.clip();

/// draw the image
ctx2.drawImage(img, 0, 0);

/// reset clip to default
ctx2.restore();

clipを使用してクリッピングマスクを設定したので、次にキャンバスに描画されたものはすべて、その形状内に収まるようにクリップされます(形状が開始位置で終了できることを確認してください)。

Path clipped image

これらのメソッドのオンラインデモを参照してください

52
user1693593