帆布去除线性渐变 [英] Canvas remove linear gradient

查看:47
本文介绍了帆布去除线性渐变的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用线性渐变来绘制和重新绘制HTML画布上下文中的数百个(随着时间的推移成千上万个)图标形状,每个图标形状都有自己独特的线性渐变.

I am using linear gradients to plot and re-plot hundreds (over time thousands) of icon shapes on a HTML canvas context each with its own unique linear gradient.

创建后是否可以重新使用HTML canvas上下文线性渐变?

Is it possible to re-use a HTML canvas context linear gradient once created?

如果是的话,如何更改x1,y1,x2,y2和终止色?

If so how do you change the x1,y1,x2,y2 and the stop colours?

还是可以从上下文中删除线性渐变?

Or can you remove the linear gradient from the context?

或者如果以上都不是,则是否有其他任何画布功能,例如clearRect从上下文中删除其边界内的所有线性渐变吗?

Or if neither of the above, does any other canvas function e.g. clearRect delete all linear gradients from the context within its boundaries?

或者如果这些都不是,那么在具有线性渐变子级的上下文中,成千上万个(数十万个)上下文在长时间内的性能损失是多少?

Or if none of these, what is the performance penalty over a long period of time with thousands (hundreds of thousands?) of a context with linear gradient children?

推荐答案

是的,创建后可以重用CanvasGradient,

Yes you can reuse a CanvasGradient once created,

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>

但是,不能,您不能在创建时修改其内部属性集.您唯一可以做的就是添加新的色标.

But no, you can't modify its inner properties set at creation. The only thing you can do with it is to add new color stops.

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

What can be done though is to change the transformation matrix of your context, this will also influence your gradients:

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>

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

But this implies that all your other drawings will also follow this transformation...

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

So one way to move only the gradient is by using compositing, by first drawing your gradient across the whole canvas, and then draw only your shape, with the globalCompositionOperation set to '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>

因此,人们可能会编写一些辅助函数来方便地处理转换,但是我不确定与重新声明新渐变相比,这样做是否值得.

So one could probably write some helper function that would handle the transformations in an handy way, but I am not sure if the benefits compared to redeclaring new gradients are worth it.

现在,我不清楚您是否真的需要编辑渐变,如果后者是您可能要考虑的一个选项,那就是在屏幕外创建图标的Sprite-sheet画布,然后在需要此图标时只需调用 drawImage :

Now, I am not clear as if you really need to edit your gradient or not, and if the latter, one option that you might want to consider, is to create a sprite-sheet of your icons on an off-screen canvas, and then simply call drawImage whenever you need this icon:

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>

这篇关于帆布去除线性渐变的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