UNPKG

canvas-txt

Version:

Render multiline textboxes in HTML5 canvas with auto line breaks and better alignment system

125 lines (124 loc) 3.5 kB
function B({ ctx: e, line: c, spaceWidth: p, spaceChar: n, width: a }) { const i = c.trim(), o = i.split(/\s+/), s = o.length - 1; if (s === 0) return i; const m = e.measureText(o.join("")).width, d = (a - m) / p, b = Math.floor(d / s); if (d < 1) return i; const r = n.repeat(b); return o.join(r); } const W = " "; function k({ ctx: e, text: c, justify: p, width: n }) { const a = /* @__PURE__ */ new Map(), i = (r) => { let g = a.get(r); return g !== void 0 || (g = e.measureText(r).width, a.set(r, g)), g; }; let o = [], s = c.split(` `); const m = p ? i(W) : 0; let d = 0, b = 0; for (const r of s) { let g = i(r); const y = r.length; if (g <= n) { o.push(r); continue; } let h = r, t, f, l = ""; for (; g > n; ) { if (d++, t = b, f = t === 0 ? 0 : i(r.substring(0, t)), f < n) for (; f < n && t < y && (t++, f = i(h.substring(0, t)), t !== y); ) ; else if (f > n) for (; f > n && (t = Math.max(1, t - 1), f = i(h.substring(0, t)), !(t === 0 || t === 1)); ) ; if (b = Math.round( b + (t - b) / d ), t--, t > 0) { let u = t; if (h.substring(u, u + 1) != " ") { for (; h.substring(u, u + 1) != " " && u >= 0; ) u--; u > 0 && (t = u); } } t === 0 && (t = 1), l = h.substring(0, t), l = p ? B({ ctx: e, line: l, spaceWidth: m, spaceChar: W, width: n }) : l, o.push(l), h = h.substring(t), g = i(h); } g > 0 && (l = p ? B({ ctx: e, line: h, spaceWidth: m, spaceChar: W, width: n }) : h, o.push(l)); } return o; } function H({ ctx: e, text: c, style: p }) { const n = e.textBaseline, a = e.font; e.textBaseline = "bottom", e.font = p; const { actualBoundingBoxAscent: i } = e.measureText(c); return e.textBaseline = n, e.font = a, i; } const C = { debug: !1, align: "center", vAlign: "middle", fontSize: 14, fontWeight: "", fontStyle: "", fontVariant: "", font: "Arial", lineHeight: null, justify: !1 }; function E(e, c, p) { const { width: n, height: a, x: i, y: o } = p, s = { ...C, ...p }; if (n <= 0 || a <= 0 || s.fontSize <= 0) return { height: 0 }; const m = i + n, d = o + a, { fontStyle: b, fontVariant: r, fontWeight: g, fontSize: y, font: h } = s, t = `${b} ${r} ${g} ${y}px ${h}`; e.font = t; let f = o + a / 2 + s.fontSize / 2, l; s.align === "right" ? (l = m, e.textAlign = "right") : s.align === "left" ? (l = i, e.textAlign = "left") : (l = i + n / 2, e.textAlign = "center"); const u = k({ ctx: e, text: c, justify: s.justify, width: n }), S = s.lineHeight ? s.lineHeight : H({ ctx: e, text: "M", style: t }), v = S * (u.length - 1), P = v / 2; let A = o; if (s.vAlign === "top" ? (e.textBaseline = "top", f = o) : s.vAlign === "bottom" ? (e.textBaseline = "bottom", f = d - v, A = d) : (e.textBaseline = "bottom", A = o + a / 2, f -= P), u.forEach((T) => { T = T.trim(), e.fillText(T, l, f), f += S; }), s.debug) { const T = "#0C8CE9"; e.lineWidth = 1, e.strokeStyle = T, e.strokeRect(i, o, n, a), e.lineWidth = 1, e.strokeStyle = T, e.beginPath(), e.moveTo(l, o), e.lineTo(l, d), e.stroke(), e.strokeStyle = T, e.beginPath(), e.moveTo(i, A), e.lineTo(m, A), e.stroke(); } return { height: v + S }; } export { E as drawText, H as getTextHeight, k as splitText };