基于 SVG 的图形交互方案实践

不知道从什么时候起,人们开始喜欢上数字大屏这种“花里胡哨”的东西,仿佛只要用上“科技蓝”这样神奇的色调,就可以让一家公司焕然一新,瞬间变得科技感满满。不管数字大屏的实际意义,是用来帮助企业监控和决策,还是为了方便领导参观和视察,抑或是为了向外界展示和宣传。总之,自从数字大屏诞生之后,它始终就没能摆脱其前任“中国式报表”那种大而全的宿命。追随着 ECharts、Superset、FineBI、DataEase 等数据可视化产品的身影一路走来,你会发现人们在追求“花里胡哨”这件事情上永无止境。如今的数据大屏,元素多(表格、视频、2D/2.5D/3D地图)、种类多(图表、报表、流程图)、媒介多(PC、平板、电视、LED),主打的就是一个眼花缭乱。

在这里插入图片描述

当数字大屏的这股时尚潮流涌向物联网和工业互联网领域以后,就不可避免地催生出像上面这样的“数字大屏”需求,请原谅我使用如此模糊的措辞,因为我实在难以给它一个准确的定义,工艺流程图、设备运行监控图、组态图、SCADA…。也许,这些名称不见得都能做到全面概括,可这些东西的确具备了数字大屏的特征,哪怕这些设备元件、管道阀门在科技蓝配色下违和感十足。作为一位低调的程序员,我一向不喜欢这种粉饰太平的面子工程,所以,当设计师同事带着设计图来找我时,我当时内心是拒绝的:

在这里插入图片描述

也许,此时你的内心深处会闪过一丝蔑视,认为这有什么难度呢?我只需要在图片上叠加若干个透明的 div,这样不就可以实现图片特定区域的交互逻辑啦!我承认,这是一个非常好的思路,但是在实践过程中你就会发现,div 的交互区域通常都是一个标准的矩形,而设计师同事常常使用圆角矩形和不规则图形来增强设计感。因此,在交互方面可能会存在一些缺陷,尤其是在 2.5D 的图片设计稿中,交互区域实际上是一个多边形。接下来,我将介绍一种基于 HTML5 图片热区特性来实现交互的思路:

<div class="container"><img src="Demo-01.jpg" usemap="#imageMap" style="width: 600px; height: 315px"><map name="imageMap"></map>
</div>

首先,准备一张图片以及一个 map 标签,并且这个 map 标签通过 usemap 属性与这张图片进行了关联。参照上面的示意图,我们定义了两个可交互的区域。其中,区域1是矩形区域,区域2是圆形区域:

const areas = [{key: '半泽直树',shape: 'rect',coords: [0, 0, 308.5, 315]
}, {key: '大和田',shape: 'circle',coords: [418, 134, 157.5]
}]

因为 area 标签需要搭配 map 标签来使用,所以,我们将通过下面的代码来动态地创建区域,同时为每个区域绑定相应的事件:

const popup = document.getElementById('popup');
const imageMap = document.getElementsByName('imageMap')[0];areas.forEach(area => {// 创建区域let ele = document.createElement('area');ele.shape = area.shape;ele.coords = area.coords.join(',')ele.setAttribute('data-key', area.key);// 绑定事件ele.onclick = function (e) {alert(e.target.dataset.key);};ele.onmousemove = function (e) {popup.innerHTML = `<div class="content">${e.target.dataset.key}</div>`;popup.style.left = `${e.x - 75}px`;popup.style.top = `${e.y - 45}px`;popup.style.display = 'block'};ele.onmouseover = function (e) {popup.style.display = 'display';};// 添加到map标签imageMap.appendChild(ele);
})

此时,当我们鼠标移动到指定的区域时,就可以触发对应的气泡提示,如下图所示:

在这里插入图片描述

