当前位置: 首页 > news >正文

基于 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 等知识体系。特别是当我们需要处理拖拽/平移、缩放、旋转等常规操作时,这些都是最具挑战性的部分,不是吗?

相关文章:

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

不知道从什么时候起&#xff0c;人们开始喜欢上数字大屏这种“花里胡哨”的东西&#xff0c;仿佛只要用上“科技蓝”这样神奇的色调&#xff0c;就可以让一家公司焕然一新&#xff0c;瞬间变得科技感满满。不管数字大屏的实际意义&#xff0c;是用来帮助企业监控和决策&#xf…...

微服务(rpc)

微服务&#xff08;rpc&#xff09; 微服务必备的模块生产者消费者管理平台流量控制集群情况下如何做到流量监控 负载均衡服务发现和治理序列化传输序列化和反序列化 微服务是一种架构风格&#xff0c;将一个应用程序拆分为一组小型、独立的服务&#xff0c;每个服务都可以独立…...

ThinkPHP 多应用配置,及不同域名访问不同应用的配置【详解】

ThinkPHP 多应用配置&#xff0c;及不同域名访问不同应用的配置【详解】 一、安装多应用扩展二、删除项目的中默认的controller文件夹三、创建多应用四、修改config/app.php文件五、测试并且访问多应用六、配置小皮&#xff0c;不同域名访问不同的应用七、小结 一、安装多应用扩…...

Springboot+Mybatis框架是否会取代SSM框架?

个人认真思考的观点&#xff1a;从市场使用来说&#xff0c;会有这个趋势。从技术上来说&#xff0c;不存在被替代这一说。 Spring BootMybatis框架是指使用Spring Boot作为基础框架&#xff0c;并集成Mybatis作为持久层框架的组合&#xff0c;它是一个基于Spring框架的快速开发…...

使用windeployqt和InstallShield打包发布Qt软件的流程

前言 Qt编译之后需要打包发布&#xff0c;并且发布给用户后需要增加一个安装软件&#xff0c;通过安装软件可以实现Qt软件的安装&#xff1b;用于安装软件的软件有很多&#xff0c;这里主要介绍InstallShield使用的流程&#xff1b; 使用windeployqt打包Qt编译后的程序 Qt程序…...

解决selenium的getdrive()方法阻塞问题

selenium里面的Webdriver的get()方法默认是阻塞的&#xff0c;也就是说要等整个页面全都加载完它才会相应。但我们大部分时候不需要用到页面里的所有东西&#xff0c;也许只需要用到里面的一个元素就行了 所以下面是我的解决方法&#xff1a; 初始化代码&#xff1a; # 设置…...

js的闭包

闭包是有权限访问其他函数作用域的局部变量的一个函数 代码 function outer(){const a1;function f(){console.log(a)}f()}outer() 简单来说&#xff1a;闭包内层函数引用的外层函数变量 为什么要使用闭包&#xff1f; 用此方法可以来统计函数调用次数&#xff0c; 但是如…...

ubuntu20.04 直接安装vpp23.06 测试双 VPP Tunnel Ike2

环境信息&#xff1a;VMware Workstation 17 Pro ubuntu20.04 (清华源) ubuntu 源点进去选&#xff1a;ubuntu-22.04.3-desktop-amd64.iso 如果之前装过VPP&#xff0c;用以下命令确定是否卸载干净&#xff1a; dpkg -l | grep vpp dpkg -l | grep DPDK 卸载&#xff1a; …...

mysql sql 执行流程

监控查询缓存的命中率 show status like ‘%qcache%’; mysql 缓存机制&#xff0c;以及 8.0 为啥取消 select sql_NO_Cache * from 表 where xxx; # 不使用缓存...

go-kafka

go kafka包 本文使用的是kafka-go 6.5k 这个包 其他包参考&#xff1a; 我们在细分市场中非常依赖GO和Kafka。不幸的是&#xff0c;在撰写本文时&#xff0c;Kafka的GO客户库的状态并不理想。可用选项是&#xff1a; 萨拉玛&#xff08;Sarama&#xff09; 10k&#xff0c;这…...

