开发环境:node
开发语言:javascript
开发依赖的包:javascript
图表的宽度是1000
先把最终要呈现的效果贴出来,见下图:html
查看charts配置项及实例,给图表增长额外的文本块,只有一个属性graphic【ˈgrafik】
彷佛能够实现,它指的是原生图形元素组件,能够由多种类型组成,如image, text, circle, sector, ring, polygon, polyline, ...
,结合给的实例,看起来很不错,so easy!立刻开干。java
代码以下:node
var node_echarts = require("node-echarts"); var Path = require("path"); const text = '个人电脑只有4G运行内存,采用默认的idea配置,内存在30分钟内会飚到 >80% ,同时会发生OOM!Chrome就不敢打开!经过上面的配置能够将内存使用下降一半以上,只有idea和chrome 的话,内存才刚刚 40% 。下面的能够看也能够不看了,下面的分析是别人就好了分析,经过阅读可见他的电脑内存的确不小(16G的macbook pro),对于咱们学生党,默默的使用着4G内存的电脑,就很少说上面了!不过,参与讨论的一位开发者给笔者发了一份他的设置,虽然是针对同个项目,该设置却极其复杂。笔者对本身的设置并没有不满,但很是好奇,这些彻底不一样的设置对比 JetBrains 提供的默认设置。'; const config = { legend: { bottom: 0, show: true, data: ["身份证数量", "环比增加率"] }, xAxis: [ { type: "category", data: [ "201701", "201702", "201703", "201704", "201705", "201706", "201707", "201708", "201709", "201710", "201711", "201712", "201801" ], axisLabel: { interval: 0 }, axisPointer: { type: "shadow" }, splitLine:{show: false} } ], yAxis: [ { type: "value", axisLabel: { formatter: null } }, { type: "value", axisLabel: { formatter: "{value}%" }} ], series: [ { type: "bar", name: "身份证数量", data: [ 23620000, 21060000, 26420000, 30180000, 31430000, 34100000, 33740000, 40170000, 39910000, 38420000, 49300000, 50710000, 46550000 ], yAxisIndex: 0 }, { type: "line", name: "环比增加率", data: [ -23, -12.13, 20.26, 12.46, 4, 7.82, -1.09, 16.02, -0.65, -3.88, 22.05, 2.79, -8.95 ], yAxisIndex: 1 } ], color: ["#4498f6", "#d9e96c"], graphic: [ { type: 'text', left: '10%', bottom: 'bottom', style: { fill: '#333', text: text, font: '14px Microsoft YaHei' } } ], }; node_echarts({ width: 1000, height: 400, option: config, path: Path.join(__dirname, "./charts.png") }); process.exit();
效果以下图:git
问题有俩:github
第2个问题很好解决,熟悉echarts配置的话,给grid的bottom,legend的bottom设个值,但这个值是动态的,和文案的行数有关系,先定一个基值,假如只有一行,grid的bottom为90,legend的bottom为35,每多一行,就多加15,伪代码以下:chrome
const config = { legend: { bottom: 3 * 15 + 35, show: true, data: ["身份证数量", "环比增加率"] }, grid: {bottom: 3 * 15 + 90} ...
要获得具体的行数,实际上是解决第1个问题,因此最核心的问题是在限定的宽度里怎么计算一段文字的行数?
先算下一行能放多少个字符,中英文都算一个字符,以都是中文来算,这样算下获得结果是63,行数 = Math.ceil(字符总长度/63)。获得行数后,还要知道每行的字符是什么,毕竟换行是要这样的:canvas
... graphic: [ { type: 'text', left: '10%', bottom: 'bottom', style: { fill: '#333', text: ['text','text2', 'text3'].join('\n'), font: '14px Microsoft YaHei' } } ], ...
想了半天,代码出来了数组
... const buildText = (result=text) => { const graphicY = 15; let texts = []; let gridBottom = 90; let legendBottom = 35; const rlen = 63; // 一行最多的字符长度 const len = result.length; // 全部字符长度 // 大于一行 if (len > rlen) { const temp = Math.ceil(len / rlen); // 总行数 const arr = result.split(""); // 把文案分割为每一个字符 const newArrs = {}; // 循环总行数 for (let k = 0; k < temp; k++) { newArrs[k] = []; // 存储每行的字符 for (let i = rlen * k; i < rlen * (k + 1); i++) { if(arr[i] != undefined) newArrs[k].push(arr[i]); } } for(let j in newArrs){ texts.push(newArrs[j].join('')); } const lastLen = texts.length-1; gridBottom = lastLen * graphicY + gridBottom; legendBottom = lastLen * graphicY + legendBottom; } else { texts = [result]; } // console.log(texts); return { graphic: [ { type: "text", left: "10%", bottom: "bottom", style: { fill: "#333", text: texts.join("\n"), font: "14px Microsoft YaHei" } } ], gridBottom: gridBottom, legendBottom: legendBottom }; } const texts = buildText(); const config = { ... legend: { bottom: texts.legendBottom, show: true, data: ["身份证数量", "环比增加率"] }, grid:{ bottom: texts.gridBottom}, ... } config.graphic = texts.graphic;
获得效果以下:echarts
换行了,可是每行的字符不同,并且没行间距,挤得慌。说明计算每一行的长度的方法不对,中文和英文的宽度不同,须要知道每一个字符的宽度才行,而后graphic不是支持image嘛,我就换个思路,把文字换成图片吧,由于和canvas相关,google一把“canvas 文字换行”后,找到这篇canvas文本绘制自动换行、字间距、竖排等实现,再此安利下这位做者,他写的博文都是满满的干货,canvas中有一个颇有用的API:measureText
,用来计算字符宽度,把文字变成图片的代码以下:
var fs = require('fs') var path = require('path') var Canvas = require('canvas') const maxWidth = 1000; let height = 20; const text = '个人电脑只有4G运行内存,采用默认的idea配置,内存在30分钟内会飚到 >80% ,同时会发生OOM!Chrome就不敢打开!经过上面的配置能够将内存使用下降一半以上,只有idea和chrome 的话,内存才刚刚 40% 。下面的能够看也能够不看了,下面的分析是别人就好了分析,经过阅读可见他的电脑内存的确不小(16G的macbook pro),对于咱们学生党,默默的使用着4G内存的电脑,就很少说上面了!不过,参与讨论的一位开发者给笔者发了一份他的设置,虽然是针对同个项目,该设置却极其复杂。笔者对本身的设置并没有不满,但很是好奇,这些彻底不一样的设置对比 JetBrains 提供的默认设置。'; const rlen = 63; // 一行最多的字符长度 const len = text.length; // 全部字符长度 const temp = Math.ceil(len / rlen); // 总行数 var canvas = Canvas.createCanvas(maxWidth, temp * height) var ctx = canvas.getContext('2d') ctx.globalAlpha = 1 ctx.font = '14px Microsoft Yahei' ctx.lineWidth = 1 ctx.fillStyle = '#000' const arrText = text.split(''); let line = ''; let y = 20; const lineHeight = 25; // 核心思路是这段代码,循环每一个字符,当字符宽度大于最大宽度就换行,且Y坐标也增长 for (var n = 0; n < arrText.length; n++) { var testLine = line + arrText[n]; var metrics = ctx.measureText(testLine); var testWidth = metrics.width; if (testWidth > maxWidth && n > 0) { ctx.fillText(line, 0, y); line = arrText[n]; y += lineHeight; } else { line = testLine; } // console.log(line) } console.log(line) ctx.fillText(line, 0, y); canvas.createPNGStream().pipe(fs.createWriteStream(path.join(__dirname, 'text.png')))
拿到的图片以下:
而后修改graphic
... graphic:[{ type: 'image', left: '10%', bottom: 'bottom', style: { image: path.join(__dirname, 'text.png') width: 1000, height: 200 } }], ...
但是图片没有生成,上面这段代码没有发生做用。查了源代码,也没找到缘由,只好提个issue。这又回到起点,不过计算字符长度的方法采用上述方法
const buildText = (text=text) => { const graphicY = 15; let gridBottom = 90; let legendBottom = 35; const maxWidth = 900; const canvas = createCanvas(maxWidth,100); const ctx = canvas.getContext("2d"); ctx.font = "normal 14px SongTi"; const arrText = text.split(''); let line = ''; const newArrs = []; for (var n = 0; n < arrText.length; n++) { var testLine = line + arrText[n]; var metrics = ctx.measureText(testLine); var testWidth = metrics.width; if (testWidth > maxWidth & n>0) { line = arrText[n]; newArrs.push(testLine.substr(0, testLine.length-1)); } else { line = testLine; } } newArrs.push(line); //console.log(newArrs); const row = newArrs.length; if (row > 1) { gridBottom = row * graphicY + gridBottom; legendBottom = row * graphicY + legendBottom; } return { graphic:[ { type: "text", left: "10%", bottom: "bottom", style: { fill: "#333", text: newArrs.join("\n"), font: "14px Songti" } } ], gridBottom: gridBottom, legendBottom: legendBottom }; }
获得的效果以下:
行间距的问题没解决,想到graphic既然是数组,把每行拆开做为单独的对象,bottom的值都不同。
const buildText = (text=text) =>{ const graphicY = 15; const lineY = 20; // 设置每行文字bottom的基值 let gridBottom = 90; let legendBottom = 35; const maxWidth = 900; const canvas = createCanvas(maxWidth,100); const ctx = canvas.getContext("2d"); ctx.font = "normal 14px SongTi"; const arrText = text.split(''); let line = ''; const newArrs = []; for (var n = 0; n < arrText.length; n++) { var testLine = line + arrText[n]; var metrics = ctx.measureText(testLine); var testWidth = metrics.width; if (testWidth > maxWidth & n>0) { line = arrText[n]; newArrs.push(testLine.substr(0, testLine.length-1)); } else { line = testLine; } } newArrs.push(line); //console.log(newArrs); const row = newArrs.length; // 总行数 if (row > 1) { gridBottom = row * graphicY + gridBottom; legendBottom = row * graphicY + legendBottom; } let graphics = []; // 循环每行文字 for (let k=0; k < row; k++){ const temp = { type: "text", left: "5%", bottom: (row-1-k) * lineY, // 数值越大,越靠前 style: { fill: "#333", text: [`${newArrs[k]}`].join("\n"), font: "14px SongTi" } } graphics.push(temp); } // console.log(graphics); return { graphic: graphics, gridBottom: gridBottom, legendBottom: legendBottom };
至此,问题都解决了。
最终的源代码传送门:github
了解echarts坐标系
熟悉echarts基本配置
熟悉echarts graphic配置
了解canvas基本API
熟悉数学
对于数字要保持敏感,不要写死了
多搜索,多尝试,多思考才会变通