2

次のように、2 つの要素をブレンドするときにselectどのタイプの を適用するかを選択できる HTML リストを作成したいと思います。globalCompositeOperationcanvas

<select name="blending-modes" id="blending-modes">
    <option value="source-over">source-over</option>
    <option value="source-in">source-in</option>
    <option value="source-out">source-out</option>
    ...
</select>

globalCompositeOperation使用可能なタイプのリストをJavascriptオブジェクトまたは配列としてプログラムで取得する方法はありselectますか? この情報はネイティブ変数に保存されていますか?

here で説明されているように、一部のブレンド モードがユーザーのブラウザでサポートされているかどうかを確認したくありません。globalCompositeOperationブラウザでブレンディング モードを選択するために、サポートされているタイプの完全なリストを取得したいと考えています。

4

2 に答える 2

5

globalCompositeOperationいいえ、ブラウザがサポートするモードを示すネイティブ プロパティはありません。仕様で定義されたすべてのもの
をループしてテストし、それがまだ設定したものであるかどうかを確認する必要があります。

function getGCOModes() {
  var gCO = ["clear", "copy", "source-over", "destination-over", "source-in", "destination-in", "source-out", "destination-out", "source-atop", "destination-atop", "xor", "lighter", "plus-darker", "plus-lighter", "normal", "multiply", "screen", "overlay", "darken", "color-dodge", "color-burn", "hard-light", "soft-light", "difference", "exclusion", "hue", "saturation", "color", "luminosity", "plus-lighter", "plus-darker"];
  var ctx = document.createElement('canvas').getContext('2d');
  return gCO.filter(function(g) {
    ctx.globalCompositeOperation = g;
    return ctx.globalCompositeOperation === g;
  });
}

var supportedGCO = getGCOModes();

log.innerHTML = supportedGCO.join(' ');
<p id="log"></p>

ただし、Safari (少なくとも 9.0.1)"hue"は、"saturation""color"およびモードを受け入れ"luminosity""ますが、実際にはサポートしていないため、警告/バグが 1 つあります...

そこで、さまざまなモードをテストする関数を作成しました。
アイデアは、単色で塗りつぶされた 2 つの 3x3px キャンバスを 3 つ目のキャンバスに描画することです。最初のものは左上隅に、2 つ目は左下隅に描かれ、それぞれが 3 番目のキャンバスの中央のピクセルのピクセルを共有しています。

明らかに、これはプロパティ チェックよりも遅くなりますが、必要なのはページごとに 1 回だけであるため、パフォーマンスは問題にならない可能性があります。

