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

news2025/1/12 6:10:20

不知道从什么时候起,人们开始喜欢上数字大屏这种“花里胡哨”的东西,仿佛只要用上“科技蓝”这样神奇的色调,就可以让一家公司焕然一新,瞬间变得科技感满满。不管数字大屏的实际意义,是用来帮助企业监控和决策,还是为了方便领导参观和视察,抑或是为了向外界展示和宣传。总之,自从数字大屏诞生之后,它始终就没能摆脱其前任“中国式报表”那种大而全的宿命。追随着 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.coloradmin.cn/o/922979.html

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

相关文章

压力传感器丨定义、原理、应用

压力传感器是工业实践中常用的设备&#xff0c;作为自控系统的重要组成部分&#xff0c;压力传感器能够用于工业过程中压力参数的测量和控制&#xff0c;常用于高温、低压、腐蚀、振动等环境。 压力传感器的原理是基于压力感测技术&#xff0c;能够将被测压力转化为4G信号&…

开学日临近,送你一份VR校园攻略

开学日临近&#xff0c;各位萌新是否还在心怀激荡&#xff0c;无限憧憬着美丽校园呢&#xff1f;可能有一部分大学已经开学了&#xff0c;那么刚入校园的你们&#xff0c;是不是也想尽快熟悉未来学习、生活的地方呢&#xff1f;这份VR校园攻略带你沉浸式体验校园生活。 首先是大…

基于前端技术原生HTML、JS、CSS 电子病历编辑器源码

电子病历系统采取结构化与自由式录入的新模式&#xff0c;自由书写&#xff0c;轻松录入。实现病人医疗记录&#xff08;包含有首页、病程记录、检查检验结果、医嘱、手术记录、护理记录等等。&#xff09;的保存、管理、传输和重现&#xff0c;取代手写纸张病历。不仅实现了纸…

亚马逊出口灯具需要做的认证fcc认证UL认证ROHS认证CE认证

灯具CE认证&#xff1a; ​CE认证是产品进入欧盟及欧洲贸易自由区国家市场的通行证。任何国家的产品要进入欧盟、欧洲自由贸易区必须进行CE认证&#xff0c;在产品上加贴CE标志。LED灯具CE认证检测&#xff0c;为各国产品在欧洲市场进行贸易提供了统一的技术规范&#xff0c;C…

寻找最佳财务软件:简单易用的首选推荐

现代企业越来越依赖高效的财务管理工具&#xff0c;而财务软件成为了许多企业的首选。然而&#xff0c;市场上众多的财务软件让人眼花缭乱&#xff0c;财务软件哪个最好用最简单&#xff1f; Zoho Books是由Zoho Corporation开发的一款全功能财务管理软件。它提供了一系列强大的…

Leetcode---111双周赛

题目列表 2824. 统计和小于目标的下标对数目 2825. 循环增长使字符串子序列等于另一个字符串 2826. 将三个组排序 2827. 范围中美丽整数的数目 一、统计和小于目标的下标对数目 这题直接暴力求解&#xff0c;时间复杂度是O(n^2)&#xff0c;代码如下 class Solution { pu…

知识推荐:化工企业ERP系统如何选型?

多年来&#xff0c;在政策激励、舆论倡导和产业实践的不断推动下&#xff0c;智能制造的理念已经深入人心&#xff0c;成为化工行业的普遍共识。在当前经济新常态下&#xff0c;化工产业面临着产能过剩、效益下滑、环境恶化等多重压力&#xff0c;必须从战略高度认识并积极应对…

Kali 软件管理测试案例

案例1 &#xff1a;显示目录树 tree ┌──(root㉿kali)-[~] └─# tree --help usage: tree [-acdfghilnpqrstuvxACDFJQNSUX] [-L level [-R]] [-H baseHREF][-T title] [-o filename] [-P pattern] [-I pattern] [--gitignore][--gitfile[]file] [--matchdirs] [--metafirs…

PotPlayer+LAV+MadVR+XySubFilter 配置指南(修订版)

Introduction Potplayer 作为 Windows 上很受欢迎的一款播放器&#xff0c;公认的最佳配置为结合 LAV、madVR、XySubfilter 进行使用。本文将介绍一种对其简单配置的方法。 LAV&#xff1a;一套主流的开源解码 filterMadVR&#xff1a;一个高性能的视频渲染器XySubFilter&…

keepalived双机热备

