JS: How to get list of supported HTML canvas globalCompositeOperation types

前端 未结 2 1386
星月不相逢
星月不相逢 2020-12-12 01:13

I want to make a HTML select list, with which I can choose which type of globalCompositeOperation will be applied when blending two canvas

2条回答
  •  情歌与酒
    2020-12-12 01:24

    I've just converted Kaiido's solution into an js object with a public test(blendModeName) method. Maybe it's of use to someone.

    // 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;
      }
    }
    

    With this you can test for specific blend modes, instead of testing all at once.

    var blendModeTester = new BlendModeTester();
    blendModeTester.initialize();
    
    if(blendModeTester.test('hue')) { 
      // do stuff 
    };
    

提交回复
热议问题