如何在windows电脑上安装多个node,并可以进行随意切换

一、进入官网http://nvm.uihtm.com/ 下载 二、启动解压后的程序 1.开始安装nvm 选择要安装的目录 一直下一步–下一步–最后点击完成 3.最后点击完成即可 ![在这里插入图片描述](https://img-blog.csdnimg.cn/3656568c7e9946e8a04219811fc4c4d3.png 三、在cmd控制台进行操作…...

《机器学习核心技术》分类算法 - 决策树

「作者主页」&#xff1a;士别三日wyx 「作者简介」&#xff1a;CSDN top100、阿里云博客专家、华为云享专家、网络安全领域优质创作者 「推荐专栏」&#xff1a;小白零基础《Python入门到精通》 决策树 1、决策树API2、决策时实际应用2.1、获取数据集2.2、划分数据集2.3、决策…...

aws PinPoint发附件demo

php 版aws PinPoint发附件demo Laravel8框架&#xff0c;安装了"aws/aws-sdk-php": "^3.257" 主要代码&#xff1a; public function sendRawMail(Request $request) {$file $request->file(attachment);/*echo count($file);dd($file);*/$filenam…...

边写代码边学习之Bidirectional LSTM

1. 什么是Bidirectional LSTM 双向 LSTM (BiLSTM) 是一种主要用于自然语言处理的循环神经网络。 与标准 LSTM 不同&#xff0c;输入是双向流动的&#xff0c;并且它能够利用双方的信息。 它也是一个强大的工具&#xff0c;可以在序列的两个方向上对单词和短语之间的顺序依赖…...

Django学习笔记-实现联机对战

笔记内容转载自 AcWing 的 Django 框架课讲义&#xff0c;课程链接&#xff1a;AcWing Django 框架课。 CONTENTS 1. 统一长度单位2. 增加联机对战模式3. 配置Django Channels 1. 统一长度单位 多人模式中每个玩家所看到的地图相对来说应该是一样的&#xff0c;因此需要固定地…...

nacos总结1

5.Nacos注册中心 国内公司一般都推崇阿里巴巴的技术&#xff0c;比如注册中心&#xff0c;SpringCloudAlibaba也推出了一个名为Nacos的注册中心。 5.1.认识和安装Nacos Nacos是阿里巴巴的产品&#xff0c;现在是SpringCloud中的一个组件。相比Eureka功能更加丰富&#xff0c…...

Web安全测试(三):SQL注入漏洞

一、前言 结合内部资料&#xff0c;与安全渗透部门同事合力整理的安全测试相关资料教程&#xff0c;全方位涵盖电商、支付、金融、网络、数据库等领域的安全测试&#xff0c;覆盖Web、APP、中间件、内外网、Linux、Windows多个平台。学完后一定能成为安全大佬&#xff01; 全部…...

Webstorm 入门级玩转uni-app 项目-微信小程序+移动端项目方案

1. Webstorm uni-app语法插件 &#xff1a; Uniapp Support Uniapp Support - IntelliJ IDEs Plugin | Marketplace 第一个是不收费&#xff0c;第二个收费 我选择了第二个Uniapp Support &#xff0c;有试用30天&#xff0c;安装重启webstorm之后&#xff0c;可以提高生产率…...

从零开始的Hadoop学习(三)| 集群分发脚本xsync

1. Hadoop目录结构 bin目录&#xff1a;存放对Hadoop相关服务&#xff08;hdfs&#xff0c;yarn&#xff0c;mapred&#xff09;进行操作的脚本etc目录&#xff1a;Hadoop的配置文件目录&#xff0c;存放Hadoop的配置文件lib目录&#xff1a;存放Hadoop的本地库&#xff08;对…...

golang http transport源码分析

golang http transport源码分析 前言 Golang http库在日常开发中使用会很多。这里通过一个demo例子出发&#xff0c;从源码角度梳理golang http库底层的数据结构以及大致的调用流程 例子 package mainimport ("fmt""net/http""net/url""…...

