Итак, я пытаюсь анимировать маркер в виде треугольника и текстовую подсказку вдоль пути заполнения.
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(); // Получить длину path
const r = d3.interpolate(0, length); // Настройте интерполяцию от 0 до длины path
return function(t) {
const point = path.node().getPointAtLength(r(t)); // Получите следующую точку на пути
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>
Итак, основная проблема здесь - вращение текста. Он должен быть в нормальном положении.
И какой в этом случае должна быть анимация?
Как я могу анимировать всю группу и управлять каждым узлом?
Свободный перевод вопроса d3 animate group by path от участника @David.
Во-первых, вы можете компенсировать поворот, используя для текста 180 - angleDegrees
вместо angleDegrees
.
Во-вторых, вы, вероятно, хотите, чтобы текст всегда находился примерно на одинаковом расстоянии от дуги, не слишком близко, чтобы он не перекрывался, или слишком далеко, потому что это выглядит странно. Для этого есть одно из решений - сделать узел text-anchor: middle
, а затем разместить его во время transition
.
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") // чтобы было легче расположить текст
.transition()
.duration(3000)
.attrTween("transform", function(d) {
const topVal = (300 / 2 - 16);
const interpolateAngle = d3.interpolate(d.endAngle, newAngle);
// Мы хотим добавить смещение, чтобы текст всегда был хорошо виден
// Подумайте, что происходит, когда следующие функции вызываются с
// углами -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);
}
// -4 и -3 - это метод проб и ошибок, и их можно
// доработан до
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(); // Получите длину пути
const r = d3.interpolate(0, length); // Настройте интерполяцию от 0 до длины пути
return function(t) {
const point = path.node().getPointAtLength(r(t)); // Получить следующую точку на пути
d3
.select(this) // Выбрать окружность
.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>
Свободный перевод ответа от участника @Ruben Helsloot.
Айфон мало держит заряд, разбираемся с проблемой вместе с AppLab
Всем привет, не подскажете почему счетчик counter не увеличивается при инкременте? По идее ведь у меня три картины и трижды должен сработать
Подскажите, в чём может быть проблема? Не округляются десятичные(
Есть массив из трех целых элементов, один из элементов отличается от других, остальные одинаковы, нужно сравнить элементы, и вывести индекс...