node服务 图片合并echarts图表,绘制方框

发布时间 2023-06-26 11:26:04作者: Edith6

效果图:
image

环境及依赖配置

node版本参考:v14.18.3
npm版本参考:6.14.15

环境安装

Mac系统 需要先将Homebrew升级到与系统匹配的版本

系统 命令
Mac OS X brew install pkg-config cairo pango libpng jpeg giflib librsvg pixman
Ubuntu sudo apt-get install build-essential libcairo2-dev libpango1.0-dev libjpeg-dev libgif-dev librsvg2-dev
Fedora sudo yum install gcc-c++ cairo-devel pango-devel libjpeg-turbo-devel giflib-devel
Solaris pkgin install cairo pango pkg-config xproto renderproto kbproto xextproto
OpenBSD doas pkg_add cairo pango png jpeg giflib
Windows And Others wiki

Mac OS X v10.11+:如果系统版本Mac OS X v10.11+并且在编译时遇到问题,请运行以下命令:xcode-select--install

node-canvas安装

1.这里推荐使用yarn,npm可能会遇到权限问题,可使用 npm --build-from-source --unsafe-perm install 尝试(百度只搜到这个方法,亲测无效)
2.为防止node-gyp这个大坑的一系列报错和问题
先全局安装node-gyp

npm -g node-gyp 

3.安装canvas的时候需要从源代码去构建,否则会安装失败或报错,需要在安装是添加一些选项,根据对应的的包管理工具,使用对应的命令

npm: npm install canvas --build-from-source
yarn: npm_config_build_from_source=true yarn add canvas
pnpm: npm_config_build_from_source=true pnpm add canvas

node-canvas源码地址

安装过程中还可能遇到很多告警,如不影响编译安装,可不理会

echarts服务端渲染

代码:
echarts图表配置line.js


var echartLine = (data) => {
    var xData = [], yData = [];
    for (let index = 0; index < data.length; index++) {
        const item = data[index];
        xData.push(item.name)
        yData.push(item.value)
    }
    var option = {
        backgroundColor: "rgba(255,255,255,0.8)",
        grid: {
            left: "10%",
            right: "10%",
            top: "10%",
            bottom: "10%",
            containLabel: true,
            show: true,
            borderColor: "rgba(0,0,0,0)",
        },
        xAxis: {
            type: "category",
            boundaryGap: false,
            offset: 5,
            axisLabel: {
                // color: "rgba(198, 211, 236, 1)",
                fontSize: 12,
                fontFamily: "PingFangSC-Regular, PingFang SC",
                showMaxLabel: false,
            },
            axisLine: {
                show: false,
            },
            axisTick: {
                show: false,
            },
            splitLine: {
                show: false,
                lineStyle: {
                    color: "rgba(64, 72, 106, 1)",
                },
            },
            data: xData,
        },
        yAxis: {
            type: "value",
            offset: 3,
            splitLine: {
                show: true,
                lineStyle: {
                    color: "rgba(64, 72, 106, 1)",
                },
            },
            axisLine: {
                show: false,
            },
            axisTick: {
                show: false,
            },
            axisLabel: {
                // color: "rgba(198, 211, 236, 1)",
                fontSize: 12,
                fontFamily: "PingFangSC-Regular, PingFang SC",
            },
        },
        series: [
            {
                type: "line",
                smooth: true,
                // color: "rgba(98, 208, 255, 1)",
                data: yData,
            },
        ],
    }
    return option
}
module.exports = echartLine;

路由文件index.js

var express = require('express');
var router = express.Router();

//生成echart图片模块
const echarts = require('echarts');
const path = require("path");
const fs = require("fs");

const ECHARTS_LINE = require("../echarts/line");
const { createCanvas, Image } = require('canvas');

var resultData = {
    status: 0,
    data: null,
    msg: ""
}

/* GET home page. */
router.get('/', function (req, res, next) {
    res.render('index', { title: 'Express' });
});

router.get('/testApi', function (req, res, next) {
    res.send({
        code: 1,
        data: "success"
    });
});


var echartsColor = [
    "#74D690",
    "#ff0000",
    "#1AA7E8",
    "#BDE5F8",
    "#5273E0",
    "#32CEDC"
]

