前端开发中,hover是最常见的鼠标操作行为之一,用起来也很方便,CSS直接提供:hover伪类,js可以通过mouseover+mouseout事件模拟,甚至一些第三方库/框架直接提供了 hover API ,比如 jQuery 的 hover() 函数。大部分前端开发者在使用这些很方便的方法时,可能并没有思考过 hover 背后的实现原理。

hover 是跟 DOM 绑定的,常规 DOM 是一个个矩形(CSS 盒模型),鼠标移动时浏览器需要判断鼠标指针坐标是否在这个 DOM 的矩形范围之内,根本上是一个数学问题,即判断一个点是否位于一个矩形内

这是跟很简单的计算,对比点坐标和矩形四个角的坐标就行了。

但是对于其他的几种前端图形技术来说,就不一定这么简单了,比如SVG、Canvas、WebGL,因为这几种图形技术中并非只有矩形这一种简单图形。

SVG

SVG 除了 <rect> 矩形之外,还有<circle><line>等代表某种特定图形的元素,以及<arc><path>这类绘制任意图形的元素。

SVG 实现 hover 的方式跟普通 HTML 并无二致,SVG 本身就是一种特异的 HTML,可以直接使用绝大部分 DOM API 和 CSS 选择器。

Canvas 2D

Canvas 2D(下文简称Canvas)是比 SVG 更底层的图形技术,只有 rect 这一种特定图形,其他的图形都是通过使用直线、弧线、贝塞尔曲线等路径 API 绘制出来。

Canvas 绘制的图形都是在一个 <canvas> 元素内,并不能向 DOM 或 SVG 一样使用 CSS 伪类或js事件实现某个图形的hover效果。为解决这个问题, Canvas 提供了isPointInPath() API 来判断某个点是否位于某个闭合路径之内,不过这个 API 并不是很好用,这个方法时挂载到绘制上下文 context上的,只能判断某个点是否位于当前绘制的路径内,这是个非常操蛋的限制。所以在 Canvas 2D 技术领域也通常会借鉴 WebGL 的实现方案,即通过数学方法判断一个点是否位于一个不规则多边形内

WebGL

WebGL 是比 Canvas 2D 更底层的图形技术,可以说是现阶段前端领域最底层、最接近图形学的图形技术。

未来可以期待一下 WebGPU。

WebGL 中只有点、线段、三角形三种基本图元,所有视觉可见的形状都是以这三种图元组成。其实主要是三角形,包括绝大多数的线和点也是由三角形组成。因为 WebGL 1.0 不支持宽于1像素的线段,而且折线还要考虑各种 join 效果。

WebGL 2.0 支持宽于 1像素的线段。

WebGL 中实现某个图形的 hover 以及click、mouseover、mouseout等鼠标事件的根本就是上文提到的判断一个点是否位于一个不规则多边形内。这是一个纯粹的几何数学问题,理论上有很多种解法,其中在工程领域使用最普遍的是射线法,这是目前综合计算复杂度和性能消耗的最优解之一。

射线法的原理是以待判断的点坐标画一条水平的直线,然后判断这条直接与多边形各条边的交点数量,如果是奇数则代表点在多边形内,如果是偶数则代表点在多边形之外。射线法可以适用于任意多边形,包括有洞(hole)的多边形,具体的推导过程就不贴了,感兴趣的话可以自己查一下相关资料。

射线法涉及以下三个问题:

  1. 如何获取多边形的各条边的端坐标?
  2. 如果多边形的某条边是曲线怎么办?
  3. 如何判断两条线段有交点?

如何获取多边形的各条边的端坐标?

这其实并不是一个图形绘制领域的问题,而是数据制备领域的问题。以一个简单图形举例:

上图中的六边形是由四个三角形组成,前端从服务端拿到的数据一般只包括六边形的6个顶点坐标,即v1 - v6,而且这6个坐标点是按照顺时针排列(如果有hole,则hole的顶点是逆时针排列),如下:

[v1,v2,v3,v4,v5,v6]

前端拿到顶点数组后需要使用三角剖分算法将其切割成4个三角形,最后才给到 WebGL 绘制。

