仍然是 在图片上特定区域根据数值显示不同的颜色 的需求。拖了这么久,最终的解决方案终于定下来了:使用aoi检测设备导出的坐标来标定需显示数值和颜色的区域,如此一来就不需要人操作UI界面来标定数值的显示区域。
最终使用echarts显示的方法有2种:
- 地图map+使用坐标标记区域且区域有name属性的svg,根据区域的name属性显示对应的值
- 图片直接转成svg图作为地图+地理坐标系+散点图
地图+区域有name属性的svg
先将背景图放入images文件夹中,利用上传的xlsx文件中的坐标标记背景图的区域,最后打包下载svg图和背景图。
<script src="../plugins/fabric.min.js"></script>
<script src="../plugins/jquery/jquery-3.3.1.js"></script>
<script type="text/javascript" src="../plugins/jszip.min.js"></script>
<script src="../plugins/FileSaver.js"></script>
<script src="../plugins/xlsx.full.min.js"></script>
<input type="file" name="file" id="uploadFile" size="10" onchange="readFile(this);" />
<canvas id="example" style="border: solid 1px #ccc"></canvas>
<script>
function handleSvg(data) {
const img_folder_path = "../images/";
const local_url = "http://localhost:3000/html-demos/images/";
const replace_url = encodeURI(`http://localhost:3000/html-demos/images/`);
console.log(data);
Promise.all(
Object.keys(data)
.map((filename, idx) => {
return new Promise((resolve, reject) => {
const img_path = img_folder_path + filename + ".jpg";
fabric.Image.fromURL(img_path, (bg_img) => {
const canvas = new fabric.StaticCanvas("example");
canvas.setBackgroundImage(bg_img, canvas.renderAll.bind(canvas));
canvas.setWidth(bg_img.width);
canvas.setHeight(bg_img.height);
const coord_arr = data[filename]["coords"];
const name_arr = data[filename]["names"];
const area_width = 20;
const area_height = 20;
for (let i in coord_arr) {
const rect = new fabric.Rect({
left: coord_arr[i][0] - area_width / 2,
top: coord_arr[i][1] - area_height / 2,
originX: "left",
originY: "top",
width: area_width,
height: area_height,
fill: "rgba(255, 0, 0, 1)",
});
rect.toSVG = (function (toSVG) {
return function () {
const svgString = toSVG.call(this);
const domParser = new DOMParser();
const doc = domParser.parseFromString(svgString, "image/svg+xml");
let type = this.type;
const parentG = doc.querySelector(`${type}`);
parentG.setAttribute("name", name_arr[i]);
return doc.documentElement.outerHTML;
};
})(rect.toSVG);
canvas.add(rect);
}
const svgString = canvas.toSVG(null, function (svg) {
return svg.replace(local_url, replace_url);
});
resolve({ [filename + ".svg"]: svgString });
});
});
})
.concat(
Object.keys(data).map((filename, idx) => {
return new Promise((resolve, reject) => {
const img = new Image();
img.crossOrigin = "Anonymous"; // 图片可以跨域访问
img.onload = () => {
const canvas = document.createElement("canvas");
canvas.width = img.width;
canvas.height = img.height;
const ctx = canvas.getContext("2d");
ctx.drawImage(img, 0, 0);
canvas.toBlob((blob) => {
resolve({ [filename + ".jpg"]: blob });
});
};
img.onerror = reject;
img.src = img_folder_path + filename + ".jpg";
});
})
)
)
.then((images) => {
const zip = new JSZip();
images.forEach((image) => {
Object.keys(image).forEach((key) => {
zip.file(key, image[key]);
});
});
zip.generateAsync({ type: "blob" }).then((blob) => {
saveAs(blob, "svg.zip");
});
})
.catch((err) => {
console.error("Error packaging images:", err);
});
}
function readFile() {
const file = $("#uploadFile")[0].files[0];
const fileTypes = ["application/vnd.ms-excel", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"];
if (fileTypes.indexOf(file.type) === -1) {
alert("文件类型错误");
return;
}
const reader = new FileReader();
const data_res = {};
reader.onload = function (e) {
const data = e.target.result;
const workbook = XLSX.read(data, { type: "binary", cellDates: true });
const sheetNames = workbook.SheetNames;
const worksheet = workbook.Sheets[sheetNames[0]];
const res = XLSX.utils.sheet_to_json(worksheet);
for (let i in res) {
const values = Object.values(res[i]);
if (values.length < 4) continue;
// const key = values[0] + "_" + values[1];
const key = values[0];
if (key in data_res) {
data_res[key]["coords"].push(values[3].split(","));
data_res[key]["names"].push(values[2]);
} else {
data_res[key] = {
coords: [values[3].split(",")],
names: [values[2]],
};
}
}
handleSvg(data_res);
};
reader.readAsBinaryString(file);
}
</script>
文件格式如下:
生成的zip文件解压如下:
使用echarts加载svg图并填入数值
<script src="../plugins/echarts.min.v5.4.2.js"></script>
<script src="../plugins/jquery/jquery-3.3.1.js"></script>
<body>
<div id="mapChart" style="height: 400px"></div>
<script>
function drawChart() {
var chartDom = document.getElementById("mapChart");
var myChart = echarts.init(chartDom);
var option;
$.get("../images/a.svg", function (svg) {
echarts.registerMap("svg_map", { svg: svg });
option = {
tooltip: {},
visualMap: {
left: "left",
top: 10,
min: 0,
max: 100,
orient: "horizontal",
text: ["", "Value"],
realtime: true,
calculable: true,
inRange: {
color: ["#0732FC", "#F92606"],
},
},
series: [
{
name: "Value",
type: "map",
map: "svg_map",
roam: true,
emphasis: {
label: {
show: false,
},
},
selectedMode: false,
data: [{ name: "aa", value: 95 }],
},
],
};
myChart.setOption(option);
});
option && myChart.setOption(option);
}
drawChart();
</script>
</body>
svg图+地理坐标系+散点图
<script src="../plugins/fabric.min.js"></script>
<script src="../plugins/jquery/jquery-3.3.1.js"></script>
<script type="text/javascript" src="../plugins/jszip.min.js"></script>
<script src="../plugins/FileSaver.js"></script>
<canvas id="example" style="border: solid 1px #ccc"></canvas>
<script>
function handleSvg(data) {
const img_folder_path = "../images/";
const local_url = "http://localhost:3000/html-demos/images/";
const replace_url = encodeURI(`http://localhost:3000/html-demos/images/`);
console.log(data);
Promise.all(
data
.map((filename, idx) => {
return new Promise((resolve, reject) => {
const img_path = img_folder_path + filename + ".jpg";
fabric.Image.fromURL(img_path, (bg_img) => {
const canvas = new fabric.StaticCanvas("example");
canvas.setBackgroundImage(bg_img, canvas.renderAll.bind(canvas));
canvas.setWidth(bg_img.width);
canvas.setHeight(bg_img.height);
const svgString = canvas.toSVG(null, function (svg) {
return svg.replace(local_url, replace_url);
});
resolve({ [filename + ".svg"]: svgString });
});
});
})
.concat(
data.map((filename, idx) => {
return new Promise((resolve, reject) => {
const img = new Image();
img.crossOrigin = "Anonymous"; // 图片可以跨域访问
img.onload = () => {
const canvas = document.createElement("canvas");
canvas.width = img.width;
canvas.height = img.height;
const ctx = canvas.getContext("2d");
ctx.drawImage(img, 0, 0);
canvas.toBlob((blob) => {
resolve({ [filename + ".jpg"]: blob });
});
};
img.onerror = reject;
img.src = img_folder_path + filename + ".jpg";
});
})
)
)
.then((images) => {
const zip = new JSZip();
images.forEach((image) => {
Object.keys(image).forEach((key) => {
zip.file(key, image[key]);
});
});
zip.generateAsync({ type: "blob" }).then((blob) => {
saveAs(blob, "svg.zip");
});
})
.catch((err) => {
console.error("Error packaging images:", err);
});
}
handleSvg(["a", "b"]);
</script>
使用echarts的geo坐标系显示数值区域
<script src="../plugins/echarts.min.js"></script>
<script src="../plugins/jquery/jquery-3.6.0.min.js"></script>
<div style="width: 100%; height: 500px" id="main"></div>
<script>
const ROOT_PATH = "http://localhost:3000/html-demos/images/";
const chartDom = document.getElementById("main");
const myChart = echarts.init(chartDom);
let option;
$.get(ROOT_PATH + "b.svg", function (svg) {
echarts.registerMap("svg_map", { svg: svg });
option = {
tooltip: {
formatter: (params) => {
return `${params.seriesName} <br/>
${params.marker}Value:${params.value[2]}`;
},
},
geo: {
tooltip: {
show: true,
},
map: "svg_map",
roam: true,
},
series: [
{
type: "scatter",
name: "aa",
coordinateSystem: "geo",
geoIndex: 0,
symbol: "rect",
symbolSize: 10,
itemStyle: {
color: "#b02a02",
},
encode: {
tooltip: 2,
},
data: [
[100, 200, 95],
[150, 150, 30],
],
},
],
};
myChart.setOption(option);
});
option && myChart.setOption(option);
</script>
总的来说:使用geo坐标系比较方便,在本例中的缺点需要自己计算颜色,且series中很多项(不同的数值显示不同颜色)。
另外需注意svg图的加载问题:svg图占用空间较小,svg图加载到页面上后,会继续加载svg文件中引用的图片,因此一定要保证图片可访问。
标签:canvas,const,fabric,img,svg,js,images,data From: https://www.cnblogs.com/caroline2016/p/18136311