HTML 语义化

目录 HTML 语义化HTML5 新特性HTML 语义化的好处语义化标签的使用场景最佳实践 HTML 语义化 HTML5 新特性 标准答案&#xff1a; 语义化标签&#xff1a; <header>&#xff1a;页头<nav>&#xff1a;导航<main>&#xff1a;主要内容<article>&#x…...

label-studio的使用教程(导入本地路径)

文章目录 1. 准备环境2. 脚本启动2.1 Windows2.2 Linux 3. 安装label-studio机器学习后端3.1 pip安装(推荐)3.2 GitHub仓库安装 4. 后端配置4.1 yolo环境4.2 引入后端模型4.3 修改脚本4.4 启动后端 5. 标注工程5.1 创建工程5.2 配置图片路径5.3 配置工程类型标签5.4 配置模型5.…...

中南大学无人机智能体的全面评估!BEDI:用于评估无人机上具身智能体的综合性基准测试

作者&#xff1a;Mingning Guo, Mengwei Wu, Jiarun He, Shaoxian Li, Haifeng Li, Chao Tao单位&#xff1a;中南大学地球科学与信息物理学院论文标题&#xff1a;BEDI: A Comprehensive Benchmark for Evaluating Embodied Agents on UAVs论文链接&#xff1a;https://arxiv.…...

如何将联系人从 iPhone 转移到 Android

从 iPhone 换到 Android 手机时&#xff0c;你可能需要保留重要的数据&#xff0c;例如通讯录。好在&#xff0c;将通讯录从 iPhone 转移到 Android 手机非常简单&#xff0c;你可以从本文中学习 6 种可靠的方法&#xff0c;确保随时保持连接&#xff0c;不错过任何信息。 第 1…...

论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(一)

宇树机器人多姿态起立控制强化学习框架论文解析 论文解读&#xff1a;交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架&#xff08;一&#xff09; 论文解读&#xff1a;交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化…...

10-Oracle 23 ai Vector Search 概述和参数

一、Oracle AI Vector Search 概述 企业和个人都在尝试各种AI&#xff0c;使用客户端或是内部自己搭建集成大模型的终端&#xff0c;加速与大型语言模型&#xff08;LLM&#xff09;的结合&#xff0c;同时使用检索增强生成&#xff08;Retrieval Augmented Generation &#…...

GitFlow 工作模式(详解)

今天再学项目的过程中遇到使用gitflow模式管理代码&#xff0c;因此进行学习并且发布关于gitflow的一些思考 Git与GitFlow模式 我们在写代码的时候通常会进行网上保存&#xff0c;无论是github还是gittee&#xff0c;都是一种基于git去保存代码的形式&#xff0c;这样保存代码…...

脑机新手指南(七):OpenBCI_GUI:从环境搭建到数据可视化(上)

一、OpenBCI_GUI 项目概述 &#xff08;一&#xff09;项目背景与目标 OpenBCI 是一个开源的脑电信号采集硬件平台&#xff0c;其配套的 OpenBCI_GUI 则是专为该硬件设计的图形化界面工具。对于研究人员、开发者和学生而言&#xff0c;首次接触 OpenBCI 设备时&#xff0c;往…...

AI语音助手的Python实现

引言 语音助手(如小爱同学、Siri)通过语音识别、自然语言处理(NLP)和语音合成技术,为用户提供直观、高效的交互体验。随着人工智能的普及,Python开发者可以利用开源库和AI模型,快速构建自定义语音助手。本文由浅入深,详细介绍如何使用Python开发AI语音助手,涵盖基础功…...

系统掌握PyTorch:图解张量、Autograd、DataLoader、nn.Module与实战模型

本文较长&#xff0c;建议点赞收藏&#xff0c;以免遗失。更多AI大模型应用开发学习视频及资料&#xff0c;尽在聚客AI学院。 本文通过代码驱动的方式&#xff0c;系统讲解PyTorch核心概念和实战技巧&#xff0c;涵盖张量操作、自动微分、数据加载、模型构建和训练全流程&#…...