也就是说,在数据制备阶段就已经将多边形的每个顶点坐标确定了,然后依序两两相接就是多边形的各条边。

当然也不排除有的技术团队在数据制备阶段就进行了三角剖分,但这么干的比较少,因为剖分后数据量会增长很多,会带来额外的存储成本和网络通信耗时。

如果多边形的某条边是曲线怎么办?

这是一个伪命题。WebGL 中不存在曲线,任意图形都是通过点、线段、三角形三种图元组合而成,即便视觉上是一个曲线或圆弧,本质上也是一个个三角形,只不过通过算法处理让人眼看不出明显的折角。所以WebGL中的任何图形本质上都是多边形,既然是多边形就可以按照上文的方案解决点与多边形的相对位置判断问题。

如何判断两条线段有交点?

明确了上面两个问题之后,就只剩下判断两条线段是否相交这一个问题了。这同样是个纯粹的数学问题。

回顾上文提到的多边形顶点数据制备,多边形的边是由相邻两个顶点相连而成,顶点是有序的,也就是说多边形的每条边都是有向线段,所以判断两条线段是否相交这个问题准确的说发应该是:判断两个有模向量是否相交

这就回到了高中数学哈哈。

第一个知识点是向量叉乘

t = 向量A x 向量B = |A||B|sin(a)

其中a是向量A和向量B的夹角。为了方便描述,我们把上述计算得到的结果赋值为t

严格的说,只有三维向量的叉乘才有几何意义,两个向量叉乘得到的是一个垂直于向量A和向量B、模为t的三维向量。二维向量的叉乘是从三维向量基础上延展出来的,有以下几何意义:

  1. t为向量A和向量B为相邻边的平行四边形的面积;
  2. 如果t>0,那么向量A正旋转到向量B的角度小于180度;
  3. 如果t<0,那么向量A正旋转到向量B的角度大于180度;
  4. 如果t=0 ,那么向量A和B平行。

判断两条线段是否相交用到了上述的规则2-4。先看下面这张图:

如果线段AB和CD相交可以推导出以下规则:

  1. 点A和点B分别位于线段CD的两侧;
  2. 点C和点D分别位于线段AB的两侧。

把第一条规则转化成数学语言,用向量描述:

  1. 向量AC位于向量AB的逆时针方向;
  2. 向量AD位于向量AB的顺时针方向;
  3. 向量BC位于向量BA的顺时针方向;
  4. 向量BD位于向量BA的逆时针方向。

进一步转化便是:

AB x AC > 0
AB x AD < 0
BA x BC < 0
BA x BD > 0

同理转化第二条规则,不再赘述。

总结

本文简单总结了前端常用的各种图形技术实现hover效果的方法,水平有限,聊当抛砖引玉。前端很多常用的方法或API底层都很值得玩味,这不比八股文有意思?