这个方案相对于纯 div 标签的思路要稍微好上一点点,因为 area 标签里的 shape 属性支持多边形,这意味着不规则区域的交互可以继续进行下去。可这种方案,本质上并没有摆脱“手工标注”,你不得不为每一个区域标注好坐标,这对于没有设计感的程序员来说可能是一场折磨,更重要的是,一旦这个方案运用到数字大屏上面,你总要去解决屏幕尺寸变化、全屏/非全屏等一系列问题,显然,这个时候这些区域的坐标都需要重新计算。这个时候,博主就想到了 SVG 这种可缩放的矢量图形,这是一种自描述的标记语言,无论怎么缩放都不会失真。下面是一个简单的 SVG 图片示例,我们可以大致了解到其结构是一个 XML 文件:

在这里插入图片描述

既然 SVG 中本身就自带着描述位置的坐标信息,那么,我们是不是可以基于 SVG 来实现相应的交互逻辑呢?下面我们以一张中国地图为例来验证这个想法:

在这里插入图片描述

OK,我们希望实现什么样的交互效果呢?当鼠标移动到指定的省份时,该省份会变成红色高亮状态,并且会在鼠标位置触发气泡提示。具体怎么做呢?首先,我们来准备下面的 HTML 结构:

<div id="popup" class="rectangle" style="display: none;"></div>
<div class="container"></div>

接下来,我们通过脚本来加载 SVG 图片,同时为其绑定相关事件:

