将线性渐变应用于 Canvas 图案 (Canvas API)
Applying a linear gradient to a CanvasPattern (Canvas API)
我有一个重复的简单图案和一个超出图案尺寸的渐变。可以在重复时将线性渐变应用于渲染的图案吗?
我尝试了以下方法,但实际上并没有像我预期的那样绘制渐变:
const canvas = document.getElementById('canvas');
const context = canvas.getContext('2d');
const gradient = context.createLinearGradient(0, 0, canvas.width, canvas.height);
gradient.addColorStop(0, '#ff0000');
gradient.addColorStop(1, '#00ff00');
const patternCanvas = document.createElement('canvas');
const patternContext = patternCanvas.getContext('2d');
patternCanvas.height = 10;
patternCanvas.width = 10;
patternContext.fillStyle = gradient; // this doesn't work as expected
patternContext.arc(5, 5, 2.5, 0, Math.PI * 2);
patternContext.fill();
const pattern = context.createPattern(patternCanvas, 'repeat');
context.fillStyle = pattern;
context.fillRect(0, 0, canvas.width, canvas.height);
<canvas id = 'canvas'>
CanvasPattern 仅保存位图,即像素。除了变换它(如移动、缩放、旋转)之外,你不能让它动态变化。
因此,您应用相同图案形状但使用渐变填充的想法仅使用 CanvasPattern 是行不通的。
不过实现起来还是很简单的,分两步:
每次你想画这个渐变图案的时候,你都会先把图案填充成纯色,然后把复合操作改为源输入,然后应用你的渐变。
const ctx = createCanvasContext2d( 500, 500 );
document.body.append( ctx.canvas );
const pat = createPattern();
const grad = ctx.createLinearGradient( 0, 0, 500, 500);
grad.addColorStop( 0, 'red' );
grad.addColorStop( 0.5, 'yellow' );
grad.addColorStop( 1, 'blue' );
ctx.arc( 250, 250, 250, 0, Math.PI*2 );
// first the pattern
ctx.fillStyle = pat;
ctx.fill();
// change the composite operation
ctx.globalCompositeOperation = 'source-in';
// now draw the gradient
ctx.fillStyle = grad;
ctx.fill();
// reset to default
ctx.globalCompositeOperation = 'source-over';
function createPattern() {
// we create a small canvas context just for the pattern
const pattern_ctx = createCanvasContext2d( 20, 20 );
// simply a black circle
pattern_ctx.arc( 10, 10, 5, Math.PI*2, 0 );
pattern_ctx.fill();
return pattern_ctx.createPattern( pattern_ctx.canvas, 'repeat' );
}
function createCanvasContext2d( width=300, height=width||150 ) {
const canvas = document.createElement( 'canvas' );
canvas.width = width;
canvas.height = height;
return canvas.getContext( '2d' );
}
但是这种方法的缺点是这个操作需要你有一个清晰的上下文,因为绘制渐变时所有不透明的东西都会被填充,而所有不在填充区域中的东西都会被移除:
const ctx = createCanvasContext2d( 500, 500 );
document.body.append( ctx.canvas );
const pat = createPattern();
const grad = ctx.createLinearGradient( 0, 0, 500, 500);
grad.addColorStop( 0, 'red' );
grad.addColorStop( 0.5, 'yellow' );
grad.addColorStop( 1, 'blue' );
// draw a green rect both in and out of our future gradient pattern
ctx.fillStyle = 'green';
ctx.fillRect(0,0,150,150);
ctx.arc( 250, 250, 250, 0, Math.PI*2 );
// first the pattern
ctx.fillStyle = pat;
ctx.fill();
// change the composite operation
ctx.globalCompositeOperation = 'source-in';
// now draw the gradient
ctx.fillStyle = grad;
ctx.fill();
// reset to default
ctx.globalCompositeOperation = 'source-over';
function createPattern() {
// we create a small canvas context just for the pattern
const pattern_ctx = createCanvasContext2d( 20, 20 );
// simply a black circle
pattern_ctx.arc( 10, 10, 5, Math.PI*2, 0 );
pattern_ctx.fill();
return pattern_ctx.createPattern( pattern_ctx.canvas, 'repeat' );
}
function createCanvasContext2d( width=300, height=width||150 ) {
const canvas = document.createElement( 'canvas' );
canvas.width = width;
canvas.height = height;
return canvas.getContext( '2d' );
}
为避免这种情况,您可以保留仅用于进行此类合成操作的第二个上下文,并且您将能够使用 drawImage 在主上下文上绘图。
const ctx = createCanvasContext2d( 500, 500 );
document.body.append( ctx.canvas );
const compositing_ctx = createCanvasContext2d( 500, 500 );
const pat = createPattern();
const grad = ctx.createLinearGradient( 0, 0, 500, 500);
grad.addColorStop( 0, 'red' );
grad.addColorStop( 0.5, 'yellow' );
grad.addColorStop( 1, 'blue' );
// draw a green rect both in and out of our future gradient pattern
ctx.fillStyle = 'green';
ctx.fillRect(0,0,150,150);
// make the compositing on the off-screen context
compositing_ctx.arc( 250, 250, 250, 0, Math.PI*2 );
compositing_ctx.fillStyle = pat;
compositing_ctx.fill();
compositing_ctx.globalCompositeOperation = 'source-in';
compositing_ctx.fillStyle = grad;
compositing_ctx.fill();
compositing_ctx.globalCompositeOperation = 'source-over';
// draw to main
ctx.drawImage( compositing_ctx.canvas, 0, 0 );
// And a small red one?
compositing_ctx.clearRect( 0, 0, 500, 500 );
compositing_ctx.beginPath();
compositing_ctx.rect( 350, 0, 150, 150 );
compositing_ctx.fillStyle = pat;
compositing_ctx.fill();
compositing_ctx.globalCompositeOperation = 'source-in';
compositing_ctx.fillStyle = "red";
compositing_ctx.fill();
compositing_ctx.globalCompositeOperation = 'source-over';
// draw to main
ctx.drawImage( compositing_ctx.canvas, 0, 0 );
function createPattern() {
// we create a small canvas context just for the pattern
const pattern_ctx = createCanvasContext2d( 20, 20 );
// simply a black circle
pattern_ctx.arc( 10, 10, 5, Math.PI*2, 0 );
pattern_ctx.fill();
return pattern_ctx.createPattern( pattern_ctx.canvas, 'repeat' );
}
function createCanvasContext2d( width=300, height=width||150 ) {
const canvas = document.createElement( 'canvas' );
canvas.width = width;
canvas.height = height;
return canvas.getContext( '2d' );
}
我有一个重复的简单图案和一个超出图案尺寸的渐变。可以在重复时将线性渐变应用于渲染的图案吗?
我尝试了以下方法,但实际上并没有像我预期的那样绘制渐变:
const canvas = document.getElementById('canvas');
const context = canvas.getContext('2d');
const gradient = context.createLinearGradient(0, 0, canvas.width, canvas.height);
gradient.addColorStop(0, '#ff0000');
gradient.addColorStop(1, '#00ff00');
const patternCanvas = document.createElement('canvas');
const patternContext = patternCanvas.getContext('2d');
patternCanvas.height = 10;
patternCanvas.width = 10;
patternContext.fillStyle = gradient; // this doesn't work as expected
patternContext.arc(5, 5, 2.5, 0, Math.PI * 2);
patternContext.fill();
const pattern = context.createPattern(patternCanvas, 'repeat');
context.fillStyle = pattern;
context.fillRect(0, 0, canvas.width, canvas.height);
<canvas id = 'canvas'>
CanvasPattern 仅保存位图,即像素。除了变换它(如移动、缩放、旋转)之外,你不能让它动态变化。
因此,您应用相同图案形状但使用渐变填充的想法仅使用 CanvasPattern 是行不通的。
不过实现起来还是很简单的,分两步:
每次你想画这个渐变图案的时候,你都会先把图案填充成纯色,然后把复合操作改为源输入,然后应用你的渐变。
const ctx = createCanvasContext2d( 500, 500 );
document.body.append( ctx.canvas );
const pat = createPattern();
const grad = ctx.createLinearGradient( 0, 0, 500, 500);
grad.addColorStop( 0, 'red' );
grad.addColorStop( 0.5, 'yellow' );
grad.addColorStop( 1, 'blue' );
ctx.arc( 250, 250, 250, 0, Math.PI*2 );
// first the pattern
ctx.fillStyle = pat;
ctx.fill();
// change the composite operation
ctx.globalCompositeOperation = 'source-in';
// now draw the gradient
ctx.fillStyle = grad;
ctx.fill();
// reset to default
ctx.globalCompositeOperation = 'source-over';
function createPattern() {
// we create a small canvas context just for the pattern
const pattern_ctx = createCanvasContext2d( 20, 20 );
// simply a black circle
pattern_ctx.arc( 10, 10, 5, Math.PI*2, 0 );
pattern_ctx.fill();
return pattern_ctx.createPattern( pattern_ctx.canvas, 'repeat' );
}
function createCanvasContext2d( width=300, height=width||150 ) {
const canvas = document.createElement( 'canvas' );
canvas.width = width;
canvas.height = height;
return canvas.getContext( '2d' );
}
但是这种方法的缺点是这个操作需要你有一个清晰的上下文,因为绘制渐变时所有不透明的东西都会被填充,而所有不在填充区域中的东西都会被移除:
const ctx = createCanvasContext2d( 500, 500 );
document.body.append( ctx.canvas );
const pat = createPattern();
const grad = ctx.createLinearGradient( 0, 0, 500, 500);
grad.addColorStop( 0, 'red' );
grad.addColorStop( 0.5, 'yellow' );
grad.addColorStop( 1, 'blue' );
// draw a green rect both in and out of our future gradient pattern
ctx.fillStyle = 'green';
ctx.fillRect(0,0,150,150);
ctx.arc( 250, 250, 250, 0, Math.PI*2 );
// first the pattern
ctx.fillStyle = pat;
ctx.fill();
// change the composite operation
ctx.globalCompositeOperation = 'source-in';
// now draw the gradient
ctx.fillStyle = grad;
ctx.fill();
// reset to default
ctx.globalCompositeOperation = 'source-over';
function createPattern() {
// we create a small canvas context just for the pattern
const pattern_ctx = createCanvasContext2d( 20, 20 );
// simply a black circle
pattern_ctx.arc( 10, 10, 5, Math.PI*2, 0 );
pattern_ctx.fill();
return pattern_ctx.createPattern( pattern_ctx.canvas, 'repeat' );
}
function createCanvasContext2d( width=300, height=width||150 ) {
const canvas = document.createElement( 'canvas' );
canvas.width = width;
canvas.height = height;
return canvas.getContext( '2d' );
}
为避免这种情况,您可以保留仅用于进行此类合成操作的第二个上下文,并且您将能够使用 drawImage 在主上下文上绘图。
const ctx = createCanvasContext2d( 500, 500 );
document.body.append( ctx.canvas );
const compositing_ctx = createCanvasContext2d( 500, 500 );
const pat = createPattern();
const grad = ctx.createLinearGradient( 0, 0, 500, 500);
grad.addColorStop( 0, 'red' );
grad.addColorStop( 0.5, 'yellow' );
grad.addColorStop( 1, 'blue' );
// draw a green rect both in and out of our future gradient pattern
ctx.fillStyle = 'green';
ctx.fillRect(0,0,150,150);
// make the compositing on the off-screen context
compositing_ctx.arc( 250, 250, 250, 0, Math.PI*2 );
compositing_ctx.fillStyle = pat;
compositing_ctx.fill();
compositing_ctx.globalCompositeOperation = 'source-in';
compositing_ctx.fillStyle = grad;
compositing_ctx.fill();
compositing_ctx.globalCompositeOperation = 'source-over';
// draw to main
ctx.drawImage( compositing_ctx.canvas, 0, 0 );
// And a small red one?
compositing_ctx.clearRect( 0, 0, 500, 500 );
compositing_ctx.beginPath();
compositing_ctx.rect( 350, 0, 150, 150 );
compositing_ctx.fillStyle = pat;
compositing_ctx.fill();
compositing_ctx.globalCompositeOperation = 'source-in';
compositing_ctx.fillStyle = "red";
compositing_ctx.fill();
compositing_ctx.globalCompositeOperation = 'source-over';
// draw to main
ctx.drawImage( compositing_ctx.canvas, 0, 0 );
function createPattern() {
// we create a small canvas context just for the pattern
const pattern_ctx = createCanvasContext2d( 20, 20 );
// simply a black circle
pattern_ctx.arc( 10, 10, 5, Math.PI*2, 0 );
pattern_ctx.fill();
return pattern_ctx.createPattern( pattern_ctx.canvas, 'repeat' );
}
function createCanvasContext2d( width=300, height=width||150 ) {
const canvas = document.createElement( 'canvas' );
canvas.width = width;
canvas.height = height;
return canvas.getContext( '2d' );
}