我试图让一个正在排水的圆圈动画,并且效果很好。但是我想要一个"标记"跟随排水水平,我很难理解如何做到这一点。
我已经在这篇文章中嵌入了一个示例,其中圆圈填充动画,数字动画。
最终状态可以在这里看到:
问题是;我想放置" XXX使用"基于排水百分比的标记。但我很难找到如何实现这一点。
所以它必须上下移动,但也要根据百分比左右移动。
我的代码如下:
function foo({x = 10}) {
return x;
}
foo(); // TypeError: can't convert undefined to object
foo({}); // 10

const usedAmount = 200;
const totalAmount = 400;
const radius = 120;
const valuePercent = (usedAmount / totalAmount) * 100;
var svg = d3.select("svg"),
width = +svg.attr("width"),
height = +svg.attr("height"),
g = svg.append("g").attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
let grad = svg
.append("defs")
.append("linearGradient")
.attr("id", "grad")
.attr("x1", "0%")
.attr("x2", "0%")
.attr("y1", "0%")
.attr("y2", "0%");
grad.append("stop").attr("offset", "1%").style("stop-color", '#000');
grad.append("stop").attr("offset", "1%").style("stop-color", '#ccc');
let arc = d3.arc()
.innerRadius( radius - 40 )
.outerRadius( radius )
.endAngle(2 * Math.PI)
.startAngle(0 * Math.PI);
let cutout = svg.select('defs')
.append('clipPath')
.attr('clip-rule', 'evenodd')
.attr('id', 'cutout')
.append("path")
.attr('transform', 'translate(' + radius + ',' + radius + ')')
.attr('d', arc)
.attr('clip-rule', 'evenodd')
.attr('fill', '#ccc');
svg.append("circle")
.attr("cx", radius)
.attr("cy", radius)
.attr("r", radius)
.attr("clip-path", "url(#cutout)") // Apply the mask
.attr("fill", "url(#grad)");
grad
.transition()
.duration(3000)
.ease(d3.easeQuad)
.delay(300)
.attr("y1", valuePercent + 1 + '%');
var marker = svg.append('g')
.attr('class', 'gauge__fillup__follow')
.attr("transform", "translate(" + 200 + "," + 50 + ")");
marker.append("rect")
.attr("x", 10)
.attr("y", 6)
.attr("fill", "#000")
.attr("width", 35)
.attr("height", 3);
marker.append('svg:text')
.attr('class', 'label')
.attr('z-index', '4')
.attr('x', 50)
.attr('y', 0)
.attr('dy', 12)
.attr('text-anchor', 'left')
.datum({textContent: ''})
.text(200)
.transition()
.duration(3000)
.delay(300)
.ease(d3.easeQuad)
.tween("text", function(d) {
const i = d3.interpolate(0, this.textContent, d);
return (t) => {
d3.select(this).text(Math.round(i(t)));
};
});
marker.append('svg:text')
.attr('class', 'label')
.attr('color', 'white')
.attr('z-index', '4')
.attr('x', 50)
.attr('y', 16)
.attr('dy', 12)
.attr('text-anchor', 'left')
.text('used');

