UNPKG

@nmmty/lazycanvas

Version:

A simple way to interact with @napi-rs/canvas in an advanced way!

448 lines (447 loc) 21.1 kB
"use strict"; 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;