我正在使用线性梯度在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.strokeStyle
或context.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
我希望这可能有所使用。;)