hover 背后的数学和图形学的更多相关文章

  1. GAN背后的数学原理

      模拟上帝之手的对抗博弈——GAN背后的数学原理 简介 深度学习的潜在优势就在于可以利用大规模具有层级结构的模型来表示相关数据所服从的概率密度.从深度学习的浪潮掀起至今,深度学习的最大成功在于判别式 ...

  2. 傅里叶变换:MP3、JPEG和Siri背后的数学

    九年前,当我还坐在学校的物理数学课的课堂里时,我的老师为我们讲授了一种新方法,给我留下了深刻映像.我认为,毫不夸张地说,这是对数学理论发现最广泛的应用.应用的领域包括:量子物理.射电天文学.MP3和J ...

  3. 傅里叶变换--MP3、JPEG和Siri背后的数学

    http://blog.jobbole.com/51301/ 九年前,当我还坐在学校的物理数学课的课堂里时,我的老师为我们讲授了一种新方法,给我留下了深刻映像. 我认为,毫不夸张地说,这是对数学理论发 ...

  4. 速算1/Sqrt(x)背后的数学原理

    概述 平方根倒数速算法,是用于快速计算1/Sqrt(x)的值的一种算法,在这里x需取符合IEEE 754标准格式的32位正浮点数.让我们先来看这段代码: float Q_rsqrt( float nu ...

  5. 吴恩达机器学习笔记43-SVM大边界分类背后的数学(Mathematics Behind Large Margin Classification of SVM)

    假设我有两个向量,

  6. Mathematics for Computer Graphics数学在计算机图形学中的应用 [转]

    最近严重感觉到数学知识的不足! http://bbs.gameres.com/showthread.asp?threadid=10509 [译]Mathematics for Computer Gra ...

  7. OpenGL坐标变换及其数学原理,两种摄像机交互模型(附源程序)

    实验平台:win7,VS2010 先上结果截图(文章最后下载程序,解压后直接运行BIN文件夹下的EXE程序): a.鼠标拖拽旋转物体,类似于OGRE中的“OgreBites::CameraStyle: ...

  8. 在数学建模中学MATLAB

    为期三周的数学建模国赛培训昨天正式结束了,还是有一定的收获的,尤其是在MATLAB的使用上. 1. 一些MATLAB的基础性东西: 元胞数组的使用:http://blog.csdn.net/z1137 ...

  9. 《数学之美》(吴军 著)读书笔记:第1章 文字和语言 vs 数字和信息

    第1章有4个小节,以及前言. 前言 1.信息 2.文字和数字 3.文字和语言背后的数学 4.小结 下面我一一展开,让我们看看每一节都说了什么. 前言 语言和数字都是信息传播的载体,他们之间其实存在着天 ...

随机推荐

  1. SphereEx 获数百万美元天使融资,接力 ShardingSphere 开启 Database Plus 新篇章

    5月14日,数据前沿技术领域初创公司 SphereEx 获得来自红杉中国种子基金.初心资本的数百万美元天使轮融资. SphereEx是一家致力于构建新型分布式数据基础设施的公司,秉承开源.共享.生态. ...

  2. Lynis 漏洞扫描工具部署及效果展示

    Lynis 漏洞扫描工具部署及效果展示 介绍 Lynis是一个安全审计工具,它可以在Linux,macOS和其他基于Unix的系统上运行.Lynis的主要重点是执行系统的运行状况检查,它还有助于检测漏 ...

  3. SQL Server 数据库单用户模式处理

    在还原数据库bak备份文件时,由于某种原因(具体何种原因在此不进行分析)导致数据库还原后处于单用户模式,如下图: 单个用户模式导致,数据库无法打开,只能通过脚本去查询数据库内的表,然后进行查询数据,极 ...

  4. Python 做简单的登录系统

    案例 之 登录系统原创作品1 该随笔 仅插入部分代码:全部py文件源代码请从百度网盘自行下载! 链接:https://pan.baidu.com/s/1_sTcDvs5XEGDcnpoQEIrMg 提 ...

  5. VS2019 及 Visual Assist X 安装配置

    Visual Studio 2019 安装 下载 https://visualstudio.microsoft.com/zh-hans/downloads/ 安装 设置 扩大 Solution Con ...

  6. Java:LinkedHashMap类小记

    Java:LinkedHashMap类小记 对 Java 中的 LinkedHashMap类,做一个微不足道的小小小小记 概述 public class LinkedHashMap<K,V> ...

  7. Ubuntu 用户管理/权限管理

    Ubuntu 用户管理/权限管理 小小记录一下 Ubuntu 下用户/权限管理常用的一些命令 用户管理 组管理 文件权限 给用户添加 sudo 权限 给用户添加 sudo 权限 首先先给出几个文件 / ...

  8. Scrum Meeting 0429

    零.说明 日期:2021-4-29 任务:简要汇报两日内已完成任务,计划后两日完成任务 一.进度情况 组员 负责 两日内已完成的任务 后两日计划完成的任务 qsy PM&前端 完成部分后端管理 ...

  9. Uniapp云打包生成apk下载链接

    使用uni[]()app云打包生成安装包下载链接 manifest.json 中配置自动获取appid manifest.json中配置app 图标 按教程生成.keystore证书 使用云打包生成安 ...

  10. [技术博客] 通过ItemTouchHelper实现侧滑删除功能

    通过ItemTouchHelper实现侧滑删除功能 一.效果 二.具体实现 demo中演示的这种左滑删除的效果在手机APP中比较常用,安卓也为我们提供了专门的辅助类ItemTouchHelper来帮助 ...