我无法让连续 canvas 个矩形在 js 中具有重复的线性渐变。任何人都知道如何?

I can't get consecutive canvas rectangles to have the duplicate linear-gradient in js. Anyone know how?

我用连续的矩形绘制了一个 canvas,每个矩形都有自己的线性渐变,但我无法使线性渐变显示在矩形上,除了第一个矩形。每个矩形大小相同,每个矩形都应具有相同的线性渐变。我只能使第一个矩形具有正确的渐变,但 none 其余矩形具有正确的渐变。其余为黑色。一直在寻找整个互联网,找不到答案。我哪里做错了?谢谢!

这是我的代码笔:https://codepen.io/monamoves/pen/RQvzOe

又来了。

HTML:

<canvas id="fabric" width="1020" height="300">
</canvas>

CSS:

canvas {
  border: thin red solid;
}

JavaScript:

window.onload = draw;

function draw() {
var cvs = document.getElementById("fabric");
var ctx = cvs.getContext("2d");

var xcoordinate = 0;
var grd = ctx.createLinearGradient(xcoordinate, 0, xcoordinate + 20, 0);

for (var i = 0; i < 50; i++) {
  ctx.beginPath();
  ctx.strokeStyle="#ccc";
  ctx.moveTo(xcoordinate, 0);
  ctx.rect(xcoordinate, 0, 20, 300);
  ctx.stroke();
  ctx.fillStyle = grd;
  grd.addColorStop(0, "black");
  grd.addColorStop(0.5, "white");
  grd.addColorStop(1, "black");
  ctx.fill();
  xcoordinate = xcoordinate + 20;  
}
}

您将一次渐变定义为仅覆盖第一个矩形。渐变不遵循您的形状,如果您告诉它在坐标 0,0 处的宽度为 20 像素,则在这些坐标之外绘制的每个形状都将是您设置的 2 种极端颜色之一。

您可以在 for 循环内的每次迭代中创建一个新的渐变,实际上,如果您想更改其 colorStops,则必须这样做。

但在你的情况下(单一渐变)最好的解决方案是只声明一次你的渐变,只设置一次它的 colorStops,并简单地修改你的上下文的变换矩阵;梯度也会受到影响。

var ctx = cvs.getContext("2d");

var xcoordinate = 0;
// set all this only once if it doesn't change
var grd = ctx.createLinearGradient(0, 0, 20, 0);
ctx.fillStyle = grd;
grd.addColorStop(0, "black");
grd.addColorStop(0.5, "white");
grd.addColorStop(1, "black");
ctx.strokeStyle="#ccc";

for (var i = 0; i < 50; i++) {
  ctx.beginPath();
  // we move the transform matrix
  ctx.setTransform(1,0,0,1,xcoordinate,0);
  // and draw always at same coords
  ctx.moveTo(0, 0);
  ctx.rect(0, 0, 20, 300);
  ctx.stroke();
  ctx.fill();
  xcoordinate = xcoordinate + 20;  
}
<canvas id=cvs></canvas>

路径和渲染变换

当您创建路径时,它使用调用路径函数时的当前变换,而当您使用填充或描边时,您可以设置另一个变换。

这允许您使用单独的坐标系,一个用于形状,一个用于填充,一个用于描边。

如果您对填充和描边使用不同的样式,这将特别有用。

示例适合路径的渐变

该示例显示了两个渐变缩放和平移以适应在它们自己的坐标系中创建的 3 个形状的填充和描边。

笔划有一个注意事项。 lineWidth 是一种样式,在调用 stroke 时由当前变换进行变换。这限制您只能使用统一比例,并且您必须反转 lineWidth 大小。查看代码。

const ctx = canvas.getContext("2d");

const grad1 = [[0, "green"], [0.45, "gold"], [0.55, "gold"], [1, "green"]];
const grad2 = [[0, "red"], [0.25, "black"], [0.75, "black"], [1, "red"]];
const addColStops = (g,s) => s.forEach(s => g.addColorStop(...s));
const style = {lineWidth: 5};

//==============================================================
// fill gradient
style.fillStyle = ctx.createLinearGradient(0, 0, 20, 0);
// to simplify I attach the coords to the gradient so that it can be scaled to
// fit the rendered content
style.fillStyle.coords = { w: 20, h: 0 };
addColStops(style.fillStyle, grad1);

//==============================================================
// stroke gradient
style.strokeStyle = ctx.createLinearGradient(0, 0, 0, 20);
style.strokeStyle.coords = { w: 0, h: 20 };
addColStops(style.strokeStyle, grad2);

//==============================================================
// 3 example boxes
drawBox(10, 10, 60, 130, style);
style.lineWidth += 4;
drawBox(90, 10, 80, 130, style);
style.lineWidth += 8;
drawBox(200, 10, 140, 130, style);

//==============================================================
// draw box
function drawBox(x, y, width, height, style) {

  // shape drawn in default coordinates 
  ctx.setTransform(1, 0, 0, 1, 0, 0); // default transform
  Object.assign(ctx, style); // set the style

  //============================================================
  // create the shape
  ctx.beginPath();
  ctx.rect(x, y, width, height);

  //============================================================
  // Make the gradient fit the stroke
  const s = style.strokeStyle.coords;

  // uniform scale for strokes only and correct for line width
  var scale = (width + ctx.lineWidth) / (s.w ? s.w : 1);
  if (s.w === 0) { scale = (height + ctx.lineWidth) / (s.h ? s.h : 1) }
  ctx.setTransform(
    scale, 0, 0, scale,
    x - ctx.lineWidth / 2, // correct for line width
    y - ctx.lineWidth / 2
  );
  // invert the lineWidth to correct its scale
  ctx.lineWidth /= scale;
  ctx.stroke();

  //============================================================
  // Make the fill gradient fit the rect
  const f = style.fillStyle.coords;
  ctx.setTransform(
    width / (f.w ? f.w : 1), // scale grad width if needed
    0, 0,
    height / (f.h ? f.h : 1), // scale grad heigh if needed
    x, y
  );
  ctx.fill();
}
<canvas id=canvas width="400"></canvas>