1. 面向开发者的 Web 技术
  2. Web API
  3. CanvasRenderingContext2D
  4. CanvasRenderingContext2D:globalCompositeOperation 属性

此页面由社区从英文翻译而来。了解更多并加入 MDN Web Docs 社区。

View in English Always switch to English

CanvasRenderingContext2D:globalCompositeOperation 属性

Baseline Widely available

This feature is well established and works across many devices and browser versions. It’s been available across browsers since ⁨2015年7月⁩.

Canvas 2D API 的 CanvasRenderingContext2D.globalCompositeOperation 属性设置要在绘制新形状时应用的合成操作的类型。

参见 Canvas 教程中的合成和裁剪

标识要使用的合成或混合模式操作的字符串。可以是以下值之一:

"source-over"

这是默认设置,并在现有画布上绘制新图形。

"source-in"

仅在新形状和目标画布重叠的地方绘制新形状。其他的都是透明的。

"source-out"

在不与现有画布内容重叠的地方绘制新图形。

"source-atop"

只在与现有画布内容重叠的地方绘制新图形。

"destination-over"

在现有画布内容的后面绘制新的图形。

"destination-in"

仅保留现有画布内容和新形状重叠的部分。其他的都是透明的。

"destination-out"

仅保留现有画布内容和新形状不重叠的部分。

"destination-atop"

仅保留现有画布内容和新形状重叠的部分。新形状是在现有画布内容的后面绘制的。

"lighter"

两个重叠图形的颜色是通过颜色值相加来确定的。

"copy"

只显示新图形。

"xor"

形状在重叠处变为透明,并在其他地方正常绘制。

"multiply"

将顶层像素与底层相应像素相乘,结果是一幅更黑暗的图片。

"screen"

像素被倒转、相乘、再倒转,结果是一幅更明亮的图片(与 multiply 相反)。

"overlay"

multiplyscreen 的结合。原本暗的地方更暗,原本亮的地方更亮。

"darken"

保留两个图层中最暗的像素。

"lighten"

保留两个图层中最亮的像素。

"color-dodge"

将底层除以顶层的反置。

"color-burn"

将反置的底层除以顶层,然后将结果反过来。

"hard-light"

类似于 overlay,multiplyscreen 的结合——但上下图层互换了。

"soft-light"

柔和版本的 hard-light。纯黑或纯白不会导致纯黑或纯白。

"difference"

从顶层减去底层(或反之亦然),始终得到正值。

"exclusion"

difference 类似,但对比度较低。

"hue"

保留底层的亮度(luma)和色度(chroma),同时采用顶层的色调(hue)。

"saturation"

保留底层的亮度和色调,同时采用顶层的色度。

"color"

保留了底层的亮度,同时采用了顶层的色调和色度。

"luminosity"

保持底层的色调和色度,同时采用顶层的亮度。

示例

改变合成操作

这是一段使用 globalCompositeOperation 属性的简单的代码片段,绘制了 2 个矩形在重叠时相互排斥的情况。

HTML

html
<canvas id="canvas"></canvas>

JavaScript

js
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);

结果

展示所有值

全局值

这段代码设置了程序的其他部分使用的全局值。

js
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 = [
 "这是默认设置,并在现有画布上下文之上绘制新图形。",
 "新图形只在新图形和目标画布重叠的地方绘制。其他的都是透明的。",
 "在不与现有画布内容重叠的地方绘制新图形。",
 "新图形只在与现有画布内容重叠的地方绘制。",
 "在现有的画布内容后面绘制新的图形。",
 "现有的画布内容保持在新图形和现有画布内容重叠的位置。其他的都是透明的。",
 "现有内容保持在新图形不重叠的地方。",
 "现有的画布只保留与新图形重叠的部分,新的图形是在画布内容后面绘制的。",
 "两个重叠图形的颜色是通过颜色值相加来确定的。",
 "只显示新图形。",
 "图像中,那些重叠和正常绘制之外的其他地方是透明的。",
 "将顶层像素与底层相应像素相乘,结果是一幅更黑暗的图片。",
 "像素被倒转,相乘,再倒转,结果是一幅更明亮的图片。",
 "multiply 和 screen 的结合,原本暗的地方更暗,原本亮的地方更亮。",
 "保留两个图层中最暗的像素。",
 "保留两个图层中最亮的像素。",
 "将底层除以顶层的反置。",
 "将反置的底层除以顶层,然后将结果反过来。",
 "multiply 和 screen 的结合,类似于叠加,但上下图层互换了。",
 "用顶层减去底层或者相反来得到一个正值。",
 "一个柔和版本的 hard-light。纯黑或纯白不会导致纯黑或纯白。",
 "和 difference 相似,但对比度较低。",
 "保留了底层的亮度和色度,同时采用了顶层的色调。",
 "保留底层的亮度和色调,同时采用顶层的色度。",
 "保留了底层的亮度,同时采用了顶层的色调和色度。",
 "保持底层的色调和色度,同时采用顶层的亮度。",
].reverse();
const width = 320;
const height = 340;

