JS:如何获取支持的 HTML canvas globalCompositeOperation 类型的列表
JS: How to get list of supported HTML canvas globalCompositeOperation types
我想制作一个 HTML select
列表,我可以选择在混合两个 canvas
元素时应用哪种类型的 globalCompositeOperation
,就像这样:
<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
元素,而不是手动填写?此信息是否存储在某个本机变量中?
我不想只验证用户浏览器是否支持某种混合模式,正如所讨论的 。我想获得支持的 globalCompositeOperation
类型的完整列表,以便在浏览器中选择混合模式。
没有本机 属性 告诉我们浏览器支持哪些 globalCompositeOperation
模式。
您必须通过循环 all spec defined ones 来测试它,并检查它是否仍然是您刚刚设置的那个:
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""
模式,但实际上并不支持它...
所以我在这里做了一个函数来测试不同的模式。
这个想法是在第三个上绘制两个填充纯色的 3x3px canvases。第一个画在左上角,第二个画在左下角,它们在第三个的中心像素共享一个像素canvas。
显然这比 属性 检查慢,但您应该每页只需要一次,因此性能可能不是问题。
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>
我刚刚使用 public test(blendModeName) 方法将 Kaiido 的解决方案转换为 js 对象。也许对某人有用。
// based on
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
};
我想制作一个 HTML select
列表,我可以选择在混合两个 canvas
元素时应用哪种类型的 globalCompositeOperation
,就像这样:
<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
元素,而不是手动填写?此信息是否存储在某个本机变量中?
我不想只验证用户浏览器是否支持某种混合模式,正如所讨论的 globalCompositeOperation
类型的完整列表,以便在浏览器中选择混合模式。
没有本机 属性 告诉我们浏览器支持哪些 globalCompositeOperation
模式。
您必须通过循环 all spec defined ones 来测试它,并检查它是否仍然是您刚刚设置的那个:
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""
模式,但实际上并不支持它...
所以我在这里做了一个函数来测试不同的模式。
这个想法是在第三个上绘制两个填充纯色的 3x3px canvases。第一个画在左上角,第二个画在左下角,它们在第三个的中心像素共享一个像素canvas。
显然这比 属性 检查慢,但您应该每页只需要一次,因此性能可能不是问题。
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>
我刚刚使用 public test(blendModeName) 方法将 Kaiido 的解决方案转换为 js 对象。也许对某人有用。
// based on
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
};