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属性的值应该是这样的结构:

shell 复制代码
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=360−a1

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),并通过这个函数来判断方向:

javascript 复制代码
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只要与之相反即可。

代码结构

html 复制代码
<!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和circle
	const 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/144256701
		let 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);
		// 做一些设置
		svg.appendChild(arcPath);
	});
	svg.addEventListener('mousemove', (e) => {
    	if (isDragging) {
			// 使用窗口中的圆心和点击点计算夹角,省略函数的实现
			// 判断绘制方向并计算d
        	arcPath.setAttribute("d", d);
    	}
	});

	svg.addEventListener('mouseup', (e) => {
		if(isDragging) {
			// 重置
			isDragging = false;
			lastAngle = null;
			angleDirection = 1;
		}
});
	
</script>
</body>
</html>	
相关推荐
码农丁丁3 分钟前
[前端]mac安装nvm(node.js)多版本管理
前端·macos·node.js·nvm
TWenYuan22 分钟前
【上传文件过大进行的切片式上传】
javascript·vue.js
正小安22 分钟前
Vite 系列课程|1课程道路,2什么是构建工具
前端·vite
校园卡已办38 分钟前
PHP木马编写
android·开发语言·php
资源补给站39 分钟前
大恒相机开发(1)—Python调用采集彩色图像并另存为本地
开发语言·python·数码相机
阿髙1 小时前
ios的safari下载文件 文件名乱码
前端·axios·safari·下载
LaiJying1 小时前
图书馆管理系统(四)基于jquery、ajax--完结篇
前端·ajax·jquery
风清云淡_A1 小时前
【原生js案例】ajax的简易封装实现后端数据交互
前端·javascript
云空1 小时前
《探索QT 5.14.1:功能、特性与应用全解析》
开发语言·qt
程序猿-瑞瑞1 小时前
23 go语言(golang) - gin框架安装及使用(四)
开发语言·golang·gin