基于 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.mzph.cn/news/51414.shtml

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

相关文章

Linux网络编程:线程池并发服务器 _UDP客户端和服务器_本地和网络套接字

文章目录&#xff1a; 一&#xff1a;线程池模块分析 threadpool.c 二&#xff1a;UDP通信 1.TCP通信和UDP通信各自的优缺点 2.UDP实现的C/S模型 server.c client.c 三&#xff1a;套接字 1.本地套接字 2.本地套 和 网络套对比 server.c client.c 一&#xff1a;线…

electron软件安装时,默认选择为全部用户安装

后续可能会用electron开发一些工具&#xff0c;包括不限于快速生成个人小程序、开发辅助学习的交互式软件、帮助运维同学一键部署的简易版CICD工具等等。 开发进度&#xff0c;取决于我懒惰的程度。 不过不嫌弃的同学还是可以先关注一波小程序&#xff0c;真的发布工具了&…

微软宣布在 Excel 中使用 Python:结合了 Python 的强大功能和 Excel 的灵活性。

自诞生以来&#xff0c;Microsoft Excel 改变了人们组织、分析和可视化数据的方式&#xff0c;为每天使用它的数百万人提供了决策基础。今天&#xff0c;我们宣布发布 Excel 中的 Python 公共预览版&#xff0c;从而使 Excel 中的分析功能取得重大进展。 Excel 中的 Python 可…

基于springboot+vue的考研资讯平台(前后端分离)

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战 主要内容&#xff1a;毕业设计(Javaweb项目|小程序等)、简历模板、学习资料、面试题库、技术咨询 文末联系获取 项目介绍…