function testGCOModes() {
  // In this object are stored the pixels as they should appear at the 3 positions we'll look : 
  // 0 is an empty pixel
  // 1 is the first pixel drawn
  // 2 is the second pixel drawn
  // 3 is none of the above (blending)
  // We'll look to the central pixel first since it is the most likely to change
  var gCO = {
    // composite modes
    "clear": [0, 1, 0],
    "copy": [2, 0, 2],
    "source-over": [2, 1, 2],
    "destination-over": [1, 1, 2],
    "source-in": [2, 0, 0],
    "destination-in": [1, 0, 0],
    "source-out": [0, 0, 2],
    "destination-out": [0, 1, 0],    
    "source-atop": [2, 1, 0],
    "destination-atop": [1, 0, 2],
    "xor": [0, 1, 2],
    "lighter": [3, 1, 2],
    "plus-darker": [3, 1, 2],
    "plus-lighter": [3, 1, 2],
    // blending modes
    "normal": [2, 1, 2],
    "multiply": [3, 1, 2],
    "screen": [3, 1, 2],
    "overlay": [3, 1, 2],
    "darken": [1, 1, 2],
    "color-dodge": [3, 1, 2],
    "color-burn": [3, 1, 2],
    "hard-light": [3, 1, 2],
    "soft-light": [3, 1, 2],
    "difference": [3, 1, 2],
    "exclusion": [3, 1, 2],
    "hue": [3, 1, 2],
    "saturation": [3, 1, 2],
    "color": [3, 1, 2],
    "luminosity": [3, 1, 2]
  };
  // create two 3*3 canvases that will be used as layers
  var c1 = document.createElement('canvas');
  c1.width = c1.height = 3;
  var c2 = c1.cloneNode(true),
    // the third one will be the tester
    c3 = c1.cloneNode(true),

    ctx1 = c1.getContext('2d'),
    ctx2 = c2.getContext('2d'),
    ctx3 = c3.getContext('2d');
  // fill our canvases with solid colors
  ctx1.fillStyle = 'green';
  ctx1.fillRect(0, 0, 3, 3);
  ctx2.fillStyle = 'pink';
  ctx2.fillRect(0, 0, 3, 3);
  // get the image data of one pixel that will corresponds to the values in gCO's arrays
  var em = [0, 0, 0, 0], // 0 or empty
    d1 = ctx1.getImageData(0, 0, 1, 1).data, // 1 
    d2 = ctx2.getImageData(0, 0, 1, 1).data; // 2
  // the positions of the pixels in our imageData 
  // again, start with the central one
  var pos = [16, 0, 32];

  // make an array of all our gCOs
  var keys = Object.keys(gCO);
  return keys.filter(function(g) {
    var i;
    // get the array corresponding to the actual key
    var arr = gCO[g];

    var layer = [];
    // get the correct imageData for each layer we should find
    for (i = 0; i < 3; i++) {
      switch (arr[i]) {
        case 0:
          layer[i] = em;
          break;
        case 1:
          layer[i] = d1;
          break;
        case 2:
          layer[i] = d2;
          break;
        case 3:
          layer[i] = null;
          break;
      }
    }
    // first reset the canvas
    ctx3.globalCompositeOperation = 'source-over';
    ctx3.clearRect(0, 0, 3, 3);
    // draw the first layer in the top-left corner
    ctx3.drawImage(c1, -1, -1);
    // set the current gCO
    ctx3.globalCompositeOperation = g;
    // first check the enum is recognized
    if (ctx3.globalCompositeOperation !== g) {
      return false;
    }
    // draw the second layer in the top-right corner so it comes over it
    ctx3.drawImage(c2, 1, 1);
    // get the image data of our test canvas
    var d3 = ctx3.getImageData(0, 0, 3, 3).data;
    // we will first admit that it is supported;
    var tempResult = true;
    // iterate through the 3 positions (center, top-left, bottom-right)
    for (i = 0; i < pos.length; i++) {
      // we know what it should return
      if (layer[i] !== null) {
        // is it the same pixel as expected ?
        tempResult = d3[pos[i]] === layer[i][0] &&
          d3[pos[i] + 1] === layer[i][1] &&
          d3[pos[i] + 2] === layer[i][2] &&
          d3[pos[i] + 3] === layer[i][3];
      }
      // some blending operation
      else {
        // is it different than the last drawn layer ? 
        //(if the mode is not supported, the default gCO "source-over" will be used)
        tempResult = d3[pos[i]] !== d2[0] || d3[pos[i] + 1] !== d2[1] || d3[pos[i] + 2] !== d2[2] || d3[pos[i] + 3] !== d2[3];
      }
      // our flag switched to false
      if (!tempResult)
      // no need to go to the other pixels, it's not supported
        return false;
    }
    // this mode is supported
    return true;
  });
}
var supportedGCO = testGCOModes();
log.innerHTML = supportedGCO.join(' ');
<p id="log"></p>

于 2015-11-27T11:41:34.417 に答える
2

public test(blendModeName)メソッドを使用して、Kaiidoのソリューションを js オブジェクトに変換しました。たぶんそれは誰かの役に立ちます。

