帆布删除线性梯度



我正在使用线性梯度在HTML帆布上下文上绘制并重新绘制数百个图标形状(随着时间的时间),每个图标形状都具有其自己独特的线性梯度。

创建后是否可以重新使用HTML帆布上下文线性梯度?

如果是这样,如何更改x1,y1,x2,y2和停止颜色?

或可以从上下文中删除线性梯度?

或以上两个都没有其他画布功能,例如clearRect从其边界内的上下文中删除所有线性梯度?

,或者如果没有,则与线性渐变儿童相处的长时间(成千上万的上下文?

是的,您可以在创建后重复使用canvasgradient,

var ctx = c.getContext("2d");
var grads = [];
for(var i=0; i<20; i++){
  grads.push(ctx.createLinearGradient(0,0,300,0));
}
grads.forEach(function(grad, i){
  var a = i * (360 / 20);
  grad.addColorStop(0, 'hsl(' + a + ',100%,50%)');
  grad.addColorStop(1, 'hsl(' + (a + 180) + ',100%,50%)');
  sel.appendChild(new Option("gradient " + i, i));
});
sel.onchange = function(){
  ctx.fillStyle = grads[+this.value];
  ctx.fillRect(0,0,c.width,c.height);
};
sel.onchange();
<select id="sel"></select><br>
<canvas id="c"></canvas>

但是不,您无法修改其内部属性在创建时设置的内部属性。您唯一可以做的就是添加新的颜色停止。

可以做的是改变上下文的转换矩阵,这也会影响您的渐变:

var ctx = c.getContext("2d"),
gradWidth = 50, gradHeight = 50,
// a diagonal
grad = ctx.createLinearGradient(0, 0, gradWidth, gradHeight),
angle = 0,x=150,y=40;
grad.addColorStop(0, 'red');
grad.addColorStop(0.5, 'blue');
grad.addColorStop(1, 'green');
ctx.fillStyle = grad;
function draw(){
  angle += Math.PI / 90;
  if(cb.checked){
    x += Math.cos(angle)*2;
    y += Math.sin(angle);
   }
  ctx.setTransform(1,0,0,1,0,0);
  ctx.clearRect(0,0,c.width, c.height);
  ctx.translate(x + gradWidth/2, y + gradHeight/2);
  ctx.rotate(angle);
  ctx.translate(-gradWidth/2, -gradHeight/2);
  ctx.fillRect(0,0,50,50);
  requestAnimationFrame(draw);
}
draw();
<canvas id="c"></canvas>
<label>move on X/Y axes<input type="checkbox" id="cb"></label>

,但这意味着您所有其他图纸也将遵循此转换...

因此,仅移动梯度的一种方法是使用合成,首先在整个画布上绘制梯度,然后仅绘制形状,将globalCompositionOperation设置为'destination-in'

var ctx = c.getContext("2d"),
gradWidth = 150,
gradHeight = 50,
grad = ctx.createLinearGradient(0, 0, gradWidth, gradHeight),
maxSize = Math.max(c.width, c.height),
angle = 0,x=c.width/4,y=c.height/2;
grad.addColorStop(0, 'red');
grad.addColorStop(0.5, 'blue');
grad.addColorStop(1, 'green');
function draw(){
  angle += Math.PI / 90;
  if(cb.checked){
    x += Math.cos(angle)*2;
    y += Math.sin(angle);
   }
  ctx.setTransform(1,0,0,1,0,0);
  ctx.globalCompositeOperation = 'source-over';
  ctx.clearRect(0,0,c.width, c.height);
  ctx.fillStyle = grad;
  ctx.translate(x+gradWidth/2, y+gradHeight/2);
  ctx.rotate(angle);
  ctx.translate(-gradWidth/2, -gradHeight/2);
  ctx.fillRect(-maxSize*2,-maxSize*2,maxSize*4,maxSize*4);
  ctx.setTransform(1,0,0,1,0,0);
  ctx.globalCompositeOperation = 'destination-in';
  ctx.fillStyle = 'black';
  ctx.fillRect(c.width/4,c.height/2,150,50);
  requestAnimationFrame(draw);
}
draw();
<canvas id="c"></canvas>
<label>move on X/Y axes<input type="checkbox" id="cb"></label>

因此,人们可能会写一些可以用方便的方式处理转换的辅助功能,但是我不确定与重新亮相的新梯度相比是否值得的好处值得。


现在,我不清楚您是否真的需要编辑梯度,如果您可能想考虑的一个选项是在外屏幕外创建图标的精灵表画布,然后只需在需要此图标时调用drawImage

const iconWidth = 120;
let icons, points;
const ctx = c.getContext('2d');
onload = e => {
  icons = initIcons();
  points = icons.map(_ => ({
    x: 0,
    y: 0,
    dirX: 0,
    dirY: 0
  }));
  setInterval(updatePoints, 1000);
  draw();
};
function initIcons() {
  const pathes = pathes_d.map(d => new Path2D(d)),
    offCanvas = document.createElement('canvas');
  offCanvas.width = 500;
  offCanvas.height = 500;
  const coords = [],
    offCtx = offCanvas.getContext('2d');
  pathes.concat(pathes.slice(), pathes.slice())
    .forEach(function(p, i) {
      const grad = offCtx.createLinearGradient(0, 0, 120 / 0.2, 120 /0.2),
      rand = ~~(Math.random() * 360);
      grad.addColorStop(0, 'hsl(' +  rand  + ', 100%, 50%');
      grad.addColorStop(1, 'hsl(' + (rand + 180) % 360 + ', 100%, 50%');
      offCtx.fillStyle = grad;
      let w = Math.floor(offCanvas.width / iconWidth),
        a = i / w,
        y = Math.floor(a),
        x = Math.round((a - y) * w) * iconWidth;
      y *= iconWidth;
      offCtx.setTransform(0.2, 0, 0, 0.2, x, y);
      offCtx.fill(p);
      coords.push({
        x,
        y
      });
    });
  coords.canvas = offCanvas;
  return coords;
};
function draw() {
  ctx.clearRect(0, 0, c.width, c.height);
  points.forEach((pt, i) => {
    pt.x += pt.dirX;
    pt.y += pt.dirY;
    if (pt.x + 60 >= c.width || pt.x <= 0) {
      pt.dirX *= -1;
      pt.x += pt.dirX;
    }
    if (pt.y + 60 >= c.height || pt.y <= 0) {
      pt.dirY *= -1;
      pt.y += pt.dirY;
    }
    ctx.drawImage(icons.canvas, icons[i].x, icons[i].y, 120, 120, pt.x, pt.y, 60, 60);
  });
  requestAnimationFrame(draw);
}
function updatePoints() {
  points.forEach(pt => {
    pt.dirX += Math.random() - .5;
    pt.dirY += Math.random() - .5;
  });
}
const pathes_d = [
  "M59.9,62.2c0,6,3.7,12.6,11,20l161.7,161.7v196.5h-81.9c-4.4,0-8.3,1.6-11.5,4.9c-3.2,3.2-4.9,7.1-4.9,11.5  s1.6,8.3,4.9,11.5c3.2,3.2,7.1,4.9,11.5,4.9h229.3c4.4,0,8.3-1.6,11.5-4.9c3.2-3.2,4.9-7.1,4.9-11.5s-1.6-8.3-4.9-11.5  c-3.2-3.2-7.1-4.9-11.5-4.9h-81.9V243.9L459.9,82.2c7.3-7.3,11-14,11-20c0-3.9-1.5-7-4.6-9.3c-3.1-2.3-6.3-3.8-9.7-4.5  c-3.4-0.7-7.1-1-11-1H85.3c-3.9,0-7.6,0.3-11,1c-3.4,0.7-6.7,2.2-9.7,4.5C61.5,55.2,59.9,58.3,59.9,62.2z",
  "M101.7,71.9v286.6c0,8.5,2.9,16.1,8.7,22.8c5.8,6.7,13.1,11.8,22,15.5c8.9,3.7,17.7,6.4,26.5,8.2c8.8,1.8,17,2.7,24.7,2.7  c7.7,0,15.9-0.9,24.7-2.7c8.8-1.8,17.6-4.5,26.5-8.2c8.9-3.7,16.2-8.8,22-15.5c5.8-6.7,8.7-14.2,8.7-22.8s-2.9-16.1-8.7-22.8  c-5.8-6.7-13.1-11.8-22-15.5c-8.9-3.7-17.7-6.4-26.5-8.2c-8.8-1.8-17-2.7-24.7-2.7c-17.9,0-34.3,3.3-49.1,10V182l196.5,60.6V424  c0,8.5,2.9,16.1,8.7,22.8c5.8,6.7,13.1,11.8,22,15.5c8.9,3.7,17.7,6.4,26.5,8.2s17,2.7,24.7,2.7s15.9-0.9,24.7-2.7  s17.6-4.5,26.5-8.2c8.9-3.7,16.2-8.8,22-15.5c5.8-6.7,8.7-14.2,8.7-22.8s-2.9-16.1-8.7-22.8c-5.8-6.7-13.1-11.8-22-15.5  c-8.9-3.7-17.7-6.4-26.5-8.2c-8.8-1.8-17-2.7-24.7-2.7c-17.9,0-34.3,3.3-49.1,10V137.4c0-5.3-1.6-10.1-4.9-14.5  c-3.2-4.4-7.4-7.4-12.5-9.1L133.4,48.4c-2-0.7-4.4-1-7.2-1c-6.8,0-12.6,2.4-17.4,7.2C104,59.3,101.7,65.1,101.7,71.9z",
  "M199.9,227.5c0-31.6,11.2-58.6,33.6-81s49.4-33.6,81-33.6s58.6,11.2,81,33.6c22.4,22.4,33.6,49.4,33.6,81  s-11.2,58.6-33.6,81c-22.4,22.4-49.4,33.6-81,33.6s-58.6-11.2-81-33.6S199.9,259.1,199.9,227.5z M68.9,440.4c0,8.9,3.2,16.5,9.7,23  c6.5,6.5,14.2,9.7,23,9.7c9.2,0,16.9-3.2,23-9.7l87.8-87.5c30.5,21.2,64.6,31.7,102.1,31.7c24.4,0,47.7-4.7,70-14.2  c22.3-9.5,41.5-22.3,57.6-38.4c16.1-16.1,28.9-35.3,38.4-57.6c9.5-22.3,14.2-45.6,14.2-70s-4.7-47.7-14.2-70  c-9.5-22.3-22.3-41.5-38.4-57.6C426,83.8,406.8,71,384.5,61.6c-22.3-9.5-45.6-14.2-70-14.2c-24.4,0-47.7,4.7-70,14.2  S203.1,83.8,187,99.9s-28.9,35.3-38.4,57.6s-14.2,45.6-14.2,70c0,37.5,10.6,71.6,31.7,102.1l-87.8,87.8  C72.1,423.7,68.9,431.4,68.9,440.4z"
];
<canvas id="c"></canvas>

线性/径向梯度是通常用作context.strokeStylecontext.fillStyle值的对象,因此每次将这些context属性中的任何一个重新分配为签名时,先前的梯度(或颜色)都会丢弃。这里需要的是定义梯度的程序化方法,它消除了对过度重复编码的需求。

以下makeGradient函数将值作为参数对象,并将其应用于返回所需的梯度类型。您可能会在项目中发现类似的东西。

var canvas = document.getElementById("canvas"), 
    ctx = canvas.getContext("2d");
function makeGradient(opts) {
    var grad = "linear" === opts.type ? 
        ctx.createLinearGradient(
            opts.dims.startX, opts.dims.startY, 
            opts.dims.endX, opts.dims.endY
        ) : ctx.createRadialGradient(
            opts.dims.startX, opts.dims.startY, 
            opts.dims.startR, opts.dims.endX, 
            opts.dims.endY, opts.dims.endR
        );
    opts.stops.forEach(function(n) {
        grad.addColorStop(n.stop, n.color);
    }); 
    return grad;
}
var options = {
    type: "linear",
    dims: {
        startX: 0,
        startY: 0,
        endX: 100,
        endY: 100
    },
    stops: [ 
        { stop: 0, color: "#F00" }, 
        { stop: .5, color: "#FF0" },
        { stop: 1, color: "#00F" }
    ]
};
ctx.fillStyle = makeGradient( options ), 
ctx.fillRect(0, 0, 100, 100);

现在,在再次应用之前,可以对options对象进行修改/更新。例如...

options.type = "radial";
options.dims = {
    startX: 160,
    startY: 50,
    startR: 2,
    endX: 160,
    endY: 50,
    endR: 50
};
ctx.fillStyle = makeGradient( options )
ctx.fillRect(110, 0, 100, 100);

jsfiddle

我希望这可能有所使用。;)

最新更新