我需要创建一个圆,并在发生鼠标按下事件时将其移动到SVG路径中的最近点
代码:
var points = [[180,300],[234,335],[288,310],[350,290],[405,300],[430,305],[475,310],[513,300],[550,280]];
var width = 1000, height = 600;
var line = d3.svg.line().interpolate("cardinal");
var svg = d3.select("#Con").append("svg").attr("width", width).attr("height", height);
var path = svg.append("path").datum(points).attr("d", line);
var line = svg.append("line");
var circle = svg.append(" circle").attr("cx", -10).attr("cy", -10).attr("r", 3.5);
svg.append("rect").attr("width", width).attr("height", height).on("mousedown", mouseclick);
function mouseclick() {
var m = d3.mouse(this),p = closestPoint(path.node(), m);
circle.attr("cx", p[0]).attr("cy", p[1]);
}
function closestPoint(pathNode, point) {
var pathLength = pathNode.getTotalLength(),precision = 8,best,bestLength,bestDistance = Infinity;
for (var scan, scanLength = 0, scanDistance; scanLength <= pathLength; scanLength += precision) {
if ((scanDistance = distance2(scan = pathNode.getPointAtLength(scanLength))) < bestDistance) {
best = scan, bestLength = scanLength, bestDistance = scanDistance;
}
}
precision /= 2;
while (precision > 0.5) {
var before,after,beforeLength,afterLength,beforeDistance,afterDistance;
if ((beforeLength = bestLength - precision) >= 0 && (beforeDistance = distance2(before = pathNode.getPointAtLength(beforeLength))) < bestDistance) {
best = before, bestLength = beforeLength, bestDistance = beforeDistance;
} else if ((afterLength = bestLength + precision) <= pathLength && (afterDistance = distance2(after = pathNode.getPointAtLength(afterLength))) < bestDistance) {
best = after, bestLength = afterLength, bestDistance = afterDistance;
} else {
precision /= 2;
}
}
best = [best.x, best.y];
best.distance = Math.sqrt(bestDistance);
return best;
function distance2(p) {
var dx = p.x - point[0],dy = p.y - point[1];
return dx * dx + dy * dy;
}
}
在SVG空间中单击时,我需要将圆移动到路径中最接近的点
在我的代码中,圆没有动画移动,我需要对其进行动画处理,以使其在点到点的路径上移动
无论是大距离还是小距离,我都需要它始终以一个速度移动
像这样:
答案 0 :(得分:3)
我没有使用您的代码,但希望您能理解。
我不是使用圆圈,而是使用轨道:
<use id="theUse" xlink:href="#track"
此轨道的stroke-dasharray
为".1 398.80"
,这意味着该轨道的长度为.1
(非常小),并且间隙为398.80
。 stroke-width
为7,而stroke-linecap= "round"
为7,这就是将破折号转换成一个圆圈。我正在使用stroke-dashoffset
更改破折号(圆圈)的位置,并且为了使更改具有动画效果,我在CSS中使用了transition: stroke-dashoffset 1s;
。
希望对您有帮助。
let m;
let L = track.getTotalLength();
let _start = {x:180,y:30}
let _end = {x:550,y:280}
let l = dist(_start, _end);
theUse.setAttributeNS(null,"stroke-dashoffset",L);
svg.addEventListener("click",(e)=>{
m = oMousePosSVG(e)
let pos = m.x - _start.x;
let theDistance = map(pos,_start.x,_end.x,0,L)
let s_dof = constrain(L-theDistance, .5, L-.5)
theUse.setAttributeNS(null,"stroke-dashoffset",s_dof)
})
function oMousePosSVG(e) {
var p = svg.createSVGPoint();
p.x = e.clientX;
p.y = e.clientY;
var ctm = svg.getScreenCTM().inverse();
var p = p.matrixTransform(ctm);
return p;
}
function dist(p1, p2) {
let dx = p2.x - p1.x;
let dy = p2.y - p1.y;
return Math.sqrt(dx * dx + dy * dy);
}
function map(n, a, b, _a, _b) {
let d = b - a;
let _d = _b - _a;
let u = _d / d;
return _a + n * u;
}
function constrain(n, low, high) {
return Math.max(Math.min(n, high), low);
};
svg {
border: 1px solid;
}
path {
fill: none;
}
#theUse {
transition: stroke-dashoffset 1s;
}
<svg id="svg" viewBox="150 250 450 100">
<defs>
<path id="track" d="M180,300Q223.2,334,234,335C250.2,336.5,270.6,316.75,288,310S332.45,291.5,350,290S393,297.75,405,300S419.5,303.5,430,305S462.55,310.75,475,310S501.75,304.5,513,300Q520.5,297,550,280"></path>
</defs>
<use xlink:href="#track" stroke="black" />
<use id="theUse" xlink:href="#track" stroke-width="7" stroke-dasharray = ".1 398.80" stroke="red" stroke-linecap= "round" />
</svg>
答案 1 :(得分:0)
此答案修改您的代码,以将直线从行的开头或最后一次单击鼠标移到当前鼠标单击。通过用setTimeout
调用动画方法,直到圆从行的开始移动到单击鼠标的点,动画化了圆的移动。
有趣的代码在这里:
// getAnimate returns a function that is within a closure
function getAnimate(pLength, path, currentIndex, finishPos, forward){
let animate = function (){
let scan = path.node().getPointAtLength(currentIndex);
if (scan.x < finishPos || !forward && scan.x > finishPos){
circle.attr("cx", scan.x).attr("cy", scan.y);
}
if (forward){
currentIndex += 1;
lastIndex = currentIndex;
if (scan.x < finishPos){
setTimeout(animate, 50);
}
} else {
currentIndex -= 1;
lastIndex = currentIndex;
if (scan.x > finishPos){
setTimeout(animate, 50);
}
}
}
return animate;
}
var points = [[80,100],[134,115],[188,130],[250,120],[305,120],[330,101],[375,103],[413,100],[550,90]];
var width = 500, height = 200;
var line = d3.svg.line().interpolate("cardinal");
var svg = d3.select("#Con").append("svg").attr("width", width).attr("height", height);
var path = svg.append("path").datum(points).attr("d", line);
var line = svg.append("line");
var circle = svg.append("circle").attr("cx", -10).attr("cy", -10).attr("r", 3.5);
svg.append("rect").attr("width", width).attr("height", height).on("mousedown", mouseclick);
var lastIndex = 0;
function mouseclick() {
let m = d3.mouse(this);
let p = closestPoint(path.node(), m);
let forward = true;
let currentPoint = path.node().getPointAtLength(lastIndex);
if (p[0] < currentPoint.x){
forward = false;
}
let pathLength = path.node().getTotalLength();
getAnimate(pathLength, path, lastIndex, p[0], forward)();
}
function getAnimate(pLength, path, currentIndex, finishPos, forward){
let animate = function (){
let scan = path.node().getPointAtLength(currentIndex);
if (scan.x < finishPos || !forward && scan.x > finishPos){
circle.attr("cx", scan.x).attr("cy", scan.y);
}
if (forward){
currentIndex += 1;
lastIndex = currentIndex;
if (scan.x < finishPos){
setTimeout(animate, 50);
}
} else {
currentIndex -= 1;
lastIndex = currentIndex;
if (scan.x > finishPos){
setTimeout(animate, 50);
}
}
}
return animate;
}
function closestPoint(pathNode, point) {
var pathLength = pathNode.getTotalLength(),precision = 8,best,bestLength,bestDistance = Infinity;
for (var scan, scanLength = 0, scanDistance; scanLength <= pathLength; scanLength += precision) {
if ((scanDistance = distance2(scan = pathNode.getPointAtLength(scanLength))) < bestDistance) {
best = scan, bestLength = scanLength, bestDistance = scanDistance;
}
}
precision /= 2;
while (precision > 0.5) {
var before,after,beforeLength,afterLength,beforeDistance,afterDistance;
if ((beforeLength = bestLength - precision) >= 0 && (beforeDistance = distance2(before = pathNode.getPointAtLength(beforeLength))) < bestDistance) {
best = before, bestLength = beforeLength, bestDistance = beforeDistance;
} else if ((afterLength = bestLength + precision) <= pathLength && (afterDistance = distance2(after = pathNode.getPointAtLength(afterLength))) < bestDistance) {
best = after, bestLength = afterLength, bestDistance = afterDistance;
} else {
precision /= 2;
}
}
best = [best.x, best.y];
best.distance = Math.sqrt(bestDistance);
return best;
function distance2(p) {
var dx = p.x - point[0],dy = p.y - point[1];
return dx * dx + dy * dy;
}
}
* {
margin: 0;
padding: 0;
}
#Con {
border: 1px solid black;
margin: auto;
width: 1000px;
height: 600px;
}
#Map{
z-index: -1000;
position: absolute;
}
#Cha {
position: absolute;
}
path {
z-index: 1000;
fill: none;
stroke: #000;
stroke-width: 1.5px;
}
line {
fill: none;
stroke: red;
stroke-width: 1.5px;
}
circle {
fill: red;
}
rect {
fill: none;
pointer-events: all;
}
<script type="text/javascript" src="http://mbostock.github.com/d3/d3.js?2.5.0"></script>
<div id="Con"></div>