vue动态加载SVG文件并修改节点数据的操作代码
先上一个马赛克图片叭。
接领导需求,动态实现电路图, 并附带放大、缩小功能、 以及不同的回路点击能弹窗显示相关节点的更多信息,
通俗一点讲: 随着用户点击放大和缩小, 点击位置保持不变,而且能实现点击交互。
初接触的时候,觉得根本没法下手呀,说说自己的思路叭,
- 从随着用户点击放大缩小位置不变,想到了SVG 但是需要动态加载进来呀,而且还需要需求不同节点的电流值,
- 从放大缩小来看, 首先想到的是 D3
- 在集合领导给的部分相关资料
- 综上: 进行了可行性的方案试探,也完成了整个功能的开发。
且听我细细道来开发遇到的问题,以及怎处理叭
- SVG 在 谷歌, 以及 微软中国,拼命的搜索,搜索出来有2个适合的组件, 大多数搜索出来的都是SVG 图标,但是我这个需求是很大的图片呀, 那继续换思路,
- 那试着把关键字换成 ‘动态加载SVG 图片', 这样又查出来引入SVG 图片 可以通过 image、 Object、 embed 等等。 但是这个插入仅限于插入,并不能动态修改值, 那继续换思路
- 动态加载SVG ,发现可以通过XMLHttpRequest 请求然后 添加事件、以及重新渲染DOM 元素。
那先上一段代码
```javascript const xhr = new XMLHttpRequest(); xhr.open('GET', this.svgUrl, true); xhr.send(); /* 监听xhr对象 */ xhr.onreadystatechange = function () { if (xhr.readyState == 4 && xhr.status == 200) { console.log(xhr.responseXML,'xhr.responseXML---------') } }; xhr.addEventListener('load', () => { // console.log('load'); // console.log(xhr.response,'---svg4703') // /* 获取 dom */ // console.log(xhr, xhr.responseXML,'xhr.responseXML') const resXML = stringToXml(xhr.response); this.svgDom = resXML.documentElement.cloneNode(true); //this.svgDom = resXML /* 添加事件(点击事件,鼠标滚轮事件,全屏事件) */ this.addEvents(); /* dom重置 */ this.resetDom(); /* 将svgDom对象转换成vue的虚拟dom */ var oSerializer = new XMLSerializer(); var sXML = oSerializer.serializeToString(this.svgDom); var Profile = Vue.extend({ template: "<div id='svgTemplate'>" + sXML + '</div>' }); // 创建实例,并挂载到元素上 new Profile().$mount('#svgTemplate'); });
好,我们继续。
既然是要根据不同的用户方,显示出来不同的模板,那么肯定是需要远程动态加载, 于是自己丢了一个模板到前端静态服务器上,就开始对XMLHTTP的load 事件之后对代码进行解析。
4.加载之后,发现又遇到一个问题了, 跨域
跨域是老生常谈的问题了,但是普通的请求我可以找z后端设置CORS 的允许投,那一个SVG 我表示 根本没法下手呀, 于是我换了个思路,我们先跨域跨域,本地装个插件如何, 最后把文件放在前端的服务器不就解决了,
然后我就真的傻傻的这样完成了,开发以及跟后端讨论的过程。
5.项目经理在继续和我沟通, 这个SVG 模板需要客户进去上传,也就是说,svg 文件需要专门上传到OSS 的文件服务器上,那么我想到的第一个问题是,肯定会跨域呀, 这可咋办呢, 急死我了,5555555…
6.当我把不同域名的SVG 文件通过XMLHTTP 引入的时候,发现SVG 图片根本显示不了,我不停的去切换2个文件地址。我尝试着百度、google 发现都没找到合适的解决方案咋办呢, 又不能告诉项目经理说,你花了1星期的预演,之前说可以的,现在突然不行了。
7.于是我看下浏览器报错在从2个方面出发。试着打印了 xhr.response
- 对比不显示的代码 跟显示的代码的差异点在哪里,我谷歌xml 转为 html
- 打印xhr.response 的时候,发现咦 其实
xhr.response
其实是有值返回的,也就是说 其实是返回了值, 不显示是因为
xhr.responseXML 这个值为null,
然后 `
resXML.documentElement.cloneNode(true);
`
没办法显示,抛错了。后续所有的操作都获取不到任何节点。
8.然后我开始试着找怎么从XML 转为HTML. 并且还发现真的有方法耶
//将字符串转化成dom对象;string转换为xml function stringToXml(xmlString) { var xmlDoc; if (typeof xmlString == "string") { //FF if (document.implementation.createDocument) { var parser = new DOMParser(); xmlDoc = parser.parseFromString(xmlString, "text/xml"); } else if (window.ActiveXObject) { xmlDoc = new ActiveXObject("Microsoft.XMLDOM"); xmlDoc.async = false; xmlDoc.loadXML(xmlString); } } else { xmlDoc = xmlString; } return xmlDoc; } addEvents() { console.log('这里自己写处理代码哈') }, resetDom() { console.log('这里写需要改变的节点的id 对应的值喽') },
整体SVG 动态加载的方案就这样了,
参考: https://www.jb51.net/article/193416.htm
我们在来看D3 , D3的话,
就简单粗暴一点 直接搜索vue D3 引用,
这就不详细说明了, 需求还是蛮多的。
还有遇到一个问题,就是全屏弹窗, 结合elementUI 使用的, 操作的时候发现点击事件触发了,但是弹窗并不现实,
我刚开始以为是因为 z-index 的层级不够高,然后我尝试着浏览器动态调试,z-index: 99999999. 写了一大串的9 都不显示。
原来是需要全屏显示的时候, 都需要包含在全屏的那个DIV 。
总结