modern-text 0.0.4 → 0.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -5
- package/dist/index.cjs +1 -1
- package/dist/index.js +1 -1
- package/dist/index.mjs +194 -156
- package/package.json +1 -1
- package/types/Text.d.ts +26 -30
package/README.md
CHANGED
|
@@ -30,16 +30,16 @@ const text = new Text({
|
|
|
30
30
|
fontSize: 22,
|
|
31
31
|
textDecoration: 'underline',
|
|
32
32
|
},
|
|
33
|
-
|
|
33
|
+
content: [
|
|
34
34
|
{
|
|
35
35
|
letterSpacing: 3,
|
|
36
36
|
fragments: [
|
|
37
|
-
{
|
|
38
|
-
{
|
|
37
|
+
{ content: 'He', color: 'red', fontSize: 12 },
|
|
38
|
+
{ content: 'llo', color: 'black' },
|
|
39
39
|
],
|
|
40
40
|
},
|
|
41
|
-
{
|
|
42
|
-
{
|
|
41
|
+
{ content: ', ', color: 'grey' },
|
|
42
|
+
{ content: 'World!', color: 'black' },
|
|
43
43
|
],
|
|
44
44
|
})
|
|
45
45
|
|
package/dist/index.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});class
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});class u{static get defaultStyle(){return{width:"auto",height:"auto",color:"#000000",fontSize:14,fontWeight:"normal",fontFamily:"sans-serif",fontStyle:"normal",fontKerning:"normal",textWrap:"wrap",textAlign:"start",verticalAlign:"baseline",textDecoration:null,textStrokeWidth:0,textStrokeColor:"#000000",direction:"inherit",lineHeight:1,letterSpacing:0,shadowColor:"#000000",shadowOffsetX:0,shadowOffsetY:0,shadowBlur:0}}constructor(i={}){const{view:t=document.createElement("canvas"),pixelRatio:l=window.devicePixelRatio||1,content:o="",style:h}=i;this.view=t,this.context=t.getContext("2d"),this.pixelRatio=l,this.content=o,this.style={...u.defaultStyle,...h},this.update()}measure(i=0,t=0){let l=this._createParagraphs(this.content);l=this._createWrapedParagraphs(l,i);const o=this.context;let h=0;for(let c=l.length,x=0;x<c;x++){const e=l[x];e.contentBox.top=h;let s=0,d=null;for(const n of e.fragments){this._setContextStyle({...n.style,textAlign:"left",verticalAlign:"baseline"});const r=o.measureText(n.content);n.inlineBox.left=s,n.inlineBox.top=h,n.inlineBox.width=r.width,n.inlineBox.height=n.style.fontSize*n.style.lineHeight,n.contentBox.left=n.inlineBox.left,n.contentBox.width=n.inlineBox.width,n.contentBox.height=n.style.fontSize,n.contentBox.top=n.inlineBox.top+(n.inlineBox.height-n.contentBox.height)/2,n.textBox.width=r.actualBoundingBoxLeft+r.actualBoundingBoxRight,n.textBox.height=r.actualBoundingBoxAscent+r.actualBoundingBoxDescent,n.textBox.left=n.contentBox.left+(n.contentBox.width-n.textBox.width)/2,n.textBox.top=n.contentBox.top+(n.contentBox.height-n.textBox.height)/2,n.baseline=n.textBox.top+r.actualBoundingBoxAscent,s+=n.contentBox.width+n.style.letterSpacing,e.contentBox.height<n.contentBox.height&&(d=n),e.contentBox.left=Math.min(e.contentBox.left,n.contentBox.left),e.contentBox.top=Math.min(e.contentBox.top,n.contentBox.top),e.contentBox.height=Math.max(e.contentBox.height,n.contentBox.height),e.lineBox.height=Math.max(e.lineBox.height,n.inlineBox.height)}const B=e.fragments[e.fragments.length-1];e.contentBox.width=Math.max(e.contentBox.width,B?B.contentBox.left+Math.max(B.contentBox.width,B.textBox.width):0),e.lineBox.left=0,e.lineBox.top=h,e.lineBox.width=Math.max(i,e.contentBox.width),this._setContextStyle({...(d??e).style,textAlign:"left",verticalAlign:"baseline"});const f=o.measureText("X");e.baseline=e.lineBox.top+(e.lineBox.height-f.actualBoundingBoxAscent+f.actualBoundingBoxDescent)/2+f.actualBoundingBoxAscent,h+=e.lineBox.height}const a=l.reduce((c,x)=>(c.left=Math.min(c.left,x.lineBox.left),c.top=Math.min(c.top,x.lineBox.top),c.width=Math.max(c.width,x.lineBox.width),c.height+=x.lineBox.height,c),{left:0,top:0,width:0,height:0});for(let c=l.length,x=0;x<c;x++){const e=l[x];e.fragments.forEach(s=>{const d=s.inlineBox.left,B=s.inlineBox.top,f=e.lineBox.height-s.inlineBox.height;switch(s.style.textAlign){case"end":case"right":s.inlineBox.left+=e.lineBox.width-e.contentBox.width;break;case"center":s.inlineBox.left+=(e.lineBox.width-e.contentBox.width)/2;break;case"start":case"left":default:s.inlineBox.left+=e.lineBox.left;break}switch(s.style.verticalAlign){case"top":s.inlineBox.top=e.lineBox.top;break;case"middle":s.inlineBox.top=e.lineBox.top+f/2;break;case"bottom":s.inlineBox.top=e.lineBox.top+f;break;case"sub":case"text-top":case"text-bottom":break;case"baseline":default:s.inlineBox.height<e.lineBox.height&&(s.inlineBox.top+=e.baseline-s.baseline);break}const n=s.inlineBox.left-d,r=s.inlineBox.top-B;s.contentBox.left+=n,s.contentBox.top+=r,s.textBox.left+=n,s.textBox.top+=r})}return{...a,paragraphs:l}}_createParagraphs(i){const t=(h={})=>{const{width:a,height:c,...x}=this.style;return{contentBox:{left:0,top:0,width:0,height:0},lineBox:{left:0,top:0,width:0,height:0},baseline:0,fragments:[],...h,style:{...x,...h.style}}},l=(h={})=>{const a=[],{width:c,height:x,...e}=this.style;return a.push({contentBox:{left:0,top:0,width:0,height:0},inlineBox:{left:0,top:0,width:0,height:0},textBox:{left:0,top:0,width:0,height:0},baseline:0,...h,style:{...e,...h.style},content:h.content??""}),a},o=[];if(typeof i=="string")o.push(t({fragments:l({content:i})}));else{i=Array.isArray(i)?i:[i];for(const h of i)if("fragments"in h){const{fragments:a,...c}=h,x=t({style:c});for(const e of a){const{content:s,...d}=e;x.fragments.push(...l({content:s,style:{...c,...d}}))}o.push(x)}else if("content"in h){const{content:a,...c}=h,x=t({style:c});x.fragments.push(...l({content:a,style:c})),o.push(x)}}return o}_createWrapedParagraphs(i,t){const l=[],o=i.slice();let h,a;for(;h=o.shift();){const c=h.fragments.slice();let x=0;const e=[];let s=!0;for(;a=c.shift();){const d=a.style;this._setContextStyle(d);let B="",f=!1;for(const n of a.content){const r=this.context.measureText(n).width+(s?0:d.letterSpacing),w=/^[\r\n]$/.test(n);if(w||d.textWrap==="wrap"&&t&&x+r>t){let g=w?B.length+1:B.length;!x&&!g&&(B+=n,g++),B.length&&e.push({...a,text:B}),e.length&&(l.push({baseline:h.baseline,style:{...h.style},contentBox:{...h.contentBox},lineBox:{...h.lineBox},fragments:e.slice()}),e.length=0);const p=a.content.substring(g);(p.length||c.length)&&o.unshift({baseline:h.baseline,style:{...h.style},contentBox:{...h.contentBox},lineBox:{...h.lineBox},fragments:(p.length?[{...a,content:p}]:[]).concat(c.slice())}),c.length=0,f=!0;break}else x+=r;B+=n}f||e.push({...a}),s=!1}e.length&&l.push({...h,fragments:e})}return l}_draw(i){const t=this.context;this.style.backgroundColor&&(t.fillStyle=this.style.backgroundColor,t.fillRect(0,0,t.canvas.width,t.canvas.height)),i.forEach(l=>{l.style.backgroundColor&&(t.fillStyle=l.style.backgroundColor,t.fillRect(l.lineBox.left,l.lineBox.top,l.lineBox.width,l.lineBox.height))}),i.forEach(l=>{l.fragments.forEach(o=>{switch(o.style.backgroundColor&&(t.fillStyle=o.style.backgroundColor,t.fillRect(o.inlineBox.left,o.inlineBox.top,o.inlineBox.width,o.inlineBox.height)),this._setContextStyle({...o.style,textAlign:"left",verticalAlign:"top"}),o.style.textStrokeWidth&&t.strokeText(o.content,o.contentBox.left,o.contentBox.top),t.fillText(o.content,o.contentBox.left,o.contentBox.top),o.style.textDecoration){case"underline":t.beginPath(),t.moveTo(o.contentBox.left,o.contentBox.top+o.contentBox.height-2),t.lineTo(o.contentBox.left+o.contentBox.width,o.contentBox.top+o.contentBox.height-2),t.stroke();break;case"line-through":t.beginPath(),t.moveTo(o.contentBox.left,o.contentBox.top+o.contentBox.height/2),t.lineTo(o.contentBox.left+o.contentBox.width,o.contentBox.top+o.contentBox.height/2),t.stroke();break}})})}_resizeView(i,t){const l=this.view;l.style.width=`${i}px`,l.style.height=`${t}px`,l.dataset.width=String(i),l.dataset.height=String(t),l.width=Math.max(1,Math.floor(i*this.pixelRatio)),l.height=Math.max(1,Math.floor(t*this.pixelRatio))}_setContextStyle(i){const t=this.context;switch(t.shadowColor=i.shadowColor,t.shadowOffsetX=i.shadowOffsetX,t.shadowOffsetY=i.shadowOffsetY,t.shadowBlur=i.shadowBlur,t.strokeStyle=i.textStrokeColor,t.lineWidth=i.textStrokeWidth,t.fillStyle=i.color,t.direction=i.direction,t.textAlign=i.textAlign,i.verticalAlign){case"baseline":t.textBaseline="alphabetic";break;case"top":case"middle":case"bottom":t.textBaseline=i.verticalAlign;break}t.font=[i.fontStyle,i.fontWeight,`${i.fontSize}px`,i.fontFamily].join(" "),t.fontKerning=i.fontKerning,t.letterSpacing=`${i.letterSpacing}px`}update(){const i=this.context;let{width:t,height:l}=this.style;t==="auto"&&(t=0),l==="auto"&&(l=0);const o=this.measure(t,l);t||(t=o.width),l=Math.max(l,o.height),this._resizeView(t,l);const h=this.pixelRatio;i.scale(h,h),i.clearRect(0,0,i.canvas.width,i.canvas.height),this._draw(o.paragraphs)}}exports.Text=u;
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
(function(
|
|
1
|
+
(function(p,g){typeof exports=="object"&&typeof module<"u"?g(exports):typeof define=="function"&&define.amd?define(["exports"],g):(p=typeof globalThis<"u"?globalThis:p||self,g(p.modernText={}))})(this,function(p){"use strict";class g{static get defaultStyle(){return{width:"auto",height:"auto",color:"#000000",fontSize:14,fontWeight:"normal",fontFamily:"sans-serif",fontStyle:"normal",fontKerning:"normal",textWrap:"wrap",textAlign:"start",verticalAlign:"baseline",textDecoration:null,textStrokeWidth:0,textStrokeColor:"#000000",direction:"inherit",lineHeight:1,letterSpacing:0,shadowColor:"#000000",shadowOffsetX:0,shadowOffsetY:0,shadowBlur:0}}constructor(i={}){const{view:t=document.createElement("canvas"),pixelRatio:l=window.devicePixelRatio||1,content:o="",style:s}=i;this.view=t,this.context=t.getContext("2d"),this.pixelRatio=l,this.content=o,this.style={...g.defaultStyle,...s},this.update()}measure(i=0,t=0){let l=this._createParagraphs(this.content);l=this._createWrapedParagraphs(l,i);const o=this.context;let s=0;for(let c=l.length,x=0;x<c;x++){const e=l[x];e.contentBox.top=s;let h=0,d=null;for(const n of e.fragments){this._setContextStyle({...n.style,textAlign:"left",verticalAlign:"baseline"});const r=o.measureText(n.content);n.inlineBox.left=h,n.inlineBox.top=s,n.inlineBox.width=r.width,n.inlineBox.height=n.style.fontSize*n.style.lineHeight,n.contentBox.left=n.inlineBox.left,n.contentBox.width=n.inlineBox.width,n.contentBox.height=n.style.fontSize,n.contentBox.top=n.inlineBox.top+(n.inlineBox.height-n.contentBox.height)/2,n.textBox.width=r.actualBoundingBoxLeft+r.actualBoundingBoxRight,n.textBox.height=r.actualBoundingBoxAscent+r.actualBoundingBoxDescent,n.textBox.left=n.contentBox.left+(n.contentBox.width-n.textBox.width)/2,n.textBox.top=n.contentBox.top+(n.contentBox.height-n.textBox.height)/2,n.baseline=n.textBox.top+r.actualBoundingBoxAscent,h+=n.contentBox.width+n.style.letterSpacing,e.contentBox.height<n.contentBox.height&&(d=n),e.contentBox.left=Math.min(e.contentBox.left,n.contentBox.left),e.contentBox.top=Math.min(e.contentBox.top,n.contentBox.top),e.contentBox.height=Math.max(e.contentBox.height,n.contentBox.height),e.lineBox.height=Math.max(e.lineBox.height,n.inlineBox.height)}const B=e.fragments[e.fragments.length-1];e.contentBox.width=Math.max(e.contentBox.width,B?B.contentBox.left+Math.max(B.contentBox.width,B.textBox.width):0),e.lineBox.left=0,e.lineBox.top=s,e.lineBox.width=Math.max(i,e.contentBox.width),this._setContextStyle({...(d??e).style,textAlign:"left",verticalAlign:"baseline"});const f=o.measureText("X");e.baseline=e.lineBox.top+(e.lineBox.height-f.actualBoundingBoxAscent+f.actualBoundingBoxDescent)/2+f.actualBoundingBoxAscent,s+=e.lineBox.height}const a=l.reduce((c,x)=>(c.left=Math.min(c.left,x.lineBox.left),c.top=Math.min(c.top,x.lineBox.top),c.width=Math.max(c.width,x.lineBox.width),c.height+=x.lineBox.height,c),{left:0,top:0,width:0,height:0});for(let c=l.length,x=0;x<c;x++){const e=l[x];e.fragments.forEach(h=>{const d=h.inlineBox.left,B=h.inlineBox.top,f=e.lineBox.height-h.inlineBox.height;switch(h.style.textAlign){case"end":case"right":h.inlineBox.left+=e.lineBox.width-e.contentBox.width;break;case"center":h.inlineBox.left+=(e.lineBox.width-e.contentBox.width)/2;break;case"start":case"left":default:h.inlineBox.left+=e.lineBox.left;break}switch(h.style.verticalAlign){case"top":h.inlineBox.top=e.lineBox.top;break;case"middle":h.inlineBox.top=e.lineBox.top+f/2;break;case"bottom":h.inlineBox.top=e.lineBox.top+f;break;case"sub":case"text-top":case"text-bottom":break;case"baseline":default:h.inlineBox.height<e.lineBox.height&&(h.inlineBox.top+=e.baseline-h.baseline);break}const n=h.inlineBox.left-d,r=h.inlineBox.top-B;h.contentBox.left+=n,h.contentBox.top+=r,h.textBox.left+=n,h.textBox.top+=r})}return{...a,paragraphs:l}}_createParagraphs(i){const t=(s={})=>{const{width:a,height:c,...x}=this.style;return{contentBox:{left:0,top:0,width:0,height:0},lineBox:{left:0,top:0,width:0,height:0},baseline:0,fragments:[],...s,style:{...x,...s.style}}},l=(s={})=>{const a=[],{width:c,height:x,...e}=this.style;return a.push({contentBox:{left:0,top:0,width:0,height:0},inlineBox:{left:0,top:0,width:0,height:0},textBox:{left:0,top:0,width:0,height:0},baseline:0,...s,style:{...e,...s.style},content:s.content??""}),a},o=[];if(typeof i=="string")o.push(t({fragments:l({content:i})}));else{i=Array.isArray(i)?i:[i];for(const s of i)if("fragments"in s){const{fragments:a,...c}=s,x=t({style:c});for(const e of a){const{content:h,...d}=e;x.fragments.push(...l({content:h,style:{...c,...d}}))}o.push(x)}else if("content"in s){const{content:a,...c}=s,x=t({style:c});x.fragments.push(...l({content:a,style:c})),o.push(x)}}return o}_createWrapedParagraphs(i,t){const l=[],o=i.slice();let s,a;for(;s=o.shift();){const c=s.fragments.slice();let x=0;const e=[];let h=!0;for(;a=c.shift();){const d=a.style;this._setContextStyle(d);let B="",f=!1;for(const n of a.content){const r=this.context.measureText(n).width+(h?0:d.letterSpacing),y=/^[\r\n]$/.test(n);if(y||d.textWrap==="wrap"&&t&&x+r>t){let u=y?B.length+1:B.length;!x&&!u&&(B+=n,u++),B.length&&e.push({...a,text:B}),e.length&&(l.push({baseline:s.baseline,style:{...s.style},contentBox:{...s.contentBox},lineBox:{...s.lineBox},fragments:e.slice()}),e.length=0);const w=a.content.substring(u);(w.length||c.length)&&o.unshift({baseline:s.baseline,style:{...s.style},contentBox:{...s.contentBox},lineBox:{...s.lineBox},fragments:(w.length?[{...a,content:w}]:[]).concat(c.slice())}),c.length=0,f=!0;break}else x+=r;B+=n}f||e.push({...a}),h=!1}e.length&&l.push({...s,fragments:e})}return l}_draw(i){const t=this.context;this.style.backgroundColor&&(t.fillStyle=this.style.backgroundColor,t.fillRect(0,0,t.canvas.width,t.canvas.height)),i.forEach(l=>{l.style.backgroundColor&&(t.fillStyle=l.style.backgroundColor,t.fillRect(l.lineBox.left,l.lineBox.top,l.lineBox.width,l.lineBox.height))}),i.forEach(l=>{l.fragments.forEach(o=>{switch(o.style.backgroundColor&&(t.fillStyle=o.style.backgroundColor,t.fillRect(o.inlineBox.left,o.inlineBox.top,o.inlineBox.width,o.inlineBox.height)),this._setContextStyle({...o.style,textAlign:"left",verticalAlign:"top"}),o.style.textStrokeWidth&&t.strokeText(o.content,o.contentBox.left,o.contentBox.top),t.fillText(o.content,o.contentBox.left,o.contentBox.top),o.style.textDecoration){case"underline":t.beginPath(),t.moveTo(o.contentBox.left,o.contentBox.top+o.contentBox.height-2),t.lineTo(o.contentBox.left+o.contentBox.width,o.contentBox.top+o.contentBox.height-2),t.stroke();break;case"line-through":t.beginPath(),t.moveTo(o.contentBox.left,o.contentBox.top+o.contentBox.height/2),t.lineTo(o.contentBox.left+o.contentBox.width,o.contentBox.top+o.contentBox.height/2),t.stroke();break}})})}_resizeView(i,t){const l=this.view;l.style.width=`${i}px`,l.style.height=`${t}px`,l.dataset.width=String(i),l.dataset.height=String(t),l.width=Math.max(1,Math.floor(i*this.pixelRatio)),l.height=Math.max(1,Math.floor(t*this.pixelRatio))}_setContextStyle(i){const t=this.context;switch(t.shadowColor=i.shadowColor,t.shadowOffsetX=i.shadowOffsetX,t.shadowOffsetY=i.shadowOffsetY,t.shadowBlur=i.shadowBlur,t.strokeStyle=i.textStrokeColor,t.lineWidth=i.textStrokeWidth,t.fillStyle=i.color,t.direction=i.direction,t.textAlign=i.textAlign,i.verticalAlign){case"baseline":t.textBaseline="alphabetic";break;case"top":case"middle":case"bottom":t.textBaseline=i.verticalAlign;break}t.font=[i.fontStyle,i.fontWeight,`${i.fontSize}px`,i.fontFamily].join(" "),t.fontKerning=i.fontKerning,t.letterSpacing=`${i.letterSpacing}px`}update(){const i=this.context;let{width:t,height:l}=this.style;t==="auto"&&(t=0),l==="auto"&&(l=0);const o=this.measure(t,l);t||(t=o.width),l=Math.max(l,o.height),this._resizeView(t,l);const s=this.pixelRatio;i.scale(s,s),i.clearRect(0,0,i.canvas.width,i.canvas.height),this._draw(o.paragraphs)}}p.Text=g,Object.defineProperty(p,Symbol.toStringTag,{value:"Module"})});
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
class
|
|
1
|
+
class w {
|
|
2
2
|
static get defaultStyle() {
|
|
3
3
|
return {
|
|
4
4
|
width: "auto",
|
|
@@ -11,7 +11,7 @@ class p {
|
|
|
11
11
|
fontKerning: "normal",
|
|
12
12
|
textWrap: "wrap",
|
|
13
13
|
textAlign: "start",
|
|
14
|
-
|
|
14
|
+
verticalAlign: "baseline",
|
|
15
15
|
textDecoration: null,
|
|
16
16
|
textStrokeWidth: 0,
|
|
17
17
|
textStrokeColor: "#000000",
|
|
@@ -24,198 +24,236 @@ class p {
|
|
|
24
24
|
shadowBlur: 0
|
|
25
25
|
};
|
|
26
26
|
}
|
|
27
|
-
constructor(
|
|
27
|
+
constructor(i = {}) {
|
|
28
28
|
const {
|
|
29
29
|
view: t = document.createElement("canvas"),
|
|
30
|
-
pixelRatio:
|
|
31
|
-
|
|
32
|
-
style:
|
|
33
|
-
} =
|
|
34
|
-
this.view = t, this.context = t.getContext("2d"), this.pixelRatio =
|
|
35
|
-
...
|
|
36
|
-
...
|
|
30
|
+
pixelRatio: l = window.devicePixelRatio || 1,
|
|
31
|
+
content: o = "",
|
|
32
|
+
style: h
|
|
33
|
+
} = i;
|
|
34
|
+
this.view = t, this.context = t.getContext("2d"), this.pixelRatio = l, this.content = o, this.style = {
|
|
35
|
+
...w.defaultStyle,
|
|
36
|
+
...h
|
|
37
37
|
}, this.update();
|
|
38
38
|
}
|
|
39
|
-
measure(
|
|
40
|
-
let
|
|
41
|
-
|
|
39
|
+
measure(i = 0, t = 0) {
|
|
40
|
+
let l = this._createParagraphs(this.content);
|
|
41
|
+
l = this._createWrapedParagraphs(l, i);
|
|
42
42
|
const o = this.context;
|
|
43
|
-
let
|
|
44
|
-
for (let
|
|
45
|
-
const e =
|
|
46
|
-
e.
|
|
47
|
-
let s = 0,
|
|
48
|
-
for (const
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
43
|
+
let h = 0;
|
|
44
|
+
for (let c = l.length, x = 0; x < c; x++) {
|
|
45
|
+
const e = l[x];
|
|
46
|
+
e.contentBox.top = h;
|
|
47
|
+
let s = 0, d = null;
|
|
48
|
+
for (const n of e.fragments) {
|
|
49
|
+
this._setContextStyle({
|
|
50
|
+
...n.style,
|
|
51
|
+
textAlign: "left",
|
|
52
|
+
verticalAlign: "baseline"
|
|
53
|
+
});
|
|
54
|
+
const r = o.measureText(n.content);
|
|
55
|
+
n.inlineBox.left = s, n.inlineBox.top = h, n.inlineBox.width = r.width, n.inlineBox.height = n.style.fontSize * n.style.lineHeight, n.contentBox.left = n.inlineBox.left, n.contentBox.width = n.inlineBox.width, n.contentBox.height = n.style.fontSize, n.contentBox.top = n.inlineBox.top + (n.inlineBox.height - n.contentBox.height) / 2, n.textBox.width = r.actualBoundingBoxLeft + r.actualBoundingBoxRight, n.textBox.height = r.actualBoundingBoxAscent + r.actualBoundingBoxDescent, n.textBox.left = n.contentBox.left + (n.contentBox.width - n.textBox.width) / 2, n.textBox.top = n.contentBox.top + (n.contentBox.height - n.textBox.height) / 2, n.baseline = n.textBox.top + r.actualBoundingBoxAscent, s += n.contentBox.width + n.style.letterSpacing, e.contentBox.height < n.contentBox.height && (d = n), e.contentBox.left = Math.min(e.contentBox.left, n.contentBox.left), e.contentBox.top = Math.min(e.contentBox.top, n.contentBox.top), e.contentBox.height = Math.max(e.contentBox.height, n.contentBox.height), e.lineBox.height = Math.max(e.lineBox.height, n.inlineBox.height);
|
|
53
56
|
}
|
|
54
|
-
|
|
57
|
+
const B = e.fragments[e.fragments.length - 1];
|
|
58
|
+
e.contentBox.width = Math.max(
|
|
59
|
+
e.contentBox.width,
|
|
60
|
+
B ? B.contentBox.left + Math.max(B.contentBox.width, B.textBox.width) : 0
|
|
61
|
+
), e.lineBox.left = 0, e.lineBox.top = h, e.lineBox.width = Math.max(i, e.contentBox.width), this._setContextStyle({
|
|
62
|
+
...(d ?? e).style,
|
|
63
|
+
textAlign: "left",
|
|
64
|
+
verticalAlign: "baseline"
|
|
65
|
+
});
|
|
66
|
+
const f = o.measureText("X");
|
|
67
|
+
e.baseline = e.lineBox.top + (e.lineBox.height - f.actualBoundingBoxAscent + f.actualBoundingBoxDescent) / 2 + f.actualBoundingBoxAscent, h += e.lineBox.height;
|
|
55
68
|
}
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
s.
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
});
|
|
98
|
-
break;
|
|
99
|
-
}
|
|
69
|
+
const a = l.reduce((c, x) => (c.left = Math.min(c.left, x.lineBox.left), c.top = Math.min(c.top, x.lineBox.top), c.width = Math.max(c.width, x.lineBox.width), c.height += x.lineBox.height, c), { left: 0, top: 0, width: 0, height: 0 });
|
|
70
|
+
for (let c = l.length, x = 0; x < c; x++) {
|
|
71
|
+
const e = l[x];
|
|
72
|
+
e.fragments.forEach((s) => {
|
|
73
|
+
const d = s.inlineBox.left, B = s.inlineBox.top, f = e.lineBox.height - s.inlineBox.height;
|
|
74
|
+
switch (s.style.textAlign) {
|
|
75
|
+
case "end":
|
|
76
|
+
case "right":
|
|
77
|
+
s.inlineBox.left += e.lineBox.width - e.contentBox.width;
|
|
78
|
+
break;
|
|
79
|
+
case "center":
|
|
80
|
+
s.inlineBox.left += (e.lineBox.width - e.contentBox.width) / 2;
|
|
81
|
+
break;
|
|
82
|
+
case "start":
|
|
83
|
+
case "left":
|
|
84
|
+
default:
|
|
85
|
+
s.inlineBox.left += e.lineBox.left;
|
|
86
|
+
break;
|
|
87
|
+
}
|
|
88
|
+
switch (s.style.verticalAlign) {
|
|
89
|
+
case "top":
|
|
90
|
+
s.inlineBox.top = e.lineBox.top;
|
|
91
|
+
break;
|
|
92
|
+
case "middle":
|
|
93
|
+
s.inlineBox.top = e.lineBox.top + f / 2;
|
|
94
|
+
break;
|
|
95
|
+
case "bottom":
|
|
96
|
+
s.inlineBox.top = e.lineBox.top + f;
|
|
97
|
+
break;
|
|
98
|
+
case "sub":
|
|
99
|
+
case "text-top":
|
|
100
|
+
case "text-bottom":
|
|
101
|
+
break;
|
|
102
|
+
case "baseline":
|
|
103
|
+
default:
|
|
104
|
+
s.inlineBox.height < e.lineBox.height && (s.inlineBox.top += e.baseline - s.baseline);
|
|
105
|
+
break;
|
|
106
|
+
}
|
|
107
|
+
const n = s.inlineBox.left - d, r = s.inlineBox.top - B;
|
|
108
|
+
s.contentBox.left += n, s.contentBox.top += r, s.textBox.left += n, s.textBox.top += r;
|
|
109
|
+
});
|
|
100
110
|
}
|
|
101
|
-
return { ...
|
|
111
|
+
return { ...a, paragraphs: l };
|
|
102
112
|
}
|
|
103
|
-
_createParagraphs(
|
|
104
|
-
const t = {
|
|
105
|
-
width:
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
}, i = (l = {}) => ({ ...t, fragments: [], ...l }), o = (l = {}) => {
|
|
113
|
-
const h = [], { width: r, height: e, ...s } = this.style;
|
|
114
|
-
return h.push({
|
|
115
|
-
fillX: 0,
|
|
116
|
-
fillY: 0,
|
|
117
|
-
...t,
|
|
118
|
-
...l,
|
|
113
|
+
_createParagraphs(i) {
|
|
114
|
+
const t = (h = {}) => {
|
|
115
|
+
const { width: a, height: c, ...x } = this.style;
|
|
116
|
+
return {
|
|
117
|
+
contentBox: { left: 0, top: 0, width: 0, height: 0 },
|
|
118
|
+
lineBox: { left: 0, top: 0, width: 0, height: 0 },
|
|
119
|
+
baseline: 0,
|
|
120
|
+
fragments: [],
|
|
121
|
+
...h,
|
|
119
122
|
style: {
|
|
120
|
-
...
|
|
121
|
-
...
|
|
123
|
+
...x,
|
|
124
|
+
...h.style
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
}, l = (h = {}) => {
|
|
128
|
+
const a = [], { width: c, height: x, ...e } = this.style;
|
|
129
|
+
return a.push({
|
|
130
|
+
contentBox: { left: 0, top: 0, width: 0, height: 0 },
|
|
131
|
+
inlineBox: { left: 0, top: 0, width: 0, height: 0 },
|
|
132
|
+
textBox: { left: 0, top: 0, width: 0, height: 0 },
|
|
133
|
+
baseline: 0,
|
|
134
|
+
...h,
|
|
135
|
+
style: {
|
|
136
|
+
...e,
|
|
137
|
+
...h.style
|
|
122
138
|
},
|
|
123
|
-
|
|
124
|
-
}),
|
|
125
|
-
},
|
|
126
|
-
if (typeof
|
|
127
|
-
|
|
139
|
+
content: h.content ?? ""
|
|
140
|
+
}), a;
|
|
141
|
+
}, o = [];
|
|
142
|
+
if (typeof i == "string")
|
|
143
|
+
o.push(t({ fragments: l({ content: i }) }));
|
|
128
144
|
else {
|
|
129
|
-
|
|
130
|
-
for (const
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
const
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
h.fragments.push(...o({ data: u, style: { ...e, ...c } }));
|
|
145
|
+
i = Array.isArray(i) ? i : [i];
|
|
146
|
+
for (const h of i)
|
|
147
|
+
if ("fragments" in h) {
|
|
148
|
+
const { fragments: a, ...c } = h, x = t({ style: c });
|
|
149
|
+
for (const e of a) {
|
|
150
|
+
const { content: s, ...d } = e;
|
|
151
|
+
x.fragments.push(...l({ content: s, style: { ...c, ...d } }));
|
|
137
152
|
}
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
153
|
+
o.push(x);
|
|
154
|
+
} else if ("content" in h) {
|
|
155
|
+
const { content: a, ...c } = h, x = t({ style: c });
|
|
156
|
+
x.fragments.push(...l({ content: a, style: c })), o.push(x);
|
|
141
157
|
}
|
|
142
|
-
n.push(h);
|
|
143
|
-
}
|
|
144
158
|
}
|
|
145
|
-
return
|
|
159
|
+
return o;
|
|
146
160
|
}
|
|
147
|
-
_createWrapedParagraphs(
|
|
148
|
-
const
|
|
149
|
-
let
|
|
150
|
-
for (;
|
|
151
|
-
const
|
|
152
|
-
let
|
|
161
|
+
_createWrapedParagraphs(i, t) {
|
|
162
|
+
const l = [], o = i.slice();
|
|
163
|
+
let h, a;
|
|
164
|
+
for (; h = o.shift(); ) {
|
|
165
|
+
const c = h.fragments.slice();
|
|
166
|
+
let x = 0;
|
|
153
167
|
const e = [];
|
|
154
168
|
let s = !0;
|
|
155
|
-
for (;
|
|
156
|
-
const
|
|
157
|
-
this._setContextStyle(
|
|
158
|
-
let
|
|
159
|
-
for (const
|
|
160
|
-
const
|
|
161
|
-
if (
|
|
162
|
-
let
|
|
163
|
-
!
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
...
|
|
167
|
-
|
|
168
|
-
|
|
169
|
+
for (; a = c.shift(); ) {
|
|
170
|
+
const d = a.style;
|
|
171
|
+
this._setContextStyle(d);
|
|
172
|
+
let B = "", f = !1;
|
|
173
|
+
for (const n of a.content) {
|
|
174
|
+
const r = this.context.measureText(n).width + (s ? 0 : d.letterSpacing), u = /^[\r\n]$/.test(n);
|
|
175
|
+
if (u || d.textWrap === "wrap" && t && x + r > t) {
|
|
176
|
+
let g = u ? B.length + 1 : B.length;
|
|
177
|
+
!x && !g && (B += n, g++), B.length && e.push({ ...a, text: B }), e.length && (l.push({
|
|
178
|
+
baseline: h.baseline,
|
|
179
|
+
style: { ...h.style },
|
|
180
|
+
contentBox: { ...h.contentBox },
|
|
181
|
+
lineBox: { ...h.lineBox },
|
|
182
|
+
fragments: e.slice()
|
|
183
|
+
}), e.length = 0);
|
|
184
|
+
const p = a.content.substring(g);
|
|
185
|
+
(p.length || c.length) && o.unshift({
|
|
186
|
+
baseline: h.baseline,
|
|
187
|
+
style: { ...h.style },
|
|
188
|
+
contentBox: { ...h.contentBox },
|
|
189
|
+
lineBox: { ...h.lineBox },
|
|
190
|
+
fragments: (p.length ? [{ ...a, content: p }] : []).concat(c.slice())
|
|
191
|
+
}), c.length = 0, f = !0;
|
|
169
192
|
break;
|
|
170
193
|
} else
|
|
171
|
-
|
|
172
|
-
|
|
194
|
+
x += r;
|
|
195
|
+
B += n;
|
|
173
196
|
}
|
|
174
|
-
f || e.push({ ...
|
|
197
|
+
f || e.push({ ...a }), s = !1;
|
|
175
198
|
}
|
|
176
|
-
e.length &&
|
|
199
|
+
e.length && l.push({ ...h, fragments: e });
|
|
177
200
|
}
|
|
178
|
-
return
|
|
201
|
+
return l;
|
|
179
202
|
}
|
|
180
|
-
_draw(
|
|
203
|
+
_draw(i) {
|
|
181
204
|
const t = this.context;
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
205
|
+
this.style.backgroundColor && (t.fillStyle = this.style.backgroundColor, t.fillRect(0, 0, t.canvas.width, t.canvas.height)), i.forEach((l) => {
|
|
206
|
+
l.style.backgroundColor && (t.fillStyle = l.style.backgroundColor, t.fillRect(l.lineBox.left, l.lineBox.top, l.lineBox.width, l.lineBox.height));
|
|
207
|
+
}), i.forEach((l) => {
|
|
208
|
+
l.fragments.forEach((o) => {
|
|
209
|
+
switch (o.style.backgroundColor && (t.fillStyle = o.style.backgroundColor, t.fillRect(o.inlineBox.left, o.inlineBox.top, o.inlineBox.width, o.inlineBox.height)), this._setContextStyle({
|
|
210
|
+
...o.style,
|
|
211
|
+
textAlign: "left",
|
|
212
|
+
verticalAlign: "top"
|
|
213
|
+
}), o.style.textStrokeWidth && t.strokeText(o.content, o.contentBox.left, o.contentBox.top), t.fillText(o.content, o.contentBox.left, o.contentBox.top), o.style.textDecoration) {
|
|
186
214
|
case "underline":
|
|
187
|
-
t.beginPath(), t.moveTo(o.
|
|
215
|
+
t.beginPath(), t.moveTo(o.contentBox.left, o.contentBox.top + o.contentBox.height - 2), t.lineTo(o.contentBox.left + o.contentBox.width, o.contentBox.top + o.contentBox.height - 2), t.stroke();
|
|
188
216
|
break;
|
|
189
217
|
case "line-through":
|
|
190
|
-
t.beginPath(), t.moveTo(o.
|
|
218
|
+
t.beginPath(), t.moveTo(o.contentBox.left, o.contentBox.top + o.contentBox.height / 2), t.lineTo(o.contentBox.left + o.contentBox.width, o.contentBox.top + o.contentBox.height / 2), t.stroke();
|
|
191
219
|
break;
|
|
192
220
|
}
|
|
193
221
|
});
|
|
194
222
|
});
|
|
195
223
|
}
|
|
196
|
-
_resizeView(
|
|
197
|
-
const
|
|
198
|
-
|
|
224
|
+
_resizeView(i, t) {
|
|
225
|
+
const l = this.view;
|
|
226
|
+
l.style.width = `${i}px`, l.style.height = `${t}px`, l.dataset.width = String(i), l.dataset.height = String(t), l.width = Math.max(1, Math.floor(i * this.pixelRatio)), l.height = Math.max(1, Math.floor(t * this.pixelRatio));
|
|
199
227
|
}
|
|
200
|
-
_setContextStyle(
|
|
228
|
+
_setContextStyle(i) {
|
|
201
229
|
const t = this.context;
|
|
202
|
-
t.shadowColor =
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
230
|
+
switch (t.shadowColor = i.shadowColor, t.shadowOffsetX = i.shadowOffsetX, t.shadowOffsetY = i.shadowOffsetY, t.shadowBlur = i.shadowBlur, t.strokeStyle = i.textStrokeColor, t.lineWidth = i.textStrokeWidth, t.fillStyle = i.color, t.direction = i.direction, t.textAlign = i.textAlign, i.verticalAlign) {
|
|
231
|
+
case "baseline":
|
|
232
|
+
t.textBaseline = "alphabetic";
|
|
233
|
+
break;
|
|
234
|
+
case "top":
|
|
235
|
+
case "middle":
|
|
236
|
+
case "bottom":
|
|
237
|
+
t.textBaseline = i.verticalAlign;
|
|
238
|
+
break;
|
|
239
|
+
}
|
|
240
|
+
t.font = [
|
|
241
|
+
i.fontStyle,
|
|
242
|
+
i.fontWeight,
|
|
243
|
+
`${i.fontSize}px`,
|
|
244
|
+
i.fontFamily
|
|
245
|
+
].join(" "), t.fontKerning = i.fontKerning, t.letterSpacing = `${i.letterSpacing}px`;
|
|
208
246
|
}
|
|
209
247
|
update() {
|
|
210
|
-
const
|
|
211
|
-
let { width: t, height:
|
|
212
|
-
t === "auto" && (t = 0),
|
|
213
|
-
const o = this.measure(t,
|
|
214
|
-
t || (t = o.width),
|
|
215
|
-
const
|
|
216
|
-
|
|
248
|
+
const i = this.context;
|
|
249
|
+
let { width: t, height: l } = this.style;
|
|
250
|
+
t === "auto" && (t = 0), l === "auto" && (l = 0);
|
|
251
|
+
const o = this.measure(t, l);
|
|
252
|
+
t || (t = o.width), l = Math.max(l, o.height), this._resizeView(t, l);
|
|
253
|
+
const h = this.pixelRatio;
|
|
254
|
+
i.scale(h, h), i.clearRect(0, 0, i.canvas.width, i.canvas.height), this._draw(o.paragraphs);
|
|
217
255
|
}
|
|
218
256
|
}
|
|
219
257
|
export {
|
|
220
|
-
|
|
258
|
+
w as Text
|
|
221
259
|
};
|
package/package.json
CHANGED
package/types/Text.d.ts
CHANGED
|
@@ -3,32 +3,32 @@ export type FontStyle = 'normal' | 'italic' | 'oblique' | `oblique ${string}`;
|
|
|
3
3
|
export type FontKerning = 'auto' | 'none' | 'normal';
|
|
4
4
|
export type TextWrap = 'wrap' | 'nowrap';
|
|
5
5
|
export type TextAlign = 'center' | 'end' | 'left' | 'right' | 'start';
|
|
6
|
-
export type
|
|
6
|
+
export type VerticalAlign = 'baseline' | 'top' | 'middle' | 'bottom' | 'sub' | 'text-top' | 'text-bottom';
|
|
7
7
|
export type TextDecoration = 'underline' | 'line-through';
|
|
8
|
-
export interface
|
|
8
|
+
export interface BoundingBox {
|
|
9
|
+
left: number;
|
|
10
|
+
top: number;
|
|
9
11
|
width: number;
|
|
10
12
|
height: number;
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
}
|
|
14
|
+
export interface TextParagraph {
|
|
15
|
+
contentBox: BoundingBox;
|
|
16
|
+
lineBox: BoundingBox;
|
|
17
|
+
baseline: number;
|
|
15
18
|
fragments: Array<TextFragment>;
|
|
19
|
+
style: TextFragmentStyle;
|
|
16
20
|
}
|
|
17
21
|
export interface TextFragment {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
absoluteX: number;
|
|
24
|
-
absoluteY: number;
|
|
25
|
-
fillX: number;
|
|
26
|
-
fillY: number;
|
|
27
|
-
data: string;
|
|
22
|
+
contentBox: BoundingBox;
|
|
23
|
+
inlineBox: BoundingBox;
|
|
24
|
+
textBox: BoundingBox;
|
|
25
|
+
baseline: number;
|
|
26
|
+
content: string;
|
|
28
27
|
style: TextFragmentStyle;
|
|
29
28
|
}
|
|
30
29
|
export interface TextFragmentStyle {
|
|
31
30
|
color: string;
|
|
31
|
+
backgroundColor?: string;
|
|
32
32
|
fontSize: number;
|
|
33
33
|
fontWeight: FontWeight;
|
|
34
34
|
fontFamily: string;
|
|
@@ -36,7 +36,7 @@ export interface TextFragmentStyle {
|
|
|
36
36
|
fontKerning: FontKerning;
|
|
37
37
|
textWrap: TextWrap;
|
|
38
38
|
textAlign: TextAlign;
|
|
39
|
-
|
|
39
|
+
verticalAlign: VerticalAlign;
|
|
40
40
|
textDecoration: TextDecoration | null;
|
|
41
41
|
textStrokeWidth: number;
|
|
42
42
|
textStrokeColor: string;
|
|
@@ -52,27 +52,23 @@ export interface TextStyle extends TextFragmentStyle {
|
|
|
52
52
|
width: number | 'auto';
|
|
53
53
|
height: number | 'auto';
|
|
54
54
|
}
|
|
55
|
-
export interface
|
|
56
|
-
|
|
55
|
+
export interface TextParagraphWithContentAndStyle extends Partial<TextFragmentStyle> {
|
|
56
|
+
content: string;
|
|
57
57
|
}
|
|
58
58
|
export interface TextParagraphWithFragmentsAndStyle extends Partial<TextFragmentStyle> {
|
|
59
59
|
fragments: Array<TextFragmentWithStyle>;
|
|
60
60
|
}
|
|
61
61
|
export interface TextFragmentWithStyle extends Partial<TextFragmentStyle> {
|
|
62
|
-
|
|
62
|
+
content: string;
|
|
63
63
|
}
|
|
64
|
-
export type TextData = string |
|
|
64
|
+
export type TextData = string | TextParagraphWithContentAndStyle | TextParagraphWithFragmentsAndStyle | Array<TextParagraphWithContentAndStyle | TextParagraphWithFragmentsAndStyle>;
|
|
65
65
|
export interface TextOptions {
|
|
66
66
|
view?: HTMLCanvasElement;
|
|
67
67
|
pixelRatio?: number;
|
|
68
|
-
|
|
68
|
+
content?: TextData;
|
|
69
69
|
style?: Partial<TextStyle>;
|
|
70
70
|
}
|
|
71
|
-
export interface MeasureResult {
|
|
72
|
-
x: number;
|
|
73
|
-
y: number;
|
|
74
|
-
width: number;
|
|
75
|
-
height: number;
|
|
71
|
+
export interface MeasureResult extends BoundingBox {
|
|
76
72
|
paragraphs: Array<TextParagraph>;
|
|
77
73
|
}
|
|
78
74
|
export declare class Text {
|
|
@@ -81,10 +77,10 @@ export declare class Text {
|
|
|
81
77
|
readonly context: CanvasRenderingContext2D;
|
|
82
78
|
pixelRatio: number;
|
|
83
79
|
style: TextStyle;
|
|
84
|
-
|
|
80
|
+
content: TextData;
|
|
85
81
|
constructor(options?: TextOptions);
|
|
86
|
-
measure(width?: number,
|
|
87
|
-
protected _createParagraphs(
|
|
82
|
+
measure(width?: number, _height?: number): MeasureResult;
|
|
83
|
+
protected _createParagraphs(content: TextData): Array<TextParagraph>;
|
|
88
84
|
protected _createWrapedParagraphs(paragraphs: Array<TextParagraph>, width: number): Array<TextParagraph>;
|
|
89
85
|
protected _draw(paragraphs: Array<TextParagraph>): void;
|
|
90
86
|
protected _resizeView(width: number, height: number): void;
|