@nmmty/lazycanvas
Version:
A simple way to interact with @napi-rs/canvas in an advanced way!
448 lines (447 loc) • 21.1 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.resizeLayers = exports.resize = exports.getBoundingBoxBezier = exports.centring = exports.isImageUrlValid = exports.generateRandomName = exports.transform = exports.parseFillStyle = exports.filters = exports.opacity = exports.drawShadow = exports.parser = exports.parseToNormal = exports.isColor = exports.generateID = void 0;
const types_1 = require("../types");
const helpers_1 = require("../structures/helpers");
const canvas_1 = require("@napi-rs/canvas");
const LazyUtil_1 = require("./LazyUtil");
const components_1 = require("../structures/components");
function generateID(type) {
return `${type}-${Math.random().toString(36).substr(2, 9)}`;
}
exports.generateID = generateID;
let percentReg = /^(\d+)%$/;
let pxReg = /^(\d+)px$/;
let canvasReg = /^(vw|vh|vmin|vmax)$/;
let linkReg = /^(link-w|link-h|link-x|link-y)-([A-Za-z0-9_]+)-(\d+)$/;
let hexReg = /^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/;
let rgbReg = /^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/;
let rgbaReg = /^rgba\((\d+),\s*(\d+),\s*(\d+),\s*(0|0?\.\d+|1(\.0)?)\)$/;
let hslReg = /^hsl\((\d+),\s*(\d+)%,\s*(\d+)%\)$/;
let hslaReg = /^hsla\((\d+),\s*(\d+)%,\s*(\d+)%,\s*(0|0?\.\d+|1(\.0)?)\)$/;
function isColor(v) {
return typeof (v === 'string' && (hexReg.test(v) || rgbReg.test(v) || rgbaReg.test(v) || hslReg.test(v) || hslaReg.test(v))) || v instanceof helpers_1.Gradient || v instanceof helpers_1.Pattern;
}
exports.isColor = isColor;
function parseToNormal(v, ctx, canvas, layer = { width: 0, height: 0 }, options = { vertical: false, layer: false }, manager) {
if (typeof v === 'number') {
return v;
}
else if (typeof v === 'string') {
if (percentReg.test(v)) {
console.log(options, layer, canvas.width, canvas.height, parseFloat(v));
return (parseFloat(v) / 100) * (options.layer ? (options.vertical ? layer.height : layer.width) : (options.vertical ? canvas.height : canvas.width));
}
else if (pxReg.test(v)) {
return parseFloat(v);
}
else if (canvasReg.test(v)) {
if (v === 'vw') {
return (options.layer ? layer.width : canvas.width);
}
else if (v === 'vh') {
return (options.layer ? layer.height : canvas.height);
}
else if (v === 'vmin') {
return (options.layer ? Math.max(layer.width, layer.height) : Math.min(canvas.width, canvas.height));
}
else if (v === 'vmax') {
return (options.layer ? Math.max(layer.width, layer.height) : Math.max(canvas.width, canvas.height));
}
}
else if (linkReg.test(v)) {
let match = v.match(linkReg);
if (!manager)
return 0;
let anyLayer = manager.get(match[2], true);
if (!anyLayer || anyLayer instanceof components_1.Group || anyLayer instanceof components_1.Path2DLayer)
return 0;
const parcer = parser(ctx, canvas, manager);
switch (match[1]) {
case 'link-w':
if (anyLayer instanceof components_1.LineLayer || anyLayer instanceof components_1.BezierLayer || anyLayer instanceof components_1.QuadraticLayer) {
return anyLayer.getBoundingBox(ctx, canvas, manager).width + (parseInt(match[3]) || 0);
}
else if (anyLayer instanceof components_1.TextLayer) {
return anyLayer.measureText(ctx, canvas).width + (parseInt(match[3]) || 0);
}
else {
return (parcer.parse(anyLayer.props.size.width) || 0) + (parseInt(match[3]) || 0);
}
case 'link-h':
if (anyLayer instanceof components_1.LineLayer || anyLayer instanceof components_1.BezierLayer || anyLayer instanceof components_1.QuadraticLayer) {
return anyLayer.getBoundingBox(ctx, canvas, manager).height + (parseInt(match[3]) || 0);
}
else if (anyLayer instanceof components_1.TextLayer) {
return anyLayer.measureText(ctx, canvas).height + (parseInt(match[3]) || 0);
}
else {
return (parcer.parse(anyLayer.props.size.height, LazyUtil_1.defaultArg.wh(parcer.parse(anyLayer.props.size.width)), LazyUtil_1.defaultArg.vl(true)) || 0) + (parseInt(match[3]) || 0);
}
case 'link-x':
return (parcer.parse(anyLayer.props.x) || 0) + (parseInt(match[3]) || 0);
case 'link-y':
return (parcer.parse(anyLayer.props.y, LazyUtil_1.defaultArg.wh(), LazyUtil_1.defaultArg.vl(true)) || 0) + (parseInt(match[3]) || 0);
}
}
}
else if (v instanceof helpers_1.Link) {
if (!manager)
return 0;
let anyLayer = manager.get(v.source, true);
if (!anyLayer || anyLayer instanceof components_1.Group || anyLayer instanceof components_1.Path2DLayer)
return 0;
const parcer = parser(ctx, canvas, manager);
switch (v.type) {
case types_1.LinkType.Width:
case 'width':
if (anyLayer instanceof components_1.LineLayer || anyLayer instanceof components_1.BezierLayer || anyLayer instanceof components_1.QuadraticLayer) {
return anyLayer.getBoundingBox(ctx, canvas, manager).width + (parcer.parse(v.additionalSpacing, LazyUtil_1.defaultArg.wh(layer.width, layer.height), LazyUtil_1.defaultArg.vl(options.vertical, options.layer)) || 0);
}
else if (anyLayer instanceof components_1.TextLayer) {
return anyLayer.measureText(ctx, canvas).width + (parcer.parse(v.additionalSpacing, LazyUtil_1.defaultArg.wh(layer.width, layer.height), LazyUtil_1.defaultArg.vl(options.vertical, options.layer)) || 0);
}
else {
return (parcer.parse(anyLayer.props.size.width) || 0) + (parcer.parse(v.additionalSpacing, LazyUtil_1.defaultArg.wh(layer.width, layer.height), LazyUtil_1.defaultArg.vl(options.vertical, options.layer)) || 0);
}
case types_1.LinkType.Height:
case 'height':
if (anyLayer instanceof components_1.LineLayer || anyLayer instanceof components_1.BezierLayer || anyLayer instanceof components_1.QuadraticLayer) {
return anyLayer.getBoundingBox(ctx, canvas, manager).height + (parcer.parse(v.additionalSpacing, LazyUtil_1.defaultArg.wh(layer.width, layer.height), LazyUtil_1.defaultArg.vl(options.vertical, options.layer)) || 0);
}
else if (anyLayer instanceof components_1.TextLayer) {
return anyLayer.measureText(ctx, canvas).height + (parcer.parse(v.additionalSpacing, LazyUtil_1.defaultArg.wh(layer.width, layer.height), LazyUtil_1.defaultArg.vl(options.vertical, options.layer)) || 0);
}
else {
return (parcer.parse(anyLayer.props.size.height, LazyUtil_1.defaultArg.wh(parcer.parse(anyLayer.props.size.width)), LazyUtil_1.defaultArg.vl(true)) || 0) + (parcer.parse(v.additionalSpacing, LazyUtil_1.defaultArg.wh(layer.width, layer.height), LazyUtil_1.defaultArg.vl(options.vertical, options.layer)) || 0);
}
case types_1.LinkType.X:
case 'x':
return (parcer.parse(anyLayer.props.x) || 0) + (parcer.parse(v.additionalSpacing, LazyUtil_1.defaultArg.wh(layer.width, layer.height), LazyUtil_1.defaultArg.vl(options.vertical, options.layer)) || 0);
case types_1.LinkType.Y:
case 'y':
return (parcer.parse(anyLayer.props.y) || 0) + (parcer.parse(v.additionalSpacing, LazyUtil_1.defaultArg.wh(layer.width, layer.height), LazyUtil_1.defaultArg.vl(options.vertical, options.layer)) || 0);
default:
return 0;
}
}
else {
return 0;
}
return 0;
}
exports.parseToNormal = parseToNormal;
function parser(ctx, canvas, manager) {
return {
parse(v, layer = LazyUtil_1.defaultArg.wh(), options = LazyUtil_1.defaultArg.vl()) {
return parseToNormal(v, ctx, canvas, layer, options, manager);
},
parseBatch(values) {
const result = {};
for (const key in values) {
const { v, layer, options } = values[key];
result[key] = parseToNormal(v, ctx, canvas, layer ?? LazyUtil_1.defaultArg.wh(), options ?? LazyUtil_1.defaultArg.vl(), manager);
}
return result;
}
};
}
exports.parser = parser;
function drawShadow(ctx, shadow) {
if (shadow) {
ctx.shadowColor = shadow.color;
ctx.shadowBlur = shadow.blur || 0;
ctx.shadowOffsetX = shadow.offsetX || 0;
ctx.shadowOffsetY = shadow.offsetY || 0;
}
}
exports.drawShadow = drawShadow;
function opacity(ctx, opacity) {
if (opacity < 1) {
ctx.globalAlpha = opacity;
}
}
exports.opacity = opacity;
function filters(ctx, filters) {
if (filters) {
ctx.filter = filters;
}
}
exports.filters = filters;
function parseFillStyle(ctx, color) {
if (!ctx)
throw new LazyUtil_1.LazyError('The context is not defined');
if (!color)
throw new LazyUtil_1.LazyError('The color is not defined');
if (color instanceof helpers_1.Gradient || color instanceof helpers_1.Pattern) {
return color.draw(ctx);
}
return color;
}
exports.parseFillStyle = parseFillStyle;
function transform(ctx, transform, layer = { width: 0, height: 0, x: 0, y: 0, type: types_1.LayerType.Morph }, extra = { text: '', textAlign: types_1.TextAlign.Left, fontSize: 0, multiline: false }) {
if (transform) {
if (transform.translate) {
ctx.translate(transform.translate.x, transform.translate.y);
}
if (transform.rotate) {
switch (layer.type) {
case types_1.LayerType.Image:
case types_1.LayerType.Morph:
case types_1.LayerType.BezierCurve:
case types_1.LayerType.QuadraticCurve:
case types_1.LayerType.Line:
ctx.translate(layer.x + (layer.width / 2), layer.y + (layer.height / 2));
ctx.rotate((Math.PI / 180) * transform.rotate);
ctx.translate(-(layer.x + (layer.width / 2)), -(layer.y + (layer.height / 2)));
break;
case types_1.LayerType.Text:
if (extra.multiline) {
ctx.translate(layer.x + (layer.width / 2), layer.y + (layer.height / 2));
ctx.rotate((Math.PI / 180) * transform.rotate);
ctx.translate(-(layer.x + (layer.width / 2)), -(layer.y + (layer.height / 2)));
}
else {
if (extra.textAlign === types_1.TextAlign.Center) {
ctx.translate(layer.x, layer.y);
ctx.rotate((Math.PI / 180) * transform.rotate);
ctx.translate(-layer.x, -layer.y);
}
else if (extra.textAlign === types_1.TextAlign.Left || extra.textAlign === types_1.TextAlign.Start) {
ctx.translate(layer.x + (extra.fontSize * extra.text.length) / 2, layer.y);
ctx.rotate((Math.PI / 180) * transform.rotate);
ctx.translate(-(layer.x + (extra.fontSize * extra.text.length) / 2), -layer.y);
}
else if (extra.textAlign === types_1.TextAlign.Right || extra.textAlign === types_1.TextAlign.End) {
ctx.translate(layer.x - (extra.fontSize * extra.text.length) / 2, layer.y);
ctx.rotate((Math.PI / 180) * transform.rotate);
ctx.translate(-(layer.x - (extra.fontSize * extra.text.length) / 2), -layer.y);
}
}
break;
}
}
if (transform.scale) {
ctx.scale(transform.scale.x, transform.scale.y);
}
if (transform.matrix) {
if (!transform.matrix.a || !transform.matrix.b || !transform.matrix.c || !transform.matrix.d || !transform.matrix.e || !transform.matrix.f)
throw new LazyUtil_1.LazyError('The matrix transformation must be a valid matrix');
ctx.transform(transform.matrix.a, transform.matrix.b, transform.matrix.c, transform.matrix.d, transform.matrix.e, transform.matrix.f);
}
}
}
exports.transform = transform;
function generateRandomName() {
return Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15);
}
exports.generateRandomName = generateRandomName;
function isImageUrlValid(src) {
try {
(0, canvas_1.loadImage)(src);
return true;
}
catch (error) {
return false;
}
}
exports.isImageUrlValid = isImageUrlValid;
function centring(centring, type, width, height, x, y) {
switch (centring) {
case types_1.Centring.Center:
case "center":
switch (type) {
case types_1.LayerType.Image:
case types_1.LayerType.Morph:
case types_1.LayerType.Clear:
x -= width / 2;
y -= height / 2;
break;
}
return { x, y };
case types_1.Centring.CenterBottom:
case "center-bottom":
switch (type) {
case types_1.LayerType.Image:
case types_1.LayerType.Morph:
case types_1.LayerType.Clear:
x -= width / 2;
break;
}
return { x, y };
case types_1.Centring.CenterTop:
case "center-top":
switch (type) {
case types_1.LayerType.Image:
case types_1.LayerType.Morph:
case types_1.LayerType.Clear:
x -= width / 2;
y -= height;
break;
}
return { x, y };
case types_1.Centring.Start:
case "start":
switch (type) {
case types_1.LayerType.Image:
case types_1.LayerType.Morph:
case types_1.LayerType.Clear:
y -= height / 2;
break;
}
return { x, y };
case types_1.Centring.StartBottom:
case "start-bottom":
return { x, y };
case types_1.Centring.StartTop:
case "start-top":
switch (type) {
case types_1.LayerType.Image:
case types_1.LayerType.Morph:
case types_1.LayerType.Clear:
y -= height;
break;
}
return { x, y };
case types_1.Centring.End:
case "end":
switch (type) {
case types_1.LayerType.Image:
case types_1.LayerType.Morph:
case types_1.LayerType.Clear:
x -= width;
y -= height / 2;
break;
}
return { x, y };
case types_1.Centring.EndBottom:
case "end-bottom":
switch (type) {
case types_1.LayerType.Image:
case types_1.LayerType.Morph:
case types_1.LayerType.Clear:
x -= width;
break;
}
return { x, y };
case types_1.Centring.EndTop:
case "end-top":
switch (type) {
case types_1.LayerType.Image:
case types_1.LayerType.Morph:
case types_1.LayerType.Clear:
x -= width;
y -= height;
break;
}
return { x, y };
case types_1.Centring.None:
case "none":
return { x, y };
}
}
exports.centring = centring;
function quadraticBezier(p0, p1, p2, t) {
const x = (1 - t) ** 2 * p0.x + 2 * (1 - t) * t * p1.x + t ** 2 * p2.x;
const y = (1 - t) ** 2 * p0.y + 2 * (1 - t) * t * p1.y + t ** 2 * p2.y;
return { x, y };
}
function cubicBezier(p0, p1, p2, p3, t) {
const x = (1 - t) ** 3 * p0.x + 3 * (1 - t) ** 2 * t * p1.x + 3 * (1 - t) * t ** 2 * p2.x + t ** 3 * p3.x;
const y = (1 - t) ** 3 * p0.y + 3 * (1 - t) ** 2 * t * p1.y + 3 * (1 - t) * t ** 2 * p2.y + t ** 3 * p3.y;
return { x, y };
}
function getBoundingBoxBezier(points, steps = 100) {
let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
for (let i = 0; i <= steps; i++) {
const t = i / steps;
let p;
if (points.length === 3) {
p = quadraticBezier(points[0], points[1], points[2], t);
}
else if (points.length === 4) {
p = cubicBezier(points[0], points[1], points[2], points[3], t);
}
else {
throw new LazyUtil_1.LazyError("Invalid number of points");
}
minX = Math.min(minX, p.x);
minY = Math.min(minY, p.y);
maxX = Math.max(maxX, p.x);
maxY = Math.max(maxY, p.y);
}
return { min: { x: minX, y: minY }, max: { x: maxX, y: maxY }, center: { x: (minX + maxX) / 2, y: (minY + maxY) / 2 }, width: maxX - minX, height: maxY - minY };
}
exports.getBoundingBoxBezier = getBoundingBoxBezier;
function resize(value, ratio) {
if (typeof value === 'number') {
return value * ratio;
}
else if (typeof value === 'string') {
if (pxReg.test(value)) {
return parseFloat(value) * ratio;
}
else if (linkReg.test(value)) {
let match = value.match(linkReg);
return `${match[1]}-${match[2]}-${parseFloat(match[3]) * ratio}`;
}
}
else if (value instanceof helpers_1.Link) {
return `${value.type}-${value.source}-${resize(value.additionalSpacing, ratio)}`;
}
return value;
}
exports.resize = resize;
function resizeLayers(layers, ratio) {
let newLayers = [];
if (layers.length > 0) {
for (const layer of layers) {
if (!(layer instanceof components_1.Group || layer instanceof components_1.Path2DLayer)) {
layer.props.x = resize(layer.props.x, ratio);
layer.props.y = resize(layer.props.y, ratio);
if ('size' in layer.props) {
layer.props.size.width = resize(layer.props.size.width, ratio);
layer.props.size.height = resize(layer.props.size.height, ratio);
if ('radius' in layer.props.size) {
for (const corner in layer.props.size.radius) {
// @ts-ignore
layer.props.size.radius[corner] = resize(layer.props.size.radius[corner], ratio);
}
}
}
if ('stroke' in layer.props) {
layer.props.stroke.width = resize(layer.props.stroke.width, ratio);
}
if ('endPoint' in layer.props) {
layer.props.endPoint.x = resize(layer.props.endPoint.x, ratio);
layer.props.endPoint.y = resize(layer.props.endPoint.y, ratio);
}
if ('controlPoints' in layer.props) {
for (const point of layer.props.controlPoints) {
point.x = resize(point.x, ratio);
point.y = resize(point.y, ratio);
}
}
if ('font' in layer.props) {
layer.props.font.size = resize(layer.props.font.size, ratio);
}
if ('transform' in layer.props) {
if (layer.props.transform.translate) {
layer.props.transform.translate.x = resize(layer.props.transform.translate.x, ratio);
layer.props.transform.translate.y = resize(layer.props.transform.translate.y, ratio);
}
if (layer.props.transform.scale) {
layer.props.transform.scale.x = resize(layer.props.transform.scale.x, ratio);
layer.props.transform.scale.y = resize(layer.props.transform.scale.y, ratio);
}
}
}
else if (layer instanceof components_1.Group) {
layer.layers = resizeLayers(layer.layers, ratio);
}
newLayers.push(layer);
}
}
return newLayers;
}
exports.resizeLayers = resizeLayers;