UNPKG

rough-markup

Version:

A markup-style wrapper for `rough.js`.

181 lines (172 loc) 6.4 kB
(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('htm'), require('roughjs')) : typeof define === 'function' && define.amd ? define(['exports', 'htm', 'roughjs'], factory) : (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.RoughMarkup = {}, global.htm, global.rough)); }(this, (function (exports, htm, rough) { 'use strict'; function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; } var htm__default = /*#__PURE__*/_interopDefaultLegacy(htm); var rough__default = /*#__PURE__*/_interopDefaultLegacy(rough); /** * Virtual node structure. * * @alpha */ class VNode { constructor(type, props, children) { this.type = type; this.props = props; this.children = children; } } /** * The SVG tag that renders the given string template to virtual nodes. * * @public */ const svg = htm__default['default'].bind((type, props, ...children) => { return new VNode(type, props, children); }); /** * A map between a type and the drawing function. */ const drawMap = { line: (g, node) => { const { x1, y1, x2, y2 } = node.props; return g.line(toNumber(x1), toNumber(y1), toNumber(x2), toNumber(y2), node.props); }, rect: (g, node) => { const { x, y, width, height } = node.props; return g.rectangle(toNumber(x), toNumber(y), toNumber(width), toNumber(height), node.props); }, ellipse: (g, node) => { const { cx, cy, rx, ry, width, height } = node.props; return g.ellipse(toNumber(cx), toNumber(cy), typeof width !== "undefined" ? toNumber(width) : toNumber(rx) * 2, typeof height !== "undefined" ? toNumber(height) : toNumber(ry) * 2, node.props); }, circle: (g, node) => { const { x, y, r } = node.props; return g.circle(toNumber(x), toNumber(y), toNumber(r) * 2, node.props); }, linearPath: (g, node) => { const points = Array.isArray(node.props.points) ? node.props.points : `${node.props.points}` .split(" ") .map((p) => p.split(",").map(toNumber)); return g.linearPath(points, node.props); }, polygon: (g, node) => { const vertices = Array.isArray(node.props.vertices) ? node.props.vertices : `${node.props.vertices}` .split(" ") .map((p) => p.split(",").map(toNumber)); return g.polygon(vertices, node.props); }, arc: (g, node) => { const { x, y, width, height, start, stop, closed } = node.props; return g.arc(Number(x), Number(y), Number(width), Number(height), Number(start), Number(stop), Boolean(closed), node.props); }, curve: (g, node) => { const points = Array.isArray(node.props.points) ? node.props.points : `${node.props.points}` .split(" ") .map((p) => p.split(",").map(toNumber)); return g.curve(points, node.props); }, path: (g, node) => { return g.path(node.props.d, node.props); }, g: (g, node) => { const subDrawables = renderToDrawables(node.children); const translate = node.props.transform.match(/translate\(\s*(\s*-?\d+)\s*,\s*(-?\d+)\s*\)/); if (translate) { const delta = translate.slice(1); subDrawables.forEach((d) => d.sets.forEach((s) => s.ops.forEach((o) => { o.data = o.data.map((da, i) => da + parseInt(delta[i % 2])); }))); } return subDrawables; } }; /** * Convert numbers or throw error. * * @param raw - the raw type. * @returns number */ function toNumber(raw) { const n = parseInt(raw, 10); if (isNaN(n)) { throw new Error("number is expected but got NaN"); } return n; } /** * Convert the virtual nodes into drawables. * * @param nodes - the virtual nodes. * @returns rough drawables. * @public */ function renderToDrawables(nodes, config) { const g = rough__default['default'].generator(config); return nodes.reduce((drawables, node) => { const draw = drawMap[node.type]; if (draw) { const subDrawables = draw(g, node); if (Array.isArray(subDrawables)) { drawables.push(...subDrawables); } else { drawables.push(subDrawables); } } else { drawables.push(...renderToDrawables(node.children)); } return drawables; }, []); } /** * Render the virtuanl nodes and mounted to given element. * * @param node - the virtual nodes. * @param el - the element (or css selector string) to draw on. * @public */ function render(node, el, config) { const drawables = renderToDrawables(Array.isArray(node) ? node : [node], config?.rough); const element = typeof el === "string" ? document.querySelector(el) : el; if (element instanceof HTMLCanvasElement) { return renderToCanvas(drawables, element, config); } if (element instanceof SVGSVGElement) { return renderToSvg(drawables, element, config); } throw new Error("unexpected element to render"); } function renderToSvg(drawables, el, config) { if (config?.clear) { el.childNodes.forEach((c) => c.remove()); } const rc = rough__default['default'].svg(el, config?.rough); drawables.forEach((drawable) => { el.appendChild(rc.draw(drawable)); }); } function renderToCanvas(drawables, el, config) { if (config?.clear) { el.getContext("2d")?.clearRect(0, 0, el.width, el.height); } const rc = rough__default['default'].canvas(el, config?.rough); drawables.forEach((drawable) => { rc.draw(drawable); }); } exports.VNode = VNode; exports.render = render; exports.renderToDrawables = renderToDrawables; exports.svg = svg; Object.defineProperty(exports, '__esModule', { value: true }); })));