马黑黑 发表于 2022-9-5 19:07

在svg中检测鼠标指针经过圆

在svg中检测鼠标指针经过圆 | 马黑黑

svg绘制圆,使用 fill 方法画出的是实心圆,使用 stroke 方法画出的是圆环。本帖只讨论鼠标指针经过实心圆的检测方法。我们先用 fill 方法绘制一个圆:

<svg id="sc" width="200" height="200" style="background: gray">
        <circle cx="100" cy="100" r="50" fill="lightblue" />
</svg>

这是在 200*200 的灰色底 svg 画布上以(100,100)为圆心,绘制一个半径为 50 的浅蓝色实心圆。现在,我们就来检测鼠标是否经过浅蓝色圆的区域,如果经过,鼠标指针变为手型,否则是默认形状。

具体思路是,鼠标点击的点 p(x,y)与圆心 o 连成线段 op,o→p 方向的直线 op 或 op 的延长线必然与圆周线相交于某一点 k,ok 等于圆的半径 r,我们可以将 op 的长度与圆的半径 r 进行比较,从而判断点 p 是否在圆内。这时,我们可以考虑用勾股定理来完成这个判断,方法是:经过 p 点往X轴方向画一根与Y轴平行的辅助线,与X轴相交于 d 点,我们就得到一个直角三角形 △opd,其中,op 为斜线,od 和 dp 为两条直角线,这两根直角线的长度分别对应于点 p 坐标的 x 和 y 值,我们通过鼠标相关事件可以获得这个xy坐标值。那么,如果两个直角边的长度的平方和小于等于圆半径的平方,说明点 p 在圆内,反之在圆外。这是中小学数学知识的应用,按理应该不难理解,除非大家把中小学数学知识还给了美术老师了。

JS有一个内置数学(Math)函数 pow,用于计算 x 的 y 次幂:

Math.pow(x,y);

设直角线分别为 a、b,斜线为 c ,则 JS 中各条线的平方运算公式分别为:

Math.pow(a,2);
Math.pow(b,2);
Math.pow(c,2);

当然也可以用最原始的运算式子:a*a,b*b,c*c。

知识铺垫到位,现在可以用 JS 编写一个检测函数了:

function isCircle(cx, cy, r, ex, ey) {
        return Math.pow(ex - cx, 2) + Math.pow(ey - cy, 2) <= Math.pow(r, 2);
}

函数需要 5 个参数:cx,圆心x坐标,cy,圆心y坐标,r是圆的半径,ex 和 ey 分别是鼠标经过或点击时的x、y坐标。

计算式子中,Math.pow(ex - cx, 2) 里,ex - cx 得出鼠标点击的坐标点的x位置离圆心的距离,其中,ex 我们会通过 offsetX 获取,它的实际数值是从事件源对象(元素)的左边到点击点的水平距离,它减去圆心x坐标值就是三角形直角边水平线段的长度;Math.pow(ey - cy, 2) 同理,ey - cy 得到的是垂直于X轴的直角边的长度;Math.pow(r, 2) 计算半径的平方值,无须解释吧。

上述函数被调用时会返回上述式子的计算结果,如果式子成立,返回真(true),否则返回假(false),这是布尔值,非真即假、非假即真,不存在第三方的值。下面是函数调用实例,检测鼠标经过画布与否并作出相应响应,画布的 id 为 sc:

sc.onmousemove = (e) => {
        if(isCircle(100, 100, 50, e.offsetX, e.offsetY)) {
                sc.style.cursor = 'pointer';
        } else {
                sc.style.cursor = 'default';
        }
}

鼠标移动代码中,带入的参数里,100 和 100 是圆心xy坐标,50 是圆的半径,后两个值就是鼠标经过或点击时的xy坐标值。如此,鼠标在svg画布上移动时,浅蓝色圆的区域鼠标指针为手型,圆外区域鼠标指针为默认形状。

最后附上完整代码:
<svg id="sc" width="200" height="200" style="background: gray">
        <circle cx="100" cy="100" r="50" fill="lightblue" />
</svg>

<script>

sc.onmousemove = (e) => {
        if(isCircle(100, 100, 50, e.offsetX, e.offsetY)) {
                sc.style.cursor = 'pointer';
        } else {
                sc.style.cursor = 'default';
        }
}

function isCircle(cx, cy, r, ex, ey) {
        return Math.pow(ex - cx, 2) + Math.pow(ey - cy, 2) <= Math.pow(r, 2);
}

</script>

红影 发表于 2022-9-5 19:51

关于三角函数的那段比较简单,难的是鼠标经过的点能用offsetX和offsetY直接获取。

马黑黑 发表于 2022-9-5 19:57

红影 发表于 2022-9-5 19:51
关于三角函数的那段比较简单,难的是鼠标经过的点能用offsetX和offsetY直接获取。

offsetX和offsetY是JS内置命令,在现代浏览器中都得到完美支持。它们获取事件源元素——即鼠标经过或点击等的元素——的鼠标指针下的xy坐标值,offsetX 是 点到左边的值,offsetY 是 点到上边的值。

梦缘 发表于 2022-9-5 20:07

谢谢老师的代码分享,学习!{:4_190:}

马黑黑 发表于 2022-9-5 20:08

梦缘 发表于 2022-9-5 20:07
谢谢老师的代码分享,学习!

这个没啥难度吧

红影 发表于 2022-9-5 21:54

马黑黑 发表于 2022-9-5 19:57
offsetX和offsetY是JS内置命令,在现代浏览器中都得到完美支持。它们获取事件源元素——即鼠标经过或点击 ...

哦,是内置命令,嗯,现在知道了{:4_204:}

马黑黑 发表于 2022-9-5 22:41

红影 发表于 2022-9-5 21:54
哦,是内置命令,嗯,现在知道了

专题介绍过的

红影 发表于 2022-9-6 10:41

马黑黑 发表于 2022-9-5 22:41
专题介绍过的

那会没记住啊,现在能更深地知道点了。

加林森 发表于 2022-9-6 10:53

来学习!

马黑黑 发表于 2022-9-6 12:24

红影 发表于 2022-9-6 10:41
那会没记住啊,现在能更深地知道点了。

循序渐进

马黑黑 发表于 2022-9-6 12:25

加林森 发表于 2022-9-6 10:53
来学习!

{:4_190:}

加林森 发表于 2022-9-6 12:53

马黑黑 发表于 2022-9-6 12:25


谢啦!

红影 发表于 2022-9-6 16:14

马黑黑 发表于 2022-9-6 12:24
循序渐进

这个原理倒不算难,只是要知道命令熟练运用命令不容易。

马黑黑 发表于 2022-9-6 16:17

红影 发表于 2022-9-6 16:14
这个原理倒不算难,只是要知道命令熟练运用命令不容易。

这要深入

红影 发表于 2022-9-6 21:00

马黑黑 发表于 2022-9-6 16:17
这要深入

是的,要熟悉那些命令。

马黑黑 发表于 2022-9-6 21:01

红影 发表于 2022-9-6 21:00
是的,要熟悉那些命令。

最好掌握

红影 发表于 2022-9-6 21:21

马黑黑 发表于 2022-9-6 21:01
最好掌握

嗯嗯,尽力呗{:4_173:}

马黑黑 发表于 2022-9-6 21:23

红影 发表于 2022-9-6 21:21
嗯嗯,尽力呗

挺好
页: [1]
查看完整版本: 在svg中检测鼠标指针经过圆