@chemicalluck/canvas-txt
Version:
Render multiline textboxes in HTML5 canvas with auto line breaks and better alignment system
3 lines (2 loc) • 2.75 kB
JavaScript
(function(b,S){typeof exports=="object"&&typeof module<"u"?S(exports):typeof define=="function"&&define.amd?define(["exports"],S):(b=typeof globalThis<"u"?globalThis:b||self,S(b.canvasTxt={}))})(this,function(b){"use strict";function S({ctx:e,line:c,spaceWidth:d,spaceChar:n,width:a}){const i=c.trim(),s=i.split(/\s+/),o=s.length-1;if(o===0)return i;const m=e.measureText(s.join("")).width,p=(a-m)/d,T=Math.floor(p/o);if(p<1)return i;const r=n.repeat(T);return s.join(r)}const P=" ";function B({ctx:e,text:c,justify:d,width:n}){const a=new Map,i=r=>{let g=a.get(r);return g!==void 0||(g=e.measureText(r).width,a.set(r,g)),g};let s=[],o=c.split(`
`);const m=d?i(P):0;let p=0,T=0;for(const r of o){let g=i(r);const A=r.length;if(g<=n){s.push(r);continue}let u=r,t,f,l="";for(;g>n;){if(p++,t=T,f=t===0?0:i(r.substring(0,t)),f<n)for(;f<n&&t<A&&(t++,f=i(u.substring(0,t)),t!==A););else if(f>n)for(;f>n&&(t=Math.max(1,t-1),f=i(u.substring(0,t)),t!==1););if(T=Math.round(T+(t-T)/p),t--,t>0){let h=t;if(u.substring(h,h+1)!=" "){for(;h>=0&&u.substring(h,h+1)!=" ";)h--;h>0&&(t=h)}}t===0&&(t=1),l=u.substring(0,t),l=d?S({ctx:e,line:l,spaceWidth:m,spaceChar:P,width:n}):l,s.push(l),u=u.substring(t),g=i(u)}g>0&&(l=d?S({ctx:e,line:u,spaceWidth:m,spaceChar:P,width:n}):u,s.push(l))}return s}function H({ctx:e,text:c,style:d}){const n=e.textBaseline,a=e.font;e.textBaseline="bottom",e.font=d;const{actualBoundingBoxAscent:i}=e.measureText(c);return e.textBaseline=n,e.font=a,i}const w={debug:!1,align:"center",vAlign:"middle",drawStyle:"fill",fontSize:14,fontWeight:"",fontStyle:"",fontVariant:"",font:"Arial",lineHeight:null,justify:!1};function M(e,c,d){const{width:n,height:a,x:i,y:s}=d,o={...w,...d};if(n<=0||a<=0||o.fontSize<=0)return{height:0};const m=i+n,p=s+a,{fontStyle:T,fontVariant:r,fontWeight:g,fontSize:A,font:u}=o,t=`${T} ${r} ${g} ${A}px ${u}`;e.font=t;let f=s+a/2+o.fontSize/2,l;o.align==="right"?(l=m,e.textAlign="right"):o.align==="left"?(l=i,e.textAlign="left"):(l=i+n/2,e.textAlign="center");const h=B({ctx:e,text:c,justify:o.justify,width:n}),W=o.lineHeight?o.lineHeight:H({ctx:e,text:"M",style:t}),k=W*(h.length-1),j=k/2;let v=s;o.vAlign==="top"?(e.textBaseline="top",f=s):o.vAlign==="bottom"?(e.textBaseline="bottom",f=p-k,v=p):(e.textBaseline="bottom",v=s+a/2,f-=j);const C=o.drawStyle==="fill"?e.fillText.bind(e):e.strokeText.bind(e);if(h.forEach(y=>{y=y.trim(),C(y,l,f),f+=W}),o.debug){const y="#0C8CE9";e.lineWidth=1,e.strokeStyle=y,e.strokeRect(i,s,n,a),e.lineWidth=1,e.strokeStyle=y,e.beginPath(),e.moveTo(l,s),e.lineTo(l,p),e.stroke(),e.strokeStyle=y,e.beginPath(),e.moveTo(i,v),e.lineTo(m,v),e.stroke()}return{height:k+W}}b.drawText=M,b.getTextHeight=H,b.splitText=B,Object.defineProperty(b,Symbol.toStringTag,{value:"Module"})});