// based on http://stackoverflow.com/questions/33955992/js-how-to-get-list-of-supported-html-canvas-globalcompositeoperation-types
function BlendModeTester () {
  var ctx1, c1, ctx2, c2, ctx3, c3;
  var pos;
  var em, d1, d2;
  var blendModeDefinition = {
      "source-over": [2, 1, 2],
      "source-in": [2, 0, 0],
      "source-out": [0, 0, 2],
      "source-atop": [2, 1, 0],
      "destination-over": [1, 1, 2],
      "destination-in": [1, 0, 0],
      "destination-out": [0, 1, 0],
      "destination-atop": [1, 0, 2],
      "lighter": [3, 1, 2],
      "copy": [2, 0, 2],
      "xor": [0, 1, 2],
      "multiply": [3, 1, 2],
      "screen": [3, 1, 2],
      "overlay": [3, 1, 2],
      "darken": [1, 1, 2],
      "color-dodge": [3, 1, 2],
      "color-burn": [3, 1, 2],
      "hard-light": [3, 1, 2],
      "soft-light": [3, 1, 2],
      "difference": [3, 1, 2],
      "exclusion": [3, 1, 2],
      "hue": [3, 1, 2],
      "saturation": [3, 1, 2],
      "color": [3, 1, 2],
      "luminosity": [3, 1, 2]
    };

  this.initialize = function () {
    // create two 3*3 canvases that will be used as layers
    c1 = document.createElement('canvas');
    c1.width = c1.height = 3;
    c2 = c1.cloneNode(true);
    // the third one will be the tester
    c3 = c1.cloneNode(true);

    ctx1 = c1.getContext('2d');
    ctx2 = c2.getContext('2d');
    ctx3 = c3.getContext('2d');

    // fill our canvases with solid colors
    ctx1.fillStyle = 'green';
    ctx1.fillRect(0, 0, 3, 3);
    ctx2.fillStyle = 'pink';
    ctx2.fillRect(0, 0, 3, 3);

    // get the image data of one pixel that will correspond to the values in the blendModeDefinition array
    em = [0, 0, 0, 0], // 0 or empty
    d1 = ctx1.getImageData(0, 0, 1, 1).data, // 1
    d2 = ctx2.getImageData(0, 0, 1, 1).data; // 2

    // the positions of the pixels in our imageData
    // again, start with the central one
    pos = [16, 0, 32];
  }

  this.test = function(blendModeName) {
    var i;
    // get the array corresponding to the actual key
    var arr = blendModeDefinition[blendModeName];

    var layer = [];
    // get the correct imageData for each layer we should find
    for (i = 0; i < 3; i++) {
      switch (arr[i]) {
        case 0:
          layer[i] = em;
          break;
        case 1:
          layer[i] = d1;
          break;
        case 2:
          layer[i] = d2;
          break;
        case 3:
          layer[i] = null;
          break;
      }
    }
    // first reset the canvas
    ctx3.globalCompositeOperation = 'source-over';
    ctx3.clearRect(0, 0, 3, 3);
    // draw the first layer in the top-left corner
    ctx3.drawImage(c1, -1, -1);
    // set the current blend mode
    ctx3.globalCompositeOperation = blendModeName;
    // draw the second layer in the top-right corner so it comes over it
    ctx3.drawImage(c2, 1, 1);
    // get the image data of our test canvas
    var d3 = ctx3.getImageData(0, 0, 3, 3).data;
    // we will first admit that it is supported;
    var tempResult = true;
    // iterate through the 3 positions (center, top-left, bottom-right)
    for (i = 0; i < pos.length; i++) {
      // we know what it should return
      if (layer[i] !== null) {
        // is it the same pixel as expected ?
        tempResult = d3[pos[i]] === layer[i][0] &&
          d3[pos[i] + 1] === layer[i][1] &&
          d3[pos[i] + 2] === layer[i][2] &&
          d3[pos[i] + 3] === layer[i][3];
      }
      // some blending operation
      else {
        // is it different than the last drawn layer ?
        //(if the mode is not supported, the default blend mode "source-over" will be used)
        tempResult = d3[pos[i]] !== d2[0] || d3[pos[i] + 1] !== d2[1] || d3[pos[i] + 2] !== d2[2] || d3[pos[i] + 3] !== d2[3];
      }
      // our flag switched to false
      if (!tempResult)
      // no need to go to the other pixels, it's not supported
        return false;
    }
    // this mode is supported
    return true;
  }
}

これにより、すべてを一度にテストする代わりに、特定のブレンド モードをテストできます。

var blendModeTester = new BlendModeTester();
blendModeTester.initialize();

if(blendModeTester.test('hue')) { 
  // do stuff 
};
于 2016-09-26T12:41:44.953 に答える