router.post('/mergeImage', function (req, res, next) {
    var data = req.body;
    var { imgUrl, imgCoor, chartData } = data
    if (imgUrl && imgCoor && chartData) {
        var coor = []
        for (let index = 0; index < imgCoor.length; index++) {
            const item = imgCoor[index];
            if (item.origin && item.origin.includes(",") && item.target && item.target.includes(",")) {
                var origin = item.origin.split(",")
                var target = item.target.split(",")
                coor.push([+origin[0], +origin[1], target[0] - origin[0], target[1] - origin[1]])
            }
        }
        var bgImg = new Image();
        bgImg.onload = function () {
            const bgCanvas = createCanvas(bgImg.width, bgImg.height);
            var bgCtx = bgCanvas.getContext('2d');
            // bgCtx.rect(0, 0, bgCanvas.width, bgCanvas.height);
            // bgCtx.fillStyle = "#fff";
            // bgCtx.fill();
            bgCtx.drawImage(bgImg, 0, 0, bgImg.width, bgImg.height);

            for (let index = 0; index < coor.length; index++) {
                const item = coor[index];
                bgCtx.strokeStyle = echartsColor[index]
                bgCtx.lineWidth = 4;
                bgCtx.strokeRect(...item);
            }
            // echarts宽高,可以根据bgCanvas动态计算宽高,这里先固定写
            var lineChartWidth = 100
            var lineChartHeight = 100

            var lineChartXY = [bgImg.width - lineChartWidth, 0]

            // 生成echarts折线图
            var option = ECHARTS_LINE(chartData);
            const chartCanvas = createCanvas(lineChartWidth, lineChartHeight);
            var chartCtx = chartCanvas.getContext('2d');
            // chartCtx.rect(0, 0, chartCanvas.width, chartCanvas.height);
            // chartCtx.fillStyle = "#fff";
            // chartCtx.fill();
            const chart = echarts.init(chartCanvas);
            chart.setOption(option);

            chartCanvas.toBuffer(function (err, buf) {
                if (err) {
                    return res.send({ msg: "Diagram generation failed" })
                } else {
                    var chartImg = new Image()
                    chartImg.onload = function () {
                        // 1492 826 1092 526
                        // console.log(bgCanvas.width, bgCanvas.height, "bgCanvas")
                        // console.log(chartCanvas.width, chartCanvas.height, "chartCanvas")
                        // console.log(bgCanvas.width - chartCanvas.width, bgCanvas.height - chartCanvas.height, "xy")

                        // res.writeHead(200, {
                        //     'Content-Type': 'image/png'
                        // })
                        bgCtx.drawImage(chartImg, ...lineChartXY, lineChartWidth, lineChartHeight)
                        // res.write(bgCanvas.toBuffer('image/png'))
                        // console.log(bgCanvas.toDataURL())
                        // bgCanvas.toBuffer('image/png')
                        resultData.status = 200
                        resultData.data = bgCanvas.toDataURL()
                        resultData.msg = "success"

                        return res.send(resultData)


                    }
                    chartImg.onerror = function () {
                        resultData.status = 500;
                        resultData.msg = "Diagram loading failed";

                        return res.send(resultData);
                    }
                    chartImg.src = buf
                }
            })
        }
        bgImg.onerror = function () {
            resultData.status = 500;
            resultData.msg = "Image loading failed";
            return res.send(resultData);

        }
        bgImg.src = imgUrl
    } else {
        resultData.data = "";
        resultData.status = 400;
        resultData.msg = "必传字段不可为空";
        res.send(resultData);
        res.end();
    }
});

module.exports = router;

测试接口

let params = {
//图片地址
  imgUrl: "http://localhost:4060/images/shujia.jpg",
  //画框对角线坐标
  imgCoor: [
    { origin: "100,100", target: "150,130" },
    { origin: "180,150", target: "210,190" },
  ],
  //折线图数据
  chartData: [
    { name: "2023-06-10", value: "15" },
    { name: "2023-06-12", value: "30" },
    { name: "2023-06-13", value: "40" },
    { name: "2023-06-14", value: "20" },
    { name: "2023-06-15", value: "22" },
    { name: "2023-06-16", value: "15" },
    { name: "2023-06-17", value: "20" },
  ],
};

mergeImage(params).then((res) => {
 console.log(res)
});