const popup = document.getElementById('popup');fetch('China.svg').then(res => res.text()).then(text => {const container = document.getElementsByClassName('container')[0];container.innerHTML = text;// 允许SVG交互const svg = document.getElementsByTagName('svg')[0];svg.setAttribute('pointer-events', 'cursor');// 为每一个路径绑定事件const paths = svg.childNodes[0].childNodes;for (var i = 0; i < paths.length; i++) {paths[i].onclick = function (e) {alert(`${e.target.id}`)};paths[i].onmouseover = function (e) {e.target.setAttribute('fill', 'red');popup.innerHTML = `<div class="content">${e.target.id}</div>`popup.style.left = `${e.x - 75}px`;popup.style.top = `${e.y - 45}px`;popup.style.display = 'block'};paths[i].onmouseout = function (e) {e.target.setAttribute('fill', '#eee');popup.style.display = 'none'};}})

在这个示例中,每一个省份对应着 SVG 中的一个 path 节点,因此,我们只需要在加载完 SVG 以后再去遍历这些节点即可。可能大家会有疑问,为什么这里不用 imgembed 或者 object 这些标签来承载一个 SVG 图形呢?因为这样我们是没有办法访问 svg 标签及其子节点的。现在,我们就可以看到下面的效果:

在这里插入图片描述

这两个方案,你更倾向于哪一个呢?从一个程序员的角度来看,我更喜欢使用 SVG,因为 XML 这种格式不管对人还是机器来说都非常友好。实际上,到目前为止,这篇博客里对方案可行性的探索业已完成,而在现实中,更多的挑战往往来自非技术因素。譬如,如何让设计师同事适应使用相对普通、朴素的矢量图格式。当然,从这篇文章的思路延伸出去,无论是复杂的数据大屏,还是布局编辑器/低代码、地图、流程图、工作流等问题,我们都无法摆脱 DOM、Canvas、WebGL、SVG 等知识体系。特别是当我们需要处理拖拽/平移、缩放、旋转等常规操作时,这些都是最具挑战性的部分,不是吗?

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/90623.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Hugo托管到Github Pages

Github通过其Github Pages服务可以user、project或organization提供免费快速的静态托管&#xff0c;同时使用Github Actions自动化开发工作流和构建。 1.创建Github仓库 可见性为public。 命名为username.github.io&#xff0c;username为你的Github用户名。 2.添加远程仓库…

chrono学习(一)

我想用chrono进行沙土的仿真&#xff0c;首先学习demo_GPU_ballCosim.cpp&#xff0c;这个例子仿真了一些沙土的沉降过程。 首先&#xff0c;运行编辑完成的文件demo_GPU_ballCosim&#xff1a; (base) eowyneowyn-MS-7D20:~/build_chrono/bin$ ./demo_GPU_ballCosim 运行完得…

电商项目part05 分布式ID服务实战

背景 日常开发中&#xff0c;需要对系统中的各种数据使用 ID 唯一表示&#xff0c;比如用户 ID 对应且仅对应一个人&#xff0c;商品 ID 对应且仅对应一件商品&#xff0c;订单 ID 对应且仅对应 一个订单。现实生活中也有各种 ID&#xff0c;比如身份证 ID 对应且仅对应一个人…

数字孪生智慧工厂:电缆厂 3D 可视化管控系统

近年来&#xff0c;我国各类器材制造业已经开始向数字化生产转型&#xff0c;使得生产流程变得更加精准高效。通过应用智能设备、物联网和大数据分析等技术&#xff0c;企业可以更好地监控生产线上的运行和质量情况&#xff0c;及时发现和解决问题&#xff0c;从而提高生产效率…

9个实用的交互设计软件,Get更简单的原型制作方式!

好用的原型图软件不仅可以快速可视化产品经理的想法&#xff0c;提高沟通效率&#xff0c;还可以加快测试进度&#xff0c;打造更真实的用户体验。今天本文为大家整理了9个好用的原型图工具&#xff0c;一起来看看吧&#xff01; 1、即时设计 在设计场景中&#xff0c;即时设…

【前端】CSS3新特性

目录 一、前言二、伪元素选择器1、选择器2、注意事项3、代码示例 三、伪元素清除浮动1、第一种伪元素清除浮动2、第二种伪元素清除浮动 四、CSS3盒子模型1、box-sizing&#xff1a;content-box2、box-sizing&#xff1a;border-box 五、CSS3图片模糊处理1、图片变模糊①、CSS3滤…

交换机端口安全实验

文章目录 一、实验的背景与目的二、实验拓扑三、实验需求四、实验解法1. PC配置IP地址部分2. 在SW1上开启802.1X身份验证3. 创建一个用户身份验证的用户。用户名为wangdaye&#xff0c;密码为1234564.创建一个端口隔离组&#xff0c;实现三台PC无法互相访问 摘要&#xff1a; 本…

ChatGPT⼊门到精通(5):ChatGPT 和Claude区别

⼀、Claude介绍 Claude是Anthropic开发的⼀款⼈⼯智能助⼿。 官⽅⽹站&#xff1a; ⼆、Claude能做什么 它可以通过⾃然语⾔与您进⾏交互,理解您的问题并作出回复。Claude的主要功能包括: 1、问答功能 Claude可以解答⼴泛的常识问题与知识问题。⽆论是历史上的某个事件,理科…

线性代数的学习和整理15:线性代数的快速方法

1 数学/线性代数里&#xff0c;其实很多东西的求得都有多种解决办法 很多概念&#xff0c;界定狠清晰&#xff0c;但是不好求 多种方法&#xff0c;拓宽思维 方法1&#xff1a;按定义直接去求解 方法2&#xff1a;按 2 比如求逆矩阵 概念方法&#xff0c;线性变化 增广矩阵…

Uniapp笔记(五)uniapp语法4

本章目标 授权登录【难点、重点】 条件编译【理解】 小程序分包【理解】 一、授权登录 我的模块其实是两个组件&#xff0c;一个是登录组件&#xff0c;一个是用户信息组件&#xff0c;根据用户的登录状态判断是否要显示那个组件 1、登录的基本布局 <template><…

【二等奖方案】大规模金融图数据中异常风险行为模式挖掘赛题「Aries」解题思路

第十届CCF大数据与计算智能大赛&#xff08;2022 CCF BDCI&#xff09;已圆满结束&#xff0c;大赛官方竞赛平台DataFountain&#xff08;简称DF平台&#xff09;正在陆续释出各赛题获奖队伍的方案思路&#xff0c;欢迎广大数据科学家交流讨论。 本方案为【大规模金融图数据中…

问题杂谈(三十七)远程调试linux中的Tomcat

前言 之前调试过Docker里面的java程序&#xff0c;但还没试过直接调试tomcat里面的java程序&#xff0c;今儿个来试试 步骤 Tomcat 修改catlina脚本&#xff1a;vi catlina.sh&#xff08;bin目录下&#xff09;找到下面这句&#xff0c;将"localhost:8000"改为”…