答案 0 :(得分:4)
您只需要将组翻译相同的数量:
marker.transition()
.duration(3000)
.ease(d3.easeQuad)
.delay(300)
.attr("transform", "translate(" + 220 + ","
+ ((usedAmount / totalAmount) * (radius * 2)) + ")");
以下是演示:
const usedAmount = 200;
const totalAmount = 400;
const radius = 120;
const valuePercent = (usedAmount / totalAmount) * 100;
var svg = d3.select("svg"),
width = +svg.attr("width"),
height = +svg.attr("height"),
g = svg.append("g").attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
let grad = svg
.append("defs")
.append("linearGradient")
.attr("id", "grad")
.attr("x1", "0%")
.attr("x2", "0%")
.attr("y1", "0%")
.attr("y2", "0%");
grad.append("stop").attr("offset", "1%").style("stop-color", '#000');
grad.append("stop").attr("offset", "1%").style("stop-color", '#ccc');
let arc = d3.arc()
.innerRadius(radius - 40)
.outerRadius(radius)
.endAngle(2 * Math.PI)
.startAngle(0 * Math.PI);
let cutout = svg.select('defs')
.append('clipPath')
.attr('clip-rule', 'evenodd')
.attr('id', 'cutout')
.append("path")
.attr('transform', 'translate(' + radius + ',' + radius + ')')
.attr('d', arc)
.attr('clip-rule', 'evenodd')
.attr('fill', '#ccc');
svg.append("circle")
.attr("cx", radius)
.attr("cy", radius)
.attr("r", radius)
.attr("clip-path", "url(#cutout)") // Apply the mask
.attr("fill", "url(#grad)");
grad
.transition()
.duration(3000)
.ease(d3.easeQuad)
.delay(300)
.attr("y1", valuePercent + 1 + '%');
var marker = svg.append('g')
.attr('class', 'gauge__fillup__follow')
.attr("transform", "translate(" + 220 + "," + 0 + ")");
marker.append("rect")
.attr("x", 10)
.attr("y", 1)
.attr("fill", "#000")
.attr("width", 35)
.attr("height", 3);
marker.append('svg:text')
.attr('class', 'label')
.attr('z-index', '4')
.attr('x', 50)
.attr('y', -6)
.attr('dy', 12)
.attr('text-anchor', 'left')
.datum({
textContent: ''
})
.text(200)
.transition()
.duration(3000)
.delay(300)
.ease(d3.easeQuad)
.tween("text", function(d) {
const i = d3.interpolate(0, this.textContent, d);
return (t) => {
d3.select(this).text(Math.round(i(t)));
};
});
marker.append('svg:text')
.attr('class', 'label')
.attr('color', 'white')
.attr('z-index', '4')
.attr('x', 50)
.attr('y', 10)
.attr('dy', 12)
.attr('text-anchor', 'left')
.text('used');
marker.transition()
.duration(3000)
.ease(d3.easeQuad)
.delay(300)
.attr("transform", "translate(" + 220 + "," + ((usedAmount / totalAmount )*(radius*2)) + ")");
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.5.0/d3.min.js"></script>
<svg height="500" width="500"></svg>
编辑:以下是attrTween
的翻译,从上到下,从左到右:
marker.transition()
.duration(3000)
.ease(d3.easeQuad)
.delay(300)
.attrTween("transform", function() {
return function(t) {
return "translate(" + (radius * (1 + (Math.sin(Math.PI / 2 * t)))) + ","
+ (((usedAmount / totalAmount) * (radius * 2)) * (1 - (Math.cos(Math.PI / 2 * t)))) + ")"
}
});
以下是演示:
const usedAmount = 200;
const totalAmount = 400;
const radius = 120;
const valuePercent = (usedAmount / totalAmount) * 100;
var svg = d3.select("svg"),
width = +svg.attr("width"),
height = +svg.attr("height"),
g = svg.append("g").attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
let grad = svg
.append("defs")
.append("linearGradient")
.attr("id", "grad")
.attr("x1", "0%")
.attr("x2", "0%")
.attr("y1", "0%")
.attr("y2", "0%");
grad.append("stop").attr("offset", "1%").style("stop-color", '#000');
grad.append("stop").attr("offset", "1%").style("stop-color", '#ccc');
let arc = d3.arc()
.innerRadius(radius - 40)
.outerRadius(radius)
.endAngle(2 * Math.PI)
.startAngle(0 * Math.PI);
let cutout = svg.select('defs')
.append('clipPath')
.attr('clip-rule', 'evenodd')
.attr('id', 'cutout')
.append("path")
.attr('transform', 'translate(' + radius + ',' + radius + ')')
.attr('d', arc)
.attr('clip-rule', 'evenodd')
.attr('fill', '#ccc');
svg.append("circle")
.attr("cx", radius)
.attr("cy", radius)
.attr("r", radius)
.attr("clip-path", "url(#cutout)") // Apply the mask
.attr("fill", "url(#grad)");
grad
.transition()
.duration(3000)
.ease(d3.easeQuad)
.delay(300)
.attr("y1", valuePercent + 1 + '%');
var marker = svg.append('g')
.attr('class', 'gauge__fillup__follow')
.attr("transform", "translate(" + (radius) + "," + 0 + ")");
marker.append("rect")
.attr("x", 10)
.attr("y", 1)
.attr("fill", "#000")
.attr("width", 35)
.attr("height", 3);
marker.append('svg:text')
.attr('class', 'label')
.attr('z-index', '4')
.attr('x', 50)
.attr('y', -6)
.attr('dy', 12)
.attr('text-anchor', 'left')
.datum({
textContent: ''
})
.text(200)
.transition()
.duration(3000)
.delay(300)
.ease(d3.easeQuad)
.tween("text", function(d) {
const i = d3.interpolate(0, this.textContent, d);
return (t) => {
d3.select(this).text(Math.round(i(t)));
};
});
marker.append('svg:text')
.attr('class', 'label')
.attr('color', 'white')
.attr('z-index', '4')
.attr('x', 50)
.attr('y', 10)
.attr('dy', 12)
.attr('text-anchor', 'left')
.text('used');
marker.transition()
.duration(3000)
.ease(d3.easeQuad)
.delay(300)
.attrTween("transform", function() {
return function(t) {
return "translate(" + (radius * (1 + (Math.sin(Math.PI / 2 * t)))) + "," + (((usedAmount / totalAmount) * (radius * 2)) * (1 - (Math.cos(Math.PI / 2 * t)))) + ")"
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.5.0/d3.min.js"></script>
<svg height="500" width="500"></svg>
答案 1 :(得分:1)
你的问题让我意识到我并不像我想的那样理解三角学,所以我对它进行了尝试。一旦我学会了pi和sine的基本原理,答案就变得非常明确了。
我阅读的精彩文章引导我回答:https://betterexplained.com/articles/intuitive-understanding-of-sine-waves/
https://jsfiddle.net/zk0wsq5a/2/
marker.transition()
.duration(3000)
.ease(d3.easeQuad)
.delay(300)
.attrTween("transform", function() {
return function(t) {
let distance = Math.PI * t * (usedAmount / totalAmount)
let x = radius + (radius * Math.sin(distance))
let y = radius * (1-Math.cos(distance))
return `translate(${x} ,${y})`
}
});