123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659 |
- <template>
- <div :id="maxContainerId" class="graphParentContainer"></div>
- </template>
- <script>
- import * as PIXI from "pixi.js";
- export default {
- props: [],
- data() {
- return {
- maxContainerId:
- new Date().getTime().toString() +
- "_" +
- Math.random().toString().substring(2),
- /*
- * 设备数组格式如下
- * {
- id: "",
- name:'',
- legendObj: {
- content: "图的地址或base64串",
- size: {
- width: 12,
- height: 12,
- },
- },
- //设备的绝对位置
- absolutePosition: {
- x: 1,
- y: 1,
- },
- anchorArr:[{
- name:'锚点名称',
- //锚点相对于设备图例的位置
- relativePosition:{
- x:1,y:1
- }
- }]
- }
- */
- nodeArr: [],
- /**
- * 线数组,格式如下:
- * lines:[{
- id: '',
- path:[{x:1,y:1}],
- style: {
- lineWidth: 4, //线宽
- lineColor: '#8D9399', //线的颜色
- }
- }]
- */
- lineArr: [],
- /**
- * 文本数组,格式如下:
- * labels: [{
- id:'',
- text:'',
- style:{
- color: '',
- fontSize: '',
- backGround:'',
- fontWeight:1
- },
- //文本的绝对位置
- absolutePosition: {
- x: 1,
- y: 1,
- },
- }]
- */
- labelArr: [],
- //存放图形用的一些信息点
- canvasProObj: {
- //基于canvas画布的中心点坐标,需要动态计算得到
- centerPointerPx: { x: 0, y: 0 },
- //原始缩放比例,动态计算得到的,因为图形默认显示时是要适应容器大小的,所以相比原始坐标会有比例变化
- originalScale: 1,
- //x轴差值,通过此值让x轴左右两端对称
- xJiaValue: 0,
- //y轴差值,通过此值让y轴上下两端对称
- yJiaValue: 0,
- //缓存纹理对象
- textureSource: {},
- //缩放系数
- pixiScale: 1,
- //原点X坐标
- positionPxX: 0,
- //原点Y坐标
- positionPxY: 0,
- //缩放时每次变化的递进值
- deep: 0.1,
- //鼠标状态,0 普通状态;-1 鼠标按下
- mouseState: 0,
- //上一个鼠标位置信息
- prevMousePosition: {},
- },
- };
- },
- computed: {},
- methods: {
- //绘图入口
- drawEntry: function (graphInfo) {
- if (!graphInfo) return;
- //把原始数据转为易于绘图的数据
- var graphDataObj = this.parseDataToDraw(graphInfo);
- this.nodeArr = graphDataObj.nodeArr;
- this.lineArr = graphDataObj.lineArr;
- this.labelArr = graphDataObj.labelArr;
- this.drawGraph();
- },
- //重绘
- resetDraw: function () {
- this.drawGraph();
- },
- //绘图
- drawGraph: function () {
- var _this = this;
- // let type = "WebGL";
- // if (!PIXI.utils.isWebGLSupported()) {
- // type = "canvas";
- // }
- var pixiContainer = document.getElementById(this.maxContainerId);
- //--------------------------------------------创建舞台
- //存在舞台时,先清空舞台内的所有元素
- if (!!_this.pixiApp) {
- while (_this.pixiApp.stage.children.length > 0) {
- _this.pixiApp.stage.removeChildAt(0);
- }
- }
- var pixiApp;
- if (_this.pixiApp == null) {
- pixiApp = new PIXI.Application({
- width: pixiContainer.clientWidth, // default: 800 宽度
- height: pixiContainer.clientHeight, // default: 600 高度
- antialias: true, // default: false 反锯齿
- transparent: true, // default: false 透明度
- resolution: 1, // default: 1 分辨率
- //backgroundColor: "0xffffff",
- });
- pixiApp.renderer.autoResize = true;
- pixiContainer.appendChild(pixiApp.view);
- _this.pixiApp = pixiApp;
- } else pixiApp = _this.pixiApp;
- _this.computeOriginZuobPosition();
- //图例
- _this.nodeArr.forEach((_dataObj) => {
- //2022.1.30版,图例用矩形框代替
- var graphicsRect = new PIXI.Graphics();
- graphicsRect.lineStyle({
- width: 1,
- color: "0x0091ff",
- alpha: 1,
- alignment: 0.5,
- });
- let newCordObj = _this.convertCoordToLeftOrigina({
- x: _dataObj.absolutePosition.x,
- y: _dataObj.absolutePosition.y,
- });
- var newWidth =
- _dataObj.legendObj.size.width * _this.canvasProObj.originalScale;
- var newHeight =
- _dataObj.legendObj.size.height * _this.canvasProObj.originalScale;
- graphicsRect.drawRect(newCordObj.x, newCordObj.y, newWidth, newHeight);
- //设置点击范围,不然click事件不会触发
- graphicsRect.hitArea = graphicsRect.getBounds();
- graphicsRect.name = _dataObj.id;
- //启用事件
- graphicsRect.interactive = true;
- graphicsRect.on("click", (event) => {
- _this.clickEventCall(event, 1);
- });
- pixiApp.stage.addChild(graphicsRect);
- return;
- var _legendObj = _dataObj.legendObj;
- // 创建一个纹理
- if (!_this.canvasProObj.textureSource[_legendObj.image.content]) {
- _this.canvasProObj.textureSource[_legendObj.image.content] =
- PIXI.Texture.from(_legendObj.image.content);
- }
- //加载纹理
- var spriteInstanceCoord = _this.convertCoordToLeftOrigina({
- x: _dataObj.position.x,
- y: _dataObj.position.y,
- });
- var spriteWidth =
- _legendObj.defaultSize.width * _this.canvasProObj.originalScale;
- var spriteHeight =
- _legendObj.defaultSize.height * _this.canvasProObj.originalScale;
- var legendSprite = new PIXI.Sprite(
- _this.canvasProObj.textureSource[_legendObj.image.content]
- );
- legendSprite.x = spriteInstanceCoord.x;
- legendSprite.y = spriteInstanceCoord.y;
- legendSprite.width = spriteWidth;
- legendSprite.height = spriteHeight;
- pixiApp.stage.addChild(legendSprite);
- });
- //文本
- _this.labelArr.forEach((_labelObj) => {
- var pixiTextStyle = new PIXI.TextStyle({
- fill: _labelObj.style.color,
- fontSize: _labelObj.style.fontSize,
- fontWeight: _labelObj.style.fontWeight,
- // stroke: "#ffffff",
- // strokeThickness: _this.defaultStyle.text.fontStrokeWidth,
- });
- var textInstance = new PIXI.Text(_labelObj.text, pixiTextStyle);
- textInstance.name = _labelObj.id;
- let newCordObj = _this.convertCoordToLeftOrigina({
- x: _labelObj.absolutePosition.x,
- y: _labelObj.absolutePosition.y,
- });
- textInstance.position.set(newCordObj.x, newCordObj.y);
- // console.log(textInstance.width, "-", textInstance.height);
- //启用事件
- textInstance.interactive = true;
- textInstance.on("click", (event) => {
- _this.clickEventCall(event, 3);
- });
- pixiApp.stage.addChild(textInstance);
- });
- //画线
- _this.lineArr.forEach((_cLine) => {
- var lineColor = "0x" + _cLine.style.lineColor.substring(1);
- var lineInstance = new PIXI.Graphics();
- lineInstance.lineStyle(_cLine.style.lineWidth, lineColor, 1, 0, false);
- var _newStart = _this.convertCoordToLeftOrigina({
- x: _cLine.path[0].x,
- y: _cLine.path[0].y,
- });
- var _newEnd = _this.convertCoordToLeftOrigina({
- x: _cLine.path[1].x,
- y: _cLine.path[1].y,
- });
- lineInstance.moveTo(_newStart.x, _newStart.y);
- lineInstance.lineTo(_newEnd.x, _newEnd.y);
- pixiApp.stage.addChild(lineInstance);
- });
- },
- /**
- * 解析数据成易于图形使用的格式
- */
- parseDataToDraw: function (graphInfo) {
- var nodeArr = [];
- //线数组
- var lineArr = [];
- //文本数组
- var labelArr = [];
- if (graphInfo.template) {
- var rootContainerArr = graphInfo.template.frame.children;
- parseNode(rootContainerArr, graphInfo.template.frame.location);
- parseLine(graphInfo.lines || []);
- parseMainPies(graphInfo.template.mainPipes);
- }
- return {
- nodeArr,
- lineArr,
- labelArr,
- };
- /**
- * 递归解析设备(node)、文本(label)
- * dataArr 参照后台接口返回的diagram对象里的graphInfo.template.frame.children
- * absolutePosition 绝对位置{x:1,y:1} 其值是compType为"container"的每一级的location相加得出的值
- */
- function parseNode(dataArr, absolutePosition) {
- dataArr.forEach((_dataObj) => {
- var newAbsolutePosition = {
- x: _dataObj.location.x + absolutePosition.x,
- y: _dataObj.location.y + absolutePosition.y,
- };
- //文本
- if (_dataObj.label && _dataObj.label.content) {
- let _labelObj = _dataObj.label;
- _labelObj.id = _labelObj.id || Math.random().toString();
- let _style = _labelObj.style || {};
- _style.color =
- _style.color ||
- window.__systemConf.systemGraph.peiDian.text.color;
- _style.fontSize =
- _style.fontSize ||
- window.__systemConf.systemGraph.peiDian.text.size;
- _style.backGround =
- _style.backGround ||
- window.__systemConf.systemGraph.peiDian.text.backGround;
- _style.fontWeight =
- _style.fontWeight ||
- window.__systemConf.systemGraph.peiDian.text.weight;
- var textNewAbsolutePosition = {
- x: _labelObj.location.x + newAbsolutePosition.x,
- y: _labelObj.location.y + newAbsolutePosition.y,
- };
- labelArr.push({
- id: _labelObj.id,
- text: _labelObj.content,
- style: {
- color: _style.color,
- fontSize: _style.fontSize,
- backGround: _style.backGround,
- fontWeight: _style.fontWeight,
- },
- //文本的绝对位置
- absolutePosition: textNewAbsolutePosition,
- });
- _labelObj.style = _style;
- _dataObj.label = _labelObj;
- }
- switch (_dataObj.compType) {
- //容器
- case "container":
- parseNode(_dataObj.children, newAbsolutePosition);
- break;
- //设备
- case "equipmentNode":
- let anchorArr = [];
- let anchorKeys = Object.keys(_dataObj.anchorLocations || {});
- anchorKeys.forEach((_anchorKey) => {
- anchorArr.push({
- name: _anchorKey,
- //锚点相对于设备图例的位置
- relativePosition: _dataObj.anchorLocations[_anchorKey],
- });
- });
- nodeArr.push({
- id: _dataObj.id,
- name: _dataObj.dataObject.localName,
- //2022.1.30版本图例以矩形框代替
- legendObj: {
- content: "图的地址或base64串",
- size: {
- width: _dataObj.size.x,
- height: _dataObj.size.y,
- },
- },
- //设备的绝对位置
- absolutePosition: newAbsolutePosition,
- anchorArr: anchorArr,
- });
- break;
- }
- });
- }
- /**
- * 解析线
- */
- function parseLine(lines) {
- lines.forEach((_line) => {
- if (_line.flag != "duplicate") {
- _line.id = _line.id || _line.dataObjectId;
- let lineDefaultStyle = window.__systemConf.systemGraph.peiDian.line;
- lineArr.push({
- id: _line.id,
- path: _line.locationPath,
- style: {
- lineWidth: lineDefaultStyle.width,
- lineColor: lineDefaultStyle.color,
- },
- });
- }
- });
- }
- /**
- * 解析干管,当做线处理
- */
- function parseMainPies(_dataArr) {
- _dataArr.forEach((_data) => {
- _data.id = _data.id || _data.dataObjectId;
- let lineDefaultStyle = window.__systemConf.systemGraph.peiDian.line;
- var pathArr = eachPathArr(_data.locationPath || [], []);
- lineArr.push({
- id: _data.id,
- path: pathArr,
- style: {
- lineWidth: lineDefaultStyle.width,
- lineColor: lineDefaultStyle.color,
- },
- });
- });
- }
- /**
- * 递归解析path数组
- */
- function eachPathArr(_arr, returnArr) {
- _arr.forEach((_data) => {
- if (_data instanceof Array) {
- eachPathArr(_data, returnArr);
- } else
- returnArr.push({
- x: _data.x,
- y: _data.y,
- });
- });
- return returnArr;
- }
- },
- /**
- * 1、X轴的原始坐标两端可能是不对称的;Y轴的原始坐标两端也可能是不对称的,所以计算各自的差值,以便把两端对称起来,以此把图形显示到正中间
- * 2、计算原始坐标值的缩放系数,因为原始坐标和像素坐标是不对应的,所以要根据最大原始坐标(每一个坐标的绝对值进行比较)和最大像素坐标做比率缩放
- * 3、计算中心点像素坐标
- */
- computeOriginZuobPosition: function () {
- var _this = this;
- var pixiContainer = document.getElementById(this.maxContainerId);
- var maxPxX = pixiContainer.clientWidth;
- var maxPxY = pixiContainer.clientHeight;
- //基于canvas画布的中心点坐标
- _this.canvasProObj.centerPointerPx = {
- x: maxPxX / 2,
- y: maxPxY / 2,
- };
- var originaMinX = null, //原始最小坐标X
- originaMaxX = null, //原始最大坐标X
- originaMinY = null, //原始最小坐标Y
- originaMaxY = null; //原始最大坐标Y
- /**
- * 取得X轴的原始最小坐标和最大坐标、
- * 取得Y轴的原始最小坐标和最大坐标、
- */
- compute();
- //计算X轴差值
- var xJiaValue = (maxPxX - originaMaxX - originaMinX) / 2;
- xJiaValue = originaMinX + xJiaValue < 0 ? 0 - originaMinX : xJiaValue;
- //计算Y轴差值
- var yJiaValue = (maxPxY - originaMaxY - originaMinY) / 2;
- yJiaValue = originaMinY + yJiaValue < 0 ? 0 - originaMinY : yJiaValue;
- _this.canvasProObj.xJiaValue = xJiaValue;
- _this.canvasProObj.yJiaValue = yJiaValue;
- //让每一个的X、Y坐标加上差值,改为在画图时再加差值,这样就减少了此处的循环
- setScale();
- /**
- * 取得X轴的原始最小坐标和最大坐标、
- * 取得Y轴的原始最小坐标和最大坐标、
- */
- function compute() {
- _this.nodeArr.forEach((_c) => {
- var _legendObj = _c.legendObj;
- originaMinX =
- originaMinX == null
- ? _c.absolutePosition.x
- : Math.min(originaMinX, _c.absolutePosition.x);
- originaMaxX =
- originaMaxX == null
- ? _c.absolutePosition.x + _legendObj.size.width
- : Math.max(
- originaMaxX,
- _c.absolutePosition.x + _legendObj.size.width
- );
- originaMinY =
- originaMinY == null ? _c.absolutePosition.y : Math.min(originaMinY);
- originaMaxY =
- originaMaxY == null
- ? _c.absolutePosition.y + _legendObj.size.height
- : Math.max(
- originaMaxY,
- _c.absolutePosition.y + _legendObj.size.height
- );
- });
- _this.lineArr.forEach((_cLine) => {
- _cLine.path.forEach((_c) => {
- originaMinX =
- originaMinX == null ? _c.x : Math.min(originaMinX, _c.x);
- originaMaxX =
- originaMaxX == null ? _c.x : Math.max(originaMaxX, _c.x);
- originaMinY =
- originaMinY == null ? _c.y : Math.min(originaMinY, _c.y);
- originaMaxY =
- originaMaxY == null ? _c.y : Math.max(originaMaxY, _c.y);
- });
- });
- }
- //计算缩放系数
- function setScale() {
- var xScale = maxPxX / (originaMaxX + xJiaValue);
- var yScale = maxPxY / (originaMaxY + yJiaValue);
- _this.canvasProObj.originalScale = Math.min(xScale, yScale);
- //当图形区域能显示全的时候不再进行缩放
- _this.canvasProObj.originalScale = Math.min(
- 1,
- _this.canvasProObj.originalScale
- );
- }
- },
- /**
- * 把坐标转为缩放后的坐标
- * _obj:{x:1,y:1}
- */
- convertCoordToLeftOrigina: function (_obj) {
- _obj.x =
- (_obj.x + this.canvasProObj.xJiaValue) *
- this.canvasProObj.originalScale;
- _obj.y =
- (_obj.y + this.canvasProObj.yJiaValue) *
- this.canvasProObj.originalScale;
- return _obj;
- },
- /**
- * 设备、线、文本点击事件的回调
- * type 1 设备; 2 线; 3 文本
- */
- clickEventCall: function (event, type) {
- var graphInstance = event.target || event.currentTarget;
- var dataId = graphInstance.name;
- var dataArr =
- type == 1
- ? this.nodeArr
- : type == 2
- ? this.lineArr
- : type == 3
- ? this.labelArr
- : [];
- var dataObj = dataArr.filter((_data) => {
- return _data.id == dataId;
- })[0];
- this.$emit("click", dataObj, type);
- },
- //图形缩放比例递进计算
- graphScaleCompute: function (deepVal) {
- return this.canvasProObj.pixiScale + deepVal;
- },
- //图形缩放
- graphScale: function (newScale) {
- newScale = Math.min(5, newScale);
- newScale = Math.max(0.25, newScale);
- //设置X、Y轴的缩放系数
- this.pixiApp.stage.scale.x = newScale;
- this.pixiApp.stage.scale.y = newScale;
- //保存缩放系数
- this.canvasProObj.pixiScale = newScale;
- //重新计算原点
- this.canvasProObj.positionPxX =
- (1 - newScale) * this.canvasProObj.centerPointerPx.x;
- this.canvasProObj.positionPxY =
- (1 - newScale) * this.canvasProObj.centerPointerPx.y;
- this.pixiApp.stage.position.set(
- this.canvasProObj.positionPxX,
- this.canvasProObj.positionPxY
- );
- },
- /**
- * 从舞台上移出元素:图例、线、文本
- * id 数据ID,即元素的name
- * type 1 设备; 2 线; 3 文本
- */
- /**
- * 更新graphInfo中的数据
- * dataType 1 设备; 2 线; 3 文本
- * operType 1 移除设备节点; 2 更新文本字体大小; 3 更新文本颜色; 4 更新文本背景色; 5 移除文本节点
- * exprObj 扩展数据,支持:{id:'数据id',fontSize:'字体大小',fontColor:'文本颜色',fontBackColor:'文本背景色'}
- */
- updateDataAndGraph: function (dataType, operType, exprObj) {
- // fill: _labelObj.style.color,
- // fontSize: _labelObj.style.fontSize,
- // fontWeight: _labelObj.style.fontWeight,
- var dataArr =
- dataType == 1
- ? this.nodeArr
- : dataType == 2
- ? this.lineArr
- : dataType == 3
- ? this.labelArr
- : [];
- for (let i = 0; i < dataArr.length; i++) {
- let _dataObj = dataArr[i];
- if (_dataObj.id == exprObj.id) {
- var stageChild = this.pixiApp.stage.getChildByName(exprObj.id);
- var _stageStyle = stageChild.style;
- switch (operType) {
- case 1:
- case 5:
- dataArr.splice(i, 1);
- this.pixiApp.stage.removeChild(stageChild);
- break;
- case 2:
- _dataObj.style.fontSize = exprObj.fontSize;
- _stageStyle.fontSize = exprObj.fontSize;
- stageChild.style = _stageStyle;
- break;
- case 3:
- _dataObj.style.color = exprObj.fontColor;
- _stageStyle.fill = exprObj.fontColor;
- stageChild.style = _stageStyle;
- break;
- case 4:
- _dataObj.style.backGround = exprObj.fontBackColor;
- // _stageStyle.fontSize = exprObj.fontSize;
- // stageChild.style = _stageStyle;
- break;
- }
- break;
- }
- }
- },
- },
- created() {},
- mounted() {},
- components: {},
- };
- </script>
- <style scoped>
- .graphParentContainer {
- width: 100%;
- height: 100%;
- background: #ffffff;
- }
- </style>
|