@lightningjs/renderer
Version:
Lightning 3 Renderer
161 lines (148 loc) • 4.22 kB
text/typescript
/*
* Copyright 2023 Comcast Cable Communications Management, LLC
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
import type { Vec4 } from '../../../renderers/webgl/internal/ShaderUtils.js';
export function roundRect(
ctx: CanvasRenderingContext2D | Path2D,
x: number,
y: number,
width: number,
height: number,
radius: Vec4,
) {
if (ctx.roundRect !== undefined) {
ctx.roundRect(x, y, width, height, radius);
return;
}
const { 0: tl, 1: tr, 2: br, 3: bl } = radius;
ctx.moveTo(x + tl, y);
ctx.lineTo(x + width - tr, y);
ctx.ellipse(x + width - tr, y + tr, tr, tr, 0, 1.5 * Math.PI, 2 * Math.PI);
ctx.lineTo(x + width, y - height - br);
ctx.ellipse(x + width - br, y + height - br, br, br, 0, 0, 0.5 * Math.PI);
ctx.lineTo(x + bl, y + height);
ctx.ellipse(x + bl, y + height - bl, bl, bl, 0, 0.5 * Math.PI, Math.PI);
ctx.lineTo(x, y + tl);
ctx.ellipse(x + tl, y + tl, tl, tl, 0, Math.PI, 1.5 * Math.PI);
}
export function roundedRectWithBorder(
ctx: CanvasRenderingContext2D,
x: number,
y: number,
width: number,
height: number,
radius: Vec4,
borderGap: number,
outerX: number,
outerY: number,
outerW: number,
outerH: number,
outerBorderRadius: Vec4,
innerX: number,
innerY: number,
innerW: number,
innerH: number,
innerBorderRadius: Vec4,
borderColor: string,
renderContext: () => void,
) {
outerX += x;
outerY += y;
outerW += width;
outerH += height;
innerX += x;
innerY += y;
innerW += width;
innerH += height;
// no gap render strategy - to avoid artifacts between border and node
if (borderGap === 0) {
//draw outer border rounded rect
ctx.beginPath();
roundRect(ctx, outerX, outerY, outerW, outerH, outerBorderRadius);
ctx.fillStyle = borderColor;
ctx.fill();
ctx.closePath();
const path = new Path2D();
roundRect(path, innerX, innerY, innerW, innerH, innerBorderRadius as Vec4);
ctx.clip(path);
renderContext();
return;
}
// with gap render strategy
//draw node content first
ctx.save();
const path = new Path2D();
roundRect(path, x, y, width, height, radius);
ctx.clip(path);
renderContext();
ctx.restore();
//draw border by clipping inner area from outer area
ctx.save();
const borderPath = new Path2D();
roundRect(borderPath, outerX, outerY, outerW, outerH, outerBorderRadius);
roundRect(borderPath, innerX, innerY, innerW, innerH, innerBorderRadius);
ctx.fillStyle = borderColor;
ctx.fill(borderPath, 'evenodd');
ctx.restore();
}
export function shadow(
ctx: CanvasRenderingContext2D,
x: number,
y: number,
width: number,
height: number,
color: string,
projection: Vec4,
radius: Vec4,
pixelRatio: number,
) {
ctx.save();
const cw = ctx.canvas.width;
const ch = ctx.canvas.height;
const scaleFactor = (2 * projection[3] + width) / width;
ctx.scale(scaleFactor, scaleFactor);
ctx.shadowColor = color;
ctx.shadowBlur = projection[2] * pixelRatio;
ctx.shadowOffsetX = projection[0] + cw * pixelRatio - projection[3] * 0.5;
ctx.shadowOffsetY = projection[1] + ch * pixelRatio - projection[3] * 0.5;
const spreadFactor = projection[3];
ctx.beginPath();
roundRect(
ctx,
(x - spreadFactor - cw) / scaleFactor,
(y - spreadFactor - ch) / scaleFactor,
width + spreadFactor,
height + spreadFactor,
radius,
);
ctx.fillStyle = color;
ctx.fill();
ctx.restore();
}
export function strokeLine(
ctx: CanvasRenderingContext2D,
x: number,
y: number,
x2: number,
y2: number,
lineWidth: number,
) {
ctx.beginPath();
ctx.lineWidth = lineWidth;
ctx.moveTo(x, y);
ctx.lineTo(x2, y2);
ctx.stroke();
}