对于 CSS box-shadow,“spread”属性用于设置阴影的大小。但CANVAS的shadow API似乎缺乏对应的属性。
我知道答案可能是否定的。
但是有没有办法在CANVAS上实现阴影“扩散”的效果呢?
如果答案仍然是否定的,那么在下一个即将推出的 HTML 标准中它会是"is"吗?
最佳答案
But is there some way to achieve the shadow "spread" effect on CANVAS?
是的:)
如何?
模仿扩散半径
的一种方法是创建一个更大版本的盒子,我们将其放置在 Canvas 外部,并使用偏移量 X 和偏移量 Y 使其阴影位于 Canvas 内我们想要的地方。之后就只剩下绘制原来的盒子了。
说明:
要创建一个更大的盒子,我们将使用 scale() ,我们需要首先计算比例因子。
我们通过计算所需阴影的大小与框的大小之间的比率来获得比例因子(SF)SF = (boxW + 2 * spreadRadius)/boxW
然后我们将使用 save() 保存 Canvas 的整个状态。方法之前调用scale(),因此我们将能够在操作后恢复到原始状态(使用 restore() 方法)
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
const w = canvas.width;
const h = canvas.height;
const x = 50;
let y = 20;
const spreadRadius = 10;
const boxW = 50;
const boxH = 50;
const color = 'rgba(255, 0, 0, 0.4)';
const blurRadius = 10;
const offsetX = 0;
const offsetY = 0;
ctx.fillRect(x, y, boxW, boxH);
ctx.font = "16px Arial";
ctx.fillText('Origin Box', x + 100, y + 30);
ctx.save();
y += 85;
let scaleFactor = ((2 * spreadRadius) + boxW) / boxW;
ctx.scale(scaleFactor, scaleFactor);
ctx.fillRect((x - spreadRadius) / scaleFactor, (y - spreadRadius ) / scaleFactor, boxW, boxH);
ctx.restore();
ctx.fillText('Scaled Box', x + 100, y + 30);
ctx.save();
y += 100;
scaleFactor = ((2 * spreadRadius) + boxW) / boxW;
ctx.scale(scaleFactor, scaleFactor);
ctx.shadowColor = color;
ctx.shadowBlur = blurRadius;
ctx.shadowOffsetX = offsetX;
ctx.shadowOffsetY = offsetY;
ctx.fillRect((x - spreadRadius) / scaleFactor, (y - spreadRadius) / scaleFactor, boxW, boxH);
ctx.restore();
ctx.fillText('Scaled Box with shadow', x + 100, y + 30);
ctx.save();
y += 100;
scaleFactor = ((2 * spreadRadius) + boxW) / boxW;
ctx.scale(scaleFactor, scaleFactor);
ctx.shadowColor = color;
ctx.shadowBlur = blurRadius;
ctx.shadowOffsetX = offsetX + w;
ctx.shadowOffsetY = offsetY + h;
ctx.fillRect((x - spreadRadius - w) / scaleFactor, (y - spreadRadius - h) / scaleFactor, boxW, boxH);
ctx.restore();
ctx.fillText('Same but we draw the box outside the canvas', x + 100, y + 30);
ctx.fillText('and keep its shadow by playing on the offsets', x + 100, y + 47);
ctx.save();
y += 100;
scaleFactor = ((2 * spreadRadius) + boxW) / boxW;
ctx.scale(scaleFactor, scaleFactor);
ctx.shadowColor = color;
ctx.shadowBlur = blurRadius;
ctx.shadowOffsetX = offsetX + w;
ctx.shadowOffsetY = offsetY + h;
ctx.fillRect((x - spreadRadius - w) / scaleFactor, (y - spreadRadius - h) / scaleFactor, boxW, boxH);
ctx.restore();
ctx.clearRect(x, y, boxW, boxH);
ctx.fillRect(x, y, boxW, boxH);
ctx.fillText('Origin Box + shadow with the spread radius', x + 100, y + 30);
#canvas{
border: 1px solid black;
}
<canvas id="canvas" width="500" height="500"></canvas>
诀窍是在 Canvas 外绘制缩放框。我们通过将 Canvas 的宽度和高度减去缩放框的 x 和 y 位置来实现此目的。通过这样做,我们确定它是在 Canvas 之外绘制的:
ctx.fillRect(x - w, y - h, boxW, boxH); // We substract w to the x position and h to the y position
然后,我们将其添加到阴影偏移量以在 Canvas 内绘制阴影:
ctx.shadowOffsetX = offsetX + w; // We add w which is the canvas width
ctx.shadowOffsetY = offsetY + h; // We add h which is the canvas height
只需绘制原始框即可得到我们想要的结果。
函数
我创建了一个 boxShadow()
函数来收集具体示例中的所有方法。该函数的工作原理如下:
创建一个shadowValues对象:
const newShadowObject = {box: {posX: x, posY: y, width: boxW, height: boxH}, offsetX, offsetY, blurRadius, spreadRadius, color}
以对象作为参数调用
boxShadow()
函数:boxShadow(newShadowObject);
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
const w = canvas.width;
const h = canvas.height;
function boxShadow(shadowValues){
const {box: {posX: x, posY: y, width: boxW, height: boxH}, offsetX, offsetY, blurRadius, spreadRadius, color} = shadowValues;
// We draw the shadow
ctx.save();
const scaleFactor = ((2 * spreadRadius) + boxW) / boxW;
ctx.scale(scaleFactor, scaleFactor);
ctx.shadowColor = color;
ctx.shadowBlur = blurRadius;
ctx.shadowOffsetX = offsetX + w;
ctx.shadowOffsetY = offsetY + h;
ctx.fillRect((x - spreadRadius - w) / scaleFactor, (y - spreadRadius - h) / scaleFactor, boxW, boxH);
ctx.restore();
// We draw the original box
ctx.clearRect(x, y, boxW, boxH);
ctx.rect(x, y, boxW, boxH);
ctx.stroke();
}
const case1 = {box: {posX: 30, posY: 30, width: 50, height: 50}, offsetX: 0, offsetY: 0, blurRadius: 0, spreadRadius: 0, color: '#ff0000'};
boxShadow(case1);
ctx.font = "16px Arial";
ctx.fillText("offsetX: 0", 30, 120);
ctx.fillText("offsetY: 0", 30, 140);
ctx.fillText("blurRadius: 0", 30, 160);
ctx.fillText("spreadRadius: 0", 30, 180);
const case2 = {box: {posX: 170, posY: 30, width: 50, height: 50}, offsetX: 0, offsetY: 0, blurRadius: 0, spreadRadius: 10, color: '#ff0000'};
boxShadow(case2);
ctx.font = "16px Arial";
ctx.fillText("offsetX: 0", 170, 120);
ctx.fillText("offsetY: 0", 170, 140);
ctx.fillText("blurRadius: 0", 170, 160);
ctx.fillText("spreadRadius: 10", 170, 180);
const case3 = {box: {posX: 320, posY: 30, width: 50, height: 50}, offsetX: 5, offsetY: 5, blurRadius: 0, spreadRadius: 10, color: '#ff0000'};
boxShadow(case3);
ctx.font = "16px Arial";
ctx.fillText("offsetX: 5", 320, 120);
ctx.fillText("offsetY: 5", 320, 140);
ctx.fillText("blurRadius: 0", 320, 160);
ctx.fillText("spreadRadius: 10", 320, 180);
const case4 = {box: {posX: 470, posY: 30, width: 50, height: 50}, offsetX: 5, offsetY: 5, blurRadius: 20, spreadRadius: 10, color: '#ff0000'};
boxShadow(case4);
ctx.font = "16px Arial";
ctx.fillText("offsetX: 5", 470, 120);
ctx.fillText("offsetY: 5", 470, 140);
ctx.fillText("blurRadius: 20", 470, 160);
ctx.fillText("spreadRadius: 10", 470, 180);
const case5 = {box: {posX: 55, posY: 225, width: 50, height: 100}, offsetX: 0, offsetY: 0, blurRadius: 20, spreadRadius: 0, color: '#ff0000'};
boxShadow(case5);
ctx.font = "16px Arial";
ctx.fillText("offsetX: 0", 30, 380);
ctx.fillText("offsetY: 0", 30, 400);
ctx.fillText("blurRadius: 20", 30, 420);
ctx.fillText("spreadRadius: 0", 30, 440);
const case6 = {box: {posX: 200, posY: 250, width: 100, height: 50}, offsetX: 10, offsetY: 5, blurRadius: 0, spreadRadius: 0, color: '#ff0000'};
boxShadow(case6);
ctx.font = "16px Arial";
ctx.fillText("offsetX: 10", 200, 380);
ctx.fillText("offsetY: 5", 200, 400);
ctx.fillText("blurRadius: 0", 200, 420);
ctx.fillText("spreadRadius: 0", 200, 440);
const case7 = {box: {posX: 400, posY: 210, width: 50, height: 50}, offsetX: 70, offsetY: 70, blurRadius: 10, spreadRadius: 10, color: '#ff0000'};
boxShadow(case7);
ctx.font = "16px Arial";
ctx.fillText("offsetX: 70", 400, 380);
ctx.fillText("offsetY: 70", 400, 400);
ctx.fillText("blurRadius: 0", 400, 420);
ctx.fillText("spreadRadius: 10", 400, 440);
#canvas{
border: 1px solid black;
background-color: #EFEFFA;
}
<canvas id="canvas" width="620" height="500"></canvas>
关于javascript - 是否有 CANVAS 阴影属性,例如 CSS box-shadow 的 "spread"属性?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60656468/