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

javascript-svg-在圆环上拖动并选中区域

目录

  • 问题描述
  • 解决思路
  • 代码结构

问题描述

假设我某个页面上使用了<svg>,其中包括一个<circle>。我希望实现的是:在circle上点击某个位置后,拖动,出现圆弧状阴影。实现效果为:
在这里插入图片描述

解决思路

要实现这个效果,需要考虑3个问题:

  1. 如何绘制圆弧阴影?
  2. 何时添加圆弧阴影?
  3. 在拖动时如何修改圆弧阴影的范围?

对于第1个问题,很明显我们需要使用<path>,并且通过设置fillstrokestroke-widthopacity等属性定制圆弧阴影的外观,通过设置d属性修改其位置和大小。

对于第2个问题,思路是这样的:首先,鼠标按下(mousedown事件)时为<svg>添加<path>元素,同时设置外观参数,并将初始为falseisDragging属性设置为true;当鼠标开始移动(mousemove事件)时,如果isDragging == true,那么说明是在拖动,此时计算d属性的值(即第3个问题),并进行更新;当鼠标抬起(mouseup事件)时,如果isDragging == true,说明拖动已经结束,此时将isDragging重新设置为false,并做一些清理(如果需要的话)。

对于第3个问题,实际是绘制如图所示的图像(从(x0, y0)顺时针连线,r1r2是不同的半径):
在这里插入图片描述

此时<path>d属性的值应该是这样的结构:

M x0 y0
L x1 y1
A r2 r2 a1 isLargeArc isOuterClockwise x2 y2
L x3 y3
A r1 r1 a2 isLargeArc isInnerClockwise x0 y0
Z

其中:
a 1 = θ 2 − θ 1 , a 2 = 360 − a 1 a1 = θ_2 - θ_1, a2 = 360 - a1 a1=θ2θ1,a2=360a1
isLargeArc表示是大弧(1)还是小弧(0),isOuterClockwise和isInnerClockwise表示外层圆弧和内侧圆弧是否为顺时针。

这里需要考虑的问题有:

3.1 如何获取θ1和当前θ2?如何通过θ计算圆弧上点的坐标?
这个在上一篇已经写过,此处不再赘述。javascript-svg-在圆环上加入闪烁光标-CSDN博客

3.2 如何判断圆弧阴影区域是大弧还是小弧?
由于绘制时可能出现跨越0点的情况,如下图所示:
在这里插入图片描述
所以考虑先判断绘制方向是顺时针还是逆时针。首先默认都是小弧,如果满足以下条件之一则为大弧:
a) θ1 > θ2、绘制方向为顺时针、(θ2 + 360 - θ1) > 180
b) θ1 < θ2、绘制方向为逆时针、(θ1 + 360 - θ2) > 180
c) 如不满足a和b,同时满足abs(θ2 - θ1) > 180

3.3 如何判断绘制方向是顺时针还是逆时针?
此时需要考虑最近两个θ的大小关系。假设有两个全局变量startAnglelastAngle,以及一个标记方向的变量angleDirection。在mousedown事件中,先计算初始位置的θ,并赋值给startAnglelastAngle;在mousemove事件中,获得当前的θ(记作currentAngle),并通过这个函数来判断方向:

function determineDirection(currentAngle) {if (lastAngle != null) {const diff = currentAngle - lastAngle;if (diff > 0 && Math.abs(diff) < 180 || diff < -180) {angleDirection = 1; // Clockwise} else if (diff < 0 && Math.abs(diff) < 180 || diff > 180) {angleDirection = 0; // Counterclockwise}}lastAngle = currentAngle;console.log(angleDirection > 0 ? "Clockwise" : "Counterclockwise");
}

最后在mouseup事件中重置lastAngleangleDirection

3.4 如何计算isOuterClockwise和isInnerClockwise?
首先,由于绘制方向的问题,这俩一定是相反的。直接使用绘制方向是否为顺时针作为isOuterClockwise的值,isInnerClockwise只要与之相反即可。

代码结构

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
</head>
<body><div><svg width="100%" height="1000" xmlns="http://www.w3.org/2000/svg"><circle id="myCircle1" cx="600" cy="480" r="285" fill="#fff" stroke="#ccc" stroke-width="1" /><!-- 先隐藏line --><line x1="1" y1="11" x2="2" y2="2" id="cursor" stroke="#1F2744" stroke-width="1" style="display:none;"></line></svg></div>
<script>// 获取svg和circleconst svg = document.querySelector('svg');const circle = document.getElementById('myCircle1');let isDragging = false;let arcPath = null;let startAngle, lastAngle;// 1 for clockwise, 0 for counterclockwise, -1 for none. 这是方便写svg// 其实可以自定义let angleDirection = 1; // 添加监听circle.addEventListener('mousedown', (e) => {isDragging = true;// 使用窗口中的圆心和点击点计算夹角,省略函数的实现// 计算细节可看:https://blog.csdn.net/pxy7896/article/details/144256701let angle = calculateCircleInfo(centerX, centerY, radius, clickX, clickY);startAngle = angle;lastAngle = startAngle;// do something// 添加path元素,并做一些设置arcPath = document.createElementNS("http://www.w3.org/2000/svg", "path");arcPath.setAttribute("opacity", 0.3);arcPath.setAttribute("fill", "blue");arcPath.setAttribute("stroke", "blue");arcPath.setAttribute("stroke-width", 1);svg.appendChild(arcPath);});svg.addEventListener('mousemove', (e) => {if (isDragging) {// 使用窗口中的圆心和点击点计算夹角,省略函数的实现// 判断绘制方向并计算darcPath.setAttribute("d", d);}});svg.addEventListener('mouseup', (e) => {if(isDragging) {// 重置isDragging = false;lastAngle = null;angleDirection = 1;}
});</script>
</body>
</html>	

http://www.mrgr.cn/news/79277.html

相关文章:

  • lanqiaoOJ 3744:小蓝的智慧拼图购物 ← pair+优先队列
  • 行列式计算方法
  • c++笔记2
  • Elasticsearch数据迁移(快照)
  • Flume基础概念
  • Qt入门8——Qt文件
  • 初识树(二叉树,堆,并查集)
  • redis击穿,穿透,雪崩以及解决方案
  • Multimodal Few-Shot Learning with Frozen Language Models译文
  • 前端速通Blob、File、FileReader、ArrayBuffer、Base64...
  • Delphi-HTTP通讯及JSON解析
  • Yocto bitbake and codeSonar
  • 单链表---合并两个链表
  • Yagmail邮件发送库:如何用Python实现自动化邮件营销?
  • 【0356】Postgres内核 XLOG读取之 打开一个 logfile segment ( 3 - 1 )
  • MongoDB的简单使用
  • 深入浅出:SOME/IP-SD的工作原理与应用
  • axios笔记
  • HTML笔记()蜘蛛纸牌之卡牌拖拽
  • 基于STM32F4实现步进电机闭环控制实现(无PID)