【洛谷】P1873 [COCI2011-2012#5] EKO / 砍树

原题链接&#xff1a;https://www.luogu.com.cn/problem/P1873 目录 1. 题目描述 2. 思路分析 3. 代码实现 1. 题目描述 2. 思路分析 二分答案。 3. 代码实现 #include<bits/stdc.h> using namespace std; #define ll long long const int N 1e6 10; int a[N], …

系统学习Linux-LVS集群

集群概述 负载均衡技术类型 四层负载均衡器 也称为 4 层交换机&#xff0c;主要通过分析 IP 层及 TCP/UDP 层的流量实现基于 IP 加端口的负载均衡&#xff0c;如常见的 LVS、F5 等&#xff1b; 七层负载均衡器 也称为 7 层交换机&#xff0c;位于 OSI 的最高层&#xff0c;即…

数据库表结构导出为word、html、markdown【转载,已解决,已验证,开源】

注&#xff1a;本文为gitcode代码验证&#xff0c;转载gitcode gitcode&#xff1a;https://gitcode.net/mirrors/pingfangushi/screw?utm_sourcecsdn_github_accelerator 整理数据库文档&#xff1a;https://mp.weixin.qq.com/s/Bo_U5_cl82hfQ6GmRs2vtA <!--数据库文档核…

Seaborn数据可视化(三)

1.绘制直方图 使用displot()绘制直方图。 import seaborn as sns import numpy as np import matplotlib.pyplot as plt# 生成随机数据 np.random.seed(0) data np.random.randn(1000)# 使用displot绘制直方图 sns.displot(data, bins10, kdeTrue)# 展示图形 plt.show() 结…

Python用 tslearn 进行时间序列聚类可视化

全文链接&#xff1a;https://tecdat.cn/?p33484 我们最近在完成一些时间序列聚类任务&#xff0c;偶然发现了 tslearn 库。我很想看看启动和运行 tslearn 已内置的聚类有多简单&#xff0c;结果发现非常简单直接&#xff08;点击文末“阅读原文”获取完整代码数据&#xff09…

Nexus私有仓库+IDEA配置远程推送

目录 一、docker安装nexus本地私服&#xff0c;Idea通过maven配置deploy本地jar包&#xff08;简单&#xff09; 二、docker push镜像到第三方nexus远程私服&#xff08;shell命令操作&#xff09; 三、springboot通过maven插件自动生成docker镜像并push到nexus私服&#xf…

【科研】-- 如何将Endnote中参考文献格式插入到Word?

文章目录 如何将Endnote中参考文献格式插入到Word&#xff1f; 如何将Endnote中参考文献格式插入到Word&#xff1f; 1、首先确保Endnote和Word安装正确&#xff0c;正常可以从学校官网中下载到正版软件&#xff0c;下载后在word栏目中会出现EndNote的标签&#xff1b; 2、可…

[Linux]进程状态

[Linux]进程状态 文章目录 [Linux]进程状态进程状态的概念阻塞状态挂起状态Linux下的进程状态孤儿进程 进程状态的概念 了解进程状态前&#xff0c;首先要知道一个正在运行的进程不是无时无刻都在CPU上进行运算的&#xff0c;而是在操作系统的管理下&#xff0c;和其他正在运行…

音视频FAQ(二)视频直播延时高

摘要 延时高是实时互动技术中常见的问题之一&#xff0c;解决延时高问题需要综合考虑网络、设备、编解码算法等多个因素。解决方案包括优化设备端延时、优化网络传输延时和使用UDP进行音视频传输等。在选择音视频传输协议时&#xff0c;需要综合考虑实际需求和网络条件&#x…

Linux —— keepalived

简介 Keepalived 是一个用 C 语言编写的路由软件。这个项目的主要目标是为 Linux 系统和基于 Linux 的基础设施提供简单而强大的负载均衡和高可用性功能。 Keepalived 开源并且免费的软件。 Keepalived 的2大核心功能 1. loadbalance 负载均衡 LB&#xff1a;ipvs--》lvs软件…

线性代数的学习和整理12: 矩阵与行列式,计算上的差别对比

目录 1 行列式和矩阵的比较 2 简单总结矩阵与行列式的不同 3 加减乘除的不同 3.1 加法不同 3.2 减法不同 3.3 标量乘法/数乘 3.3.1 标准的数乘对比 3.3.2 其他数乘对比 3.4 乘法 4 初等线性变换的不同 4.1 对矩阵进行线性变换 4.2 对行列式进行线性变换呢&#xf…

Maven详解

文章目录 一、引言1.1 为什么需要 Maven&#xff1f;1.2 Maven 解决了哪些问题&#xff1f;1.2.1 添加第三方jar包1.2.2 jar包之间的依赖关系1.2.3 处理jar包之间的冲突1.2.4 获取第三方jar包1.2.5 将项目拆分成多个工程模块1.2.6 实现项目的分布式部署 二、介绍三、Maven 的特…

flutter 雷达图

通过CustomPainter自定义雷达图 效果如下 主要代码 import package:flutter/material.dart; import dart:math; import dash_painter.dart; import model/charts_model.dart;class RadarChart extends StatelessWidget {final List<ChartModel> list;final double maxV…

5.物联网LWIP之UDP编程,stm32作为服务器实现大小写转化

UDP编程模型 1.UDP C/S模型&#xff08;代码流程只需要根据以下模型去输入即可&#xff09; 2.UDP API socket int socket(int domain, int type, int protocol); domain: AF_INET 这是大多数用来产生socket的协议&#xff0c;使用TCP或UDP来传输&#xff0c;用IPv4的地址…

从0开始配置eslint

没有在.eslintrc文件中配置parserOptions指定语言版本和模块类型 {"parserOptions": {"ecmaVersion": 7, //指定es版本为es2016"sourceType": "module", //使用import导入模块} }eslint还不能识别jsx语法 {"parserOptions"…

centos7物理机安装并配置外网访问

安装准备工作 安装之前需要准备一下&#xff0c;需要一个U盘&#xff0c;其次需要准备以下内容 1.需要centos7的ISO系统镜像 2.使用UltraISO软件写入ISO镜像 3.一台windows系统 将系统写入到U盘&#xff0c;写入步骤 打开UltraISO点击文件 → 打开&#xff0c;选择Linux镜…