一、概述 Keepalived 是一个基于 VRRP 协议来实现的 LVS 服务高可用方案&#xff0c;可以解决静态路由出现的单点故障问题 原理 在一个 LVS 服务集群中通常有主服务器&#xff08;MASTER&#xff09;和备份服务器&#xff08;BACKUP&#xff09;两种角色的服务器&#xff0c…

19篇ICCV 2023自动驾驶精选论文解析 | 涵盖3D目标检测、语义分割、点云等方向

ICCV 2023榜单上月已出&#xff0c;今年共收录了2160篇论文&#xff0c;这次是精选了今年ICCV 2023 会议中自动驾驶相关的最新论文来和大家分享&#xff0c;涵盖了3D目标检测、BEV感知、目标检测、语义分割、点云等方向&#xff0c;共19篇。 论文原文以及开源代码文末领取&…

Apipost:提升API开发效率的利器

在数字化时代&#xff0c;API已经成为企业和开发者实现业务互通的关键工具。然而&#xff0c;API的开发、调试、文档编写以及测试等工作繁琐且复杂。Apipost为这一问题提供了完美的解决方案。 Apipost是一款专为API开发人员设计的协同研发平台&#xff0c;旨在简化API的生命周…

多数元素00

题目链接 多数元素 题目描述 注意点 给定的数组总是存在多数元素多数元素是指在数组中出现次数 大于 ⌊ n/2 ⌋ 的元素 解答思路 初始想到使用map存每个元素和元素出现的次数&#xff0c;但是时间和空间都不理想因为本题给定的数组总是存在多数元素&#xff0c;使用投票算…

在CSS中,盒模型中的padding、border、margin是什么意思?

在CSS中&#xff0c;盒模型&#xff08;Box Model&#xff09;是用来描述和布局HTML元素的基本概念。它将每个HTML元素看作是一个矩形的盒子&#xff0c;这个盒子包括了内容&#xff08;content&#xff09;、内边距&#xff08;padding&#xff09;、边框&#xff08;border&a…

第二节 分支和循环语句

第二节 分支和循环语句 目录 一&#xff0e; 什么是语句&#xff1f;二&#xff0e; 分支语句&#xff08;选择结构&#xff09;三&#xff0e; 循环语句 本章重点&#xff1a; 分支语句  if  switch 循环语句  while  for  do while goto语句 一&#xff0e; 什么是…

Spark on Yarn集群模式搭建及测试

&#x1f947;&#x1f947;【大数据学习记录篇】-持续更新中~&#x1f947;&#x1f947; 点击传送&#xff1a;大数据学习专栏 持续更新中&#xff0c;感谢各位前辈朋友们支持学习~ 文章目录 1.Spark on Yarn集群模式介绍2.搭建环境准备3.搭建步骤 1.Spark on Yarn集群模式介…

华为USG防火墙登录提示:登录失败,可能的原因时账户被锁定

问题&#xff1a; 密码是正确的&#xff0c;但是华为USG6305E 防火墙登录提示&#xff1a;登录失败&#xff0c;可能的原因时账户被锁定 解决办法&#xff1a; 清楚浏览器所有的缓存&#xff0c;重启浏览器后再登录&#xff0c;正常可以登录

创建导航卡 “系统管理_导航卡_Demo”

前言 apex和pl/sql要学的东西还有很多&#xff0c;一次性放在一个里面老是找不着&#xff0c;细分一下单独说&#xff0c;也方便复习。 创建导航卡“系统管理_导航卡_Demo” 1.创建列表 创建成功 点开还可以继续修改&#xff0c;设置图标 2 创建导航页面 Step 1创建空白页面…

Visual Studio中Linux开发头文件intellisense问题的解决办法

文章目录 前言个人环境 SSH到WSL复制文件后记 前言 最近在用我心爱的Visual Studio配合WSL2做一些Linux开发&#xff0c;但是有一个问题&#xff0c;就是当我#include <sys/socket.h>&#xff0c;会提示找不到文件 我尝试了各种姿势&#xff0c;包括修改CMakeSettings.…

Linux 多线程解决客户端与服务器端通信

一、一个服务器端只能和一个客户端进行通信&#xff08;单线程模式&#xff09; 客户端代码ser.c如下&#xff1a; #include<stdio.h> #include<stdlib.h> #include<string.h> #include<unistd.h> #include<sys/socket.h> #include<netinet…