d3按路径设置组动画



这是我的下一个问题,以增强原来的问题。

所以我正在尝试的是沿着填充路径设置三角形标记和文本工具提示的动画。

var chart = d3.select("#speedometer");
const arc = d3
.arc()
.outerRadius(120)
.innerRadius(90)
.startAngle(-Math.PI / 2);
chart
.append("path")
.datum({
endAngle: Math.PI / 2
})
.attr("transform", "translate(160, 180)")
.attr("class", "background")
.style("fill", "#495270")
.attr("d", arc);
const mainGroup = chart.append('g')
.datum({
endAngle: -Math.PI / 2
});
const triangle = mainGroup
.append('path')
.attr("d", "M3.937,0,7.873,14H0Z")
.datum({
endAngle: -Math.PI / 2
})
const text = mainGroup.append('text')
.text('hello there')
.attr('transform', 'rotate(180)')
.datum({
endAngle: -Math.PI / 2
})
.transition()
.duration(3000)
.attrTween("transform", function(d) {
const topVal = (300 / 2 - 16);
const interpolate = d3.interpolate(d.endAngle, newAngle);
return function(t) {
const angleRadians = interpolate(t);
const angleDegrees = 360 * angleRadians / (2 * Math.PI);
return `
rotate(${angleDegrees})
`;
};
});
const newAngle = (70 / 100) * Math.PI - Math.PI / 2;
const foreground = chart
.append("path")
.datum({
endAngle: -Math.PI / 2
})
.style("fill", "rgb(50, 188, 228)")
.attr("transform", "translate(160, 180)")
.attr("d", arc);
foreground
.transition()
.duration(3000)
.attrTween("d", function(d) {
const interpolate = d3.interpolate(d.endAngle, newAngle);
return function(t) {
d.endAngle = interpolate(t);
return arc(d);
};
});
mainGroup
.transition()
.duration(3000)
.attrTween("transform", function(d) {
const topVal = (300 / 2 - 16);
const interpolate = d3.interpolate(d.endAngle, newAngle);
return function(t) {
const angleRadians = interpolate(t);
const angleDegrees = 360 * angleRadians / (2 * Math.PI);
return `
translate(158 176)
rotate(${angleDegrees + 180} 3.5 7)
translate(0 ${topVal})
`;
};
});
function pathTween(path) {
const length = path.node().getTotalLength(); // Get the length of the path
const r = d3.interpolate(0, length); // Set up interpolation from 0 to the path length
return function(t) {
const point = path.node().getPointAtLength(r(t)); // Get the next point along the path
d3
.select(this) // Select the circle
.attr("transform", `translate(${point.x}, ${point.y})`);
};
}
.main-wrapper {
max-width: 80%;
margin: 20px auto;
}
.element {
display: flex;
flex-flow: column nowrap;
margin-bottom: 20px;
border: 1px solid rgba(0, 0, 0, 0.4);
padding: 20px;
border-radius: 6px;
}
.element .title {
margin-bottom: 4px;
font-weight: 500;
}
.element .description {
margin-bottom: 10px;
color: rgba(0, 0, 0, 0.4);
}
#speedometer {
width: 300px;
height: 300px;
overflow: visible !important;
}
canvas {
width: 100%;
height: 100%;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.2.0/d3.min.js"></script>
<div class="main-wrapper">
<section class="ui-section">
<div class="element">
<div class="title">
Speedometr
</div>
<div class="content">
<svg id="speedometer" viewbox="0 0 300 300"></svg>
</div>
</div>
</section>
</div>

所以这里的主要问题是旋转文本。它假定处于正常位置。在这种情况下动画应该如何?如何设置整个组的动画并控制每个节点??

首先,您可以通过对文本使用180 - angleDegrees而不是angleDegrees来偏移转弯。

其次,你可能希望文本始终与弧线保持相同的距离,不要太近以至于重叠,也不要太远因为看起来很奇怪。为此,一种解决方案是将节点设为text-anchor: middle,然后在转换期间对其进行定位。

var chart = d3.select("#speedometer");
const arc = d3
.arc()
.outerRadius(120)
.innerRadius(90)
.startAngle(-Math.PI / 2);
chart
.append("path")
.datum({
endAngle: Math.PI / 2
})
.attr("transform", "translate(160, 180)")
.attr("class", "background")
.style("fill", "#495270")
.attr("d", arc);
const mainGroup = chart.append('g')
.datum({
endAngle: -Math.PI / 2
});
const triangle = mainGroup
.append('path')
.attr("d", "M3.937,0,7.873,14H0Z")
.datum({
endAngle: -Math.PI / 2
})
const text = mainGroup.append('text')
.text('hello there')
.datum({
endAngle: -Math.PI / 2
})
.attr("text-anchor", "middle") // to more easily position the text
.transition()
.duration(3000)
.attrTween("transform", function(d) {
const topVal = (300 / 2 - 16);
const interpolateAngle = d3.interpolate(d.endAngle, newAngle);
// We want to add some offset so the text is always easily visible
// Think about what happens when the following functions are called with
// angles -90, -45, 0, 45, 90
const textWidth = this.getBBox().width;
const offsetX = function(angle) {
return (angle / 90) * textWidth / 2;
};
const offsetY = function(angle) {
if (angle < 0) {
return offsetY(-angle);
}

// The -4 and -3 are a little bit trial and error, and can be
// tweaked further to
return -4 + (1 - angle / 90) * -3;
};
return function(t) {
const angleRadians = interpolateAngle(t);
const angleDegrees = 360 * angleRadians / (2 * Math.PI);
return `
translate(0 15)
rotate(${180 - angleDegrees})
translate(${offsetX(angleDegrees)} ${offsetY(angleDegrees)})
`;
};
});
const newAngle = (70 / 100) * Math.PI - Math.PI / 2;
const foreground = chart
.append("path")
.datum({
endAngle: -Math.PI / 2
})
.style("fill", "rgb(50, 188, 228)")
.attr("transform", "translate(160, 180)")
.attr("d", arc);
foreground
.transition()
.duration(3000)
.attrTween("d", function(d) {
const interpolate = d3.interpolate(d.endAngle, newAngle);
return function(t) {
d.endAngle = interpolate(t);
return arc(d);
};
});
mainGroup
.transition()
.duration(3000)
.attrTween("transform", function(d) {
const topVal = (300 / 2 - 16);
const interpolate = d3.interpolate(d.endAngle, newAngle);
return function(t) {
const angleRadians = interpolate(t);
const angleDegrees = 360 * angleRadians / (2 * Math.PI);
return `
translate(158 176)
rotate(${angleDegrees + 180} 3.5 7)
translate(0 ${topVal})
`;
};
});
function pathTween(path) {
const length = path.node().getTotalLength(); // Get the length of the path
const r = d3.interpolate(0, length); // Set up interpolation from 0 to the path length
return function(t) {
const point = path.node().getPointAtLength(r(t)); // Get the next point along the path
d3
.select(this) // Select the circle
.attr("transform", `translate(${point.x}, ${point.y})`);
};
}
.main-wrapper {
max-width: 80%;
margin: 20px auto;
}
.element {
display: flex;
flex-flow: column nowrap;
margin-bottom: 20px;
border: 1px solid rgba(0, 0, 0, 0.4);
padding: 20px;
border-radius: 6px;
}
.element .title {
margin-bottom: 4px;
font-weight: 500;
}
.element .description {
margin-bottom: 10px;
color: rgba(0, 0, 0, 0.4);
}
#speedometer {
width: 300px;
height: 300px;
overflow: visible !important;
}
canvas {
width: 100%;
height: 100%;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.2.0/d3.min.js"></script>
<div class="main-wrapper">
<section class="ui-section">
<div class="element">
<div class="title">
Speedometr
</div>
<div class="content">
<svg id="speedometer" viewbox="0 0 300 300"></svg>
</div>
</div>
</section>
</div>

最新更新