主程序

在页面加载时,该代码设置并运行示例:

js
window.onload = () => {
 // sRGB 中的 lum
 const lum = {
 r: 0.33,
 g: 0.33,
 b: 0.33,
 };
 // 调整画布大小
 canvas1.width = width;
 canvas1.height = height;
 canvas2.width = width;
 canvas2.height = height;
 lightMix();
 colorSphere();
 runComposite();
 return;
};

而这段 runComposite() 代码处理了大部分的工作,但需要依赖于许多工具函数来完成复杂的处理工作。

js
function createCanvas() {
 const canvas = document.createElement("canvas");
 canvas.style.background = `url(${op_8x8.data})`;
 canvas.style.border = "1px solid #000";
 canvas.style.margin = "5px";
 canvas.width = width / 2;
 canvas.height = height / 2;
 return canvas;
}
function runComposite() {
 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();
 const canvasToDrawFrom = createCanvas();
 const canvasToDrawResult = createCanvas();
 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 = "rgba(0,0,0,0.8)";
 ctx.fillRect(0, height / 2 - 20, width / 2, 20);
 ctx.fillStyle = "#FFF";
 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 = "rgba(0,0,0,0.8)";
 ctx.fillRect(0, height / 2 - 20, width / 2, 20);
 ctx.fillStyle = "#FFF";
 ctx.font = "14px arial";
 ctx.fillText("现有内容", 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 = "rgba(0,0,0,0.8)";
 ctx.fillRect(0, height / 2 - 20, width / 2, 20);
 ctx.fillStyle = "#FFF";
 ctx.font = "14px arial";
 ctx.fillText("新内容", 5, height / 2 - 5);
 ctx.restore();
 dd.appendChild(canvasToDrawOn);
 dd.appendChild(canvasToDrawFrom);
 dd.appendChild(canvasToDrawResult);
 dl.appendChild(dd);
 }
}

工具函数

该程序依赖许多工具函数。

js
const lightMix = () => {
 const ctx = canvas2.getContext("2d");
 ctx.save();
 ctx.globalCompositeOperation = "lighter";
 ctx.beginPath();
 ctx.fillStyle = "rgba(255,0,0,1)";
 ctx.arc(100, 200, 100, Math.PI * 2, 0, false);
 ctx.fill();
 ctx.beginPath();
 ctx.fillStyle = "rgba(0,0,255,1)";
 ctx.arc(220, 200, 100, Math.PI * 2, 0, false);
 ctx.fill();
 ctx.beginPath();
 ctx.fillStyle = "rgba(0,255,0,1)";
 ctx.arc(160, 100, 100, Math.PI * 2, 0, false);
 ctx.fill();
 ctx.restore();
 ctx.beginPath();
 ctx.fillStyle = "#f00";
 ctx.fillRect(0, 0, 30, 30);
 ctx.fill();
};
js
const colorSphere = (element) => {
 const ctx = canvas1.getContext("2d");
 const width = 360;
 const halfWidth = width / 2;
 const rotate = (1 / 360) * Math.PI * 2; // 每度的弧度
 const offset = 0; // 滚动条偏移
 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, "rgba(0,0,0,0)");
 gradient.addColorStop(0.7, `rgba(${color.R}, ${color.G}, ${color.B}, 1)`);
 gradient.addColorStop(1, "rgba(255,255,255,1)");
 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 = "#00f";
 ctx.fillRect(15, 15, 30, 30);
 ctx.fill();
 return ctx.canvas;
};
js
// HSV (1978) = H:色调 / S:饱和度 / V:明度
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 = B;
 break;
 }
 }
 return { R, G, B };
};
const createInterlace = (size, color1, color2) => {
 const proto = document.createElement("canvas").getContext("2d");
 proto.canvas.width = size * 2;
 proto.canvas.height = size * 2;
 proto.fillStyle = color1; // 左上角
 proto.fillRect(0, 0, size, size);
 proto.fillStyle = color2; // 右上角
 proto.fillRect(size, 0, size, size);
 proto.fillStyle = color2; // 左下角
 proto.fillRect(0, size, size, size);
 proto.fillStyle = color1; // 右下角
 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, "#FFF", "#eee");

结果

规范

Specification
HTML
# dom-context-2d-globalcompositeoperation-dev

浏览器兼容性

参见

Help improve MDN

Learn how to contribute

This page was last modified on by MDN contributors.

AltStyle によって変換されたページ (->オリジナル) /