CanvasRenderingContext2D: globalCompositeOperation 屬性
Canvas 2D API 的 CanvasRenderingContext2D.globalCompositeOperation 屬性用於設定繪製新形狀時應用的複合操作型別。
值
一個字串,用於標識要使用的複合或混合模式操作。它可以是以下任何值:
"source-over"-
這是預設設定,會在現有畫布內容之上繪製新形狀。
"source-in"-
新形狀僅在新形狀和目標畫布重疊的區域繪製。其他所有內容都將變為透明。
"source-out"-
新形狀在新形狀不與現有畫布內容重疊的區域繪製。
"source-atop"-
新形狀僅在新形狀與現有畫布內容重疊的區域繪製。
"destination-over"-
新形狀會在現有畫布內容之後繪製。
"destination-in"-
現有畫布內容會在新形狀和現有畫布內容重疊的區域保留。其他所有內容都將變為透明。
"destination-out"-
現有內容會在不與新形狀重疊的區域保留。
"destination-atop"-
現有畫布僅在新形狀重疊的區域保留。新形狀會在畫布內容之後繪製。
"lighter"-
在兩個形狀重疊的區域,顏色透過將顏色值相加來確定。
"copy"-
僅顯示新形狀。
"xor"-
在兩個形狀重疊的區域,形狀會變為透明;在其他所有區域正常繪製。
"multiply"-
頂層畫素與底層對應畫素相乘。結果是更暗的影像。
"screen"-
畫素反轉、相乘,然後再次反轉。結果是更亮的影像(與
multiply相反)。 "overlay"-
multiply和screen的組合。基礎圖層的暗部變得更暗,亮部變得更亮。 "darken"-
保留兩個圖層中最暗的畫素。
"lighten"-
保留兩個圖層中最亮的畫素。
"color-dodge"-
用反轉的頂層圖層除以底層圖層。
"color-burn"-
用頂層圖層除以反轉的底層圖層,然後反轉結果。
"hard-light"-
類似於
overlay,是multiply和screen的組合——但頂層和底層被交換了。 "soft-light"-
hard-light的柔和版本。純黑或純白不會產生純黑或純白。 "difference"-
從頂層圖層減去底層圖層(或反之),始終得到一個正值。
"exclusion"-
類似於
difference,但對比度較低。 "hue"-
保留底層圖層的亮度(luma)和色度(chroma),同時採用頂層圖層的色相(hue)。
"saturation"-
保留底層圖層的亮度(luma)和色相(hue),同時採用頂層圖層的色度(chroma)。
"color"-
保留底層圖層的亮度(luma),同時採用頂層圖層的色相(hue)和色度(chroma)。
"luminosity"-
保留底層圖層的色相(hue)和色度(chroma),同時採用頂層圖層的亮度(luma)。
示例
更改複合操作
此示例使用 globalCompositeOperation 屬性繪製兩個矩形,它們在重疊區域會相互排除。
HTML
<canvas id="canvas"></canvas>
JavaScript
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
ctx.globalCompositeOperation = "xor";
ctx.fillStyle = "blue";
ctx.fillRect(10, 10, 100, 100);
ctx.fillStyle = "red";
ctx.fillRect(50, 50, 100, 100);
結果
所有值的演示
全域性值
此程式碼設定了程式其餘部分使用的全域性值。
const canvas1 = document.createElement("canvas");
const canvas2 = document.createElement("canvas");
const gco = [
"source-over",
"source-in",
"source-out",
"source-atop",
"destination-over",
"destination-in",
"destination-out",
"destination-atop",
"lighter",
"copy",
"xor",
"multiply",
"screen",
"overlay",
"darken",
"lighten",
"color-dodge",
"color-burn",
"hard-light",
"soft-light",
"difference",
"exclusion",
"hue",
"saturation",
"color",
"luminosity",
].reverse();
const gcoText = [
"This is the default setting and draws new shapes on top of the existing canvas content.",
"The new shape is drawn only where both the new shape and the destination canvas overlap. Everything else is made transparent.",
"The new shape is drawn where it doesn't overlap the existing canvas content.",
"The new shape is only drawn where it overlaps the existing canvas content.",
"New shapes are drawn behind the existing canvas content.",
"The existing canvas content is kept where both the new shape and existing canvas content overlap. Everything else is made transparent.",
"The existing content is kept where it doesn't overlap the new shape.",
"The existing canvas is only kept where it overlaps the new shape. The new shape is drawn behind the canvas content.",
"Where both shapes overlap the color is determined by adding color values.",
"Only the new shape is shown.",
"Shapes are made transparent where both overlap and drawn normal everywhere else.",
"The pixels of the top layer are multiplied with the corresponding pixel of the bottom layer. A darker picture is the result.",
"The pixels are inverted, multiplied, and inverted again. A lighter picture is the result (opposite of multiply)",
"A combination of multiply and screen. Dark parts on the base layer become darker, and light parts become lighter.",
"Retains the darkest pixels of both layers.",
"Retains the lightest pixels of both layers.",
"Divides the bottom layer by the inverted top layer.",
"Divides the inverted bottom layer by the top layer, and then inverts the result.",
"A combination of multiply and screen like overlay, but with top and bottom layer swapped.",
"A softer version of hard-light. Pure black or white does not result in pure black or white.",
"Subtracts the bottom layer from the top layer or the other way round to always get a positive value.",
"Like difference, but with lower contrast.",
"Preserves the luma and chroma of the bottom layer, while adopting the hue of the top layer.",
"Preserves the luma and hue of the bottom layer, while adopting the chroma of the top layer.",
"Preserves the luma of the bottom layer, while adopting the hue and chroma of the top layer.",
"Preserves the hue and chroma of the bottom layer, while adopting the luma of the top layer.",
].reverse();
const width = 320;
const height = 340;
// lum in sRGB
const lum = {
r: 0.33,
g: 0.33,
b: 0.33,
};
// resize canvas
canvas1.width = width;
canvas1.height = height;
canvas2.width = width;
canvas2.height = height;
主程式
此程式碼 runComposite() 負責大部分工作,並依賴一些輔助函式來完成困難的部分。
function createCanvas(op) {
const canvas = document.createElement("canvas");
canvas.style.background = `url(${JSON.stringify(op.data)})`;
canvas.style.border = "1px solid black";
canvas.style.margin = "5px";
canvas.width = width / 2;
canvas.height = height / 2;
return canvas;
}
function runComposite(op) {
const dl = document.createElement("dl");
document.body.appendChild(dl);
while (gco.length) {
const pop = gco.pop();
const dt = document.createElement("dt");
dt.textContent = pop;
dl.appendChild(dt);
const dd = document.createElement("dd");
const p = document.createElement("p");
p.textContent = gcoText.pop();
dd.appendChild(p);
const canvasToDrawOn = createCanvas(op);
const canvasToDrawFrom = createCanvas(op);
const canvasToDrawResult = createCanvas(op);
let ctx = canvasToDrawResult.getContext("2d");
ctx.clearRect(0, 0, width, height);
ctx.save();
ctx.drawImage(canvas1, 0, 0, width / 2, height / 2);
ctx.globalCompositeOperation = pop;
ctx.drawImage(canvas2, 0, 0, width / 2, height / 2);
ctx.globalCompositeOperation = "source-over";
ctx.fillStyle = "rgb(0 0 0 / 80%)";
ctx.fillRect(0, height / 2 - 20, width / 2, 20);
ctx.fillStyle = "white";
ctx.font = "14px arial";
ctx.fillText(pop, 5, height / 2 - 5);
ctx.restore();
ctx = canvasToDrawOn.getContext("2d");
ctx.clearRect(0, 0, width, height);
ctx.save();
ctx.drawImage(canvas1, 0, 0, width / 2, height / 2);
ctx.fillStyle = "rgb(0 0 0 / 80%)";
ctx.fillRect(0, height / 2 - 20, width / 2, 20);
ctx.fillStyle = "white";
ctx.font = "14px arial";
ctx.fillText("existing content", 5, height / 2 - 5);
ctx.restore();
ctx = canvasToDrawFrom.getContext("2d");
ctx.clearRect(0, 0, width, height);
ctx.save();
ctx.drawImage(canvas2, 0, 0, width / 2, height / 2);
ctx.fillStyle = "rgb(0 0 0 / 80%)";
ctx.fillRect(0, height / 2 - 20, width / 2, 20);
ctx.fillStyle = "white";
ctx.font = "14px arial";
ctx.fillText("new content", 5, height / 2 - 5);
ctx.restore();
dd.appendChild(canvasToDrawOn);
dd.appendChild(canvasToDrawFrom);
dd.appendChild(canvasToDrawResult);
dl.appendChild(dd);
}
}
輔助函式
程式依賴於一些輔助函式。
function lightMix() {
const ctx = canvas2.getContext("2d");
ctx.save();
ctx.globalCompositeOperation = "lighter";
ctx.beginPath();
ctx.fillStyle = "red";
ctx.arc(100, 200, 100, Math.PI * 2, 0, false);
ctx.fill();
ctx.beginPath();
ctx.fillStyle = "blue";
ctx.arc(220, 200, 100, Math.PI * 2, 0, false);
ctx.fill();
ctx.beginPath();
ctx.fillStyle = "lime";
ctx.arc(160, 100, 100, Math.PI * 2, 0, false);
ctx.fill();
ctx.restore();
ctx.beginPath();
ctx.fillStyle = "red";
ctx.fillRect(0, 0, 30, 30);
ctx.fill();
}
function colorSphere() {
const ctx = canvas1.getContext("2d");
const width = 360;
const halfWidth = width / 2;
const rotate = (1 / 360) * Math.PI * 2; // per degree
const offset = 0; // scrollbar offset
const oLeft = -20;
const oTop = -20;
for (let n = 0; n <= 359; n++) {
const gradient = ctx.createLinearGradient(
oLeft + halfWidth,
oTop,
oLeft + halfWidth,
oTop + halfWidth,
);
const color = Color.HSV_RGB({ H: (n + 300) % 360, S: 100, V: 100 });
gradient.addColorStop(0, "transparent");
gradient.addColorStop(0.7, `rgb(${color.R} ${color.G} ${color.B})`);
gradient.addColorStop(1, "white");
ctx.beginPath();
ctx.moveTo(oLeft + halfWidth, oTop);
ctx.lineTo(oLeft + halfWidth, oTop + halfWidth);
ctx.lineTo(oLeft + halfWidth + 6, oTop);
ctx.fillStyle = gradient;
ctx.fill();
ctx.translate(oLeft + halfWidth, oTop + halfWidth);
ctx.rotate(rotate);
ctx.translate(-(oLeft + halfWidth), -(oTop + halfWidth));
}
ctx.beginPath();
ctx.fillStyle = "blue";
ctx.fillRect(15, 15, 30, 30);
ctx.fill();
return ctx.canvas;
}
// HSV (1978) = H: Hue / S: Saturation / V: Value
Color = {};
Color.HSV_RGB = (o) => {
const S = o.S / 100;
let H = o.H / 360,
V = o.V / 100;
let R, G;
let A, B, C, D;
if (S === 0) {
R = G = B = Math.round(V * 255);
} else {
if (H >= 1) H = 0;
H *= 6;
D = H - Math.floor(H);
A = Math.round(255 * V * (1 - S));
B = Math.round(255 * V * (1 - S * D));
C = Math.round(255 * V * (1 - S * (1 - D)));
V = Math.round(255 * V);
switch (Math.floor(H)) {
case 0:
R = V;
G = C;
B = A;
break;
case 1:
R = B;
G = V;
B = A;
break;
case 2:
R = A;
G = V;
B = C;
break;
case 3:
R = A;
G = B;
B = V;
break;
case 4:
R = C;
G = A;
B = V;
break;
case 5:
R = V;
G = A;
// B remains unchanged
break;
}
}
return { R, G, B };
};
function createInterlace(size, color1, color2) {
const proto = document.createElement("canvas").getContext("2d");
proto.canvas.width = size * 2;
proto.canvas.height = size * 2;
proto.fillStyle = color1; // top-left
proto.fillRect(0, 0, size, size);
proto.fillStyle = color2; // top-right
proto.fillRect(size, 0, size, size);
proto.fillStyle = color2; // bottom-left
proto.fillRect(0, size, size, size);
proto.fillStyle = color1; // bottom-right
proto.fillRect(size, size, size, size);
const pattern = proto.createPattern(proto.canvas, "repeat");
pattern.data = proto.canvas.toDataURL();
return pattern;
}
const op_8x8 = createInterlace(8, "white", "#eeeeee");
開始執行
最後,我們呼叫函式來啟動一切。
lightMix();
colorSphere();
runComposite(op_8x8);
結果
規範
| 規範 |
|---|
| HTML # dom-context-2d-globalcompositeoperation-dev |
瀏覽器相容性
載入中…