Как получить полное вращение по часовой стрелке в D3

Я хочу, чтобы квадрат совершил полный оборот по часовой стрелке после паузы на половине оборота. Следующий код заставляет его делать половину оборота по часовой стрелке, а другую половину - против часовой стрелки, вопреки тому, что я ожидал.

var svg = d3.select('svg');

var s = svg.append("rect")
  .attr("width", 50)
  .attr("height", 50)
  .attr("x", -25)
  .attr("y", -25)
  .attr("fill", "red")
  .attr("transform", "translate(100,100)");

s
  .transition()
  .duration(1000)
  .attr("transform", "translate(100,100) rotate(180)")
  .transition()
  .delay(1000)
  .duration(1000)
  .attr("transform", "translate(100,100) rotate(360)");
* {
  margin: 0;
  padding: 0;
  border: 0;
}

body {
  background: #ffd;
}
<script src = "https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<svg></svg>

Я могу взломать такой код, разделяющий вторую половину поворота на две четверти поворота по часовой стрелке, но я хочу знать, есть ли более элегантное решение.

Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
1
0
260
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Если я использую D3v3, он не поворачивается на 180 градусов, если я переключаюсь на D3v4, он поворачивается на 180 градусов.

Вы можете интерполировать до 359,99. Он не следует за строковым интерполятором из документации, потому что вы также получаете scale() в преобразовании.

translate(100, 100) rotate(359.989990234375) scale(0.999,0.999)

Этого не произойдет, если вы напишете свой собственный интерполятор.

var svg = d3.select('svg');

var s=svg.append("rect")
.attr("width",50)
.attr("height",50)
.attr("x",-25)
.attr("y",-25)
.attr("fill","red")
.attr("transform","translate(100,100) rotate(0)");

s
.transition()
.duration(3000)
.ease(d3.easeLinear)
.attr("transform","translate(100,100) rotate(180)")
.transition()
.delay(2000)
.duration(3000)
.ease(d3.easeLinear)
.attrTween("transform", () => d3.interpolate("translate(100,100) rotate(180)", "translate(100,100) rotate(360)") );
<script src = "https://d3js.org/d3.v5.min.js"></script>

<svg></svg>

Виновником здесь является сам D3, а не какая-либо спецификация SVG.

Проблема в том, что ваш переход использует d3.interpolateTransform, как мы видим здесь:

var fullname = namespace(name), i = fullname === "transform" ? interpolateTransform : interpolate;

Это исходный код v4, а не v3, но принцип тот же, как вы можете видеть в фактическом коде v3:

var interpolate = nameNS == "transform" ? d3_interpolateTransform : d3_interpolate, name = d3.ns.qualify(nameNS);

Затем, если мы посмотрим в исходный код для interpolateTransform (опять же, v4, но v3 почти то же самое), мы увидим, что он использует функцию под названием parseSvg, которая вычисляет матрицу для нового преобразования:

function parseSvg(value) {
  if (value == null) return identity;
  if (!svgNode) svgNode = document.createElementNS("http://www.w3.org/2000/svg", "g");
  svgNode.setAttribute("transform", value);
  if (!(value = svgNode.transform.baseVal.consolidate())) return identity;
  value = value.matrix;
  return decompose(value.a, value.b, value.c, value.d, value.e, value.f);
}

Эта функция генерирует 0 в качестве окончательного значения в матрице, когда вы передаете ей rotate(360) (фактическое значение - -2.4492935982947064e-16, которое практически равно нулю).

Решение

Здесь есть несколько возможных решений, самое простое - использовать interpolateString вместо interpolateTransform.

Кроме того, поскольку ваш код использует D3 v3, вы можете воспользоваться преимуществами d3.transform(), который был удален в v4 / v5:

d3.interpolateString(d3.transform(d3.select(this).attr("transform")), "translate(100,100) rotate(360)")

Вот ваш код с этим изменением:

var svg = d3.select('svg');

var s = svg.append("rect")
  .attr("width", 50)
  .attr("height", 50)
  .attr("x", -25)
  .attr("y", -25)
  .attr("fill", "red")
  .attr("transform", "translate(100,100)");

s.transition()
  .duration(1000)
  .attr("transform", "translate(100,100) rotate(180)")
  .transition()
  .delay(1000)
  .duration(1000)
  .attrTween("transform", function() {
    return d3.interpolateString(d3.transform(d3.select(this).attr("transform")), "translate(100,100) rotate(360)")
  });
<svg></svg>
<script src = "https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js"></script>

Другие вопросы по теме