D3-v3 анимация путей и точек

1562726

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

В D3 есть два основных метода анимации траектории, в зависимости от того, анимируете ли вы линию, точку или и то, и другое. Первый называется интерполяцией Stroke-Dash, а второй называется интерполяцией точки вдоль пути, различия очень мелкие. Часто вы можете использовать любой из этих методов для достижения одинакового типа анимации в целом. Вкратце, разница заключается в том, что в точке-вдоль-пути вы вычисляете общую длину пути для анимации, а в штрих-тире вы анимируете пробелы (тире), взятые из штриха (линия-путь). Таким образом, штрих-тире можно представить как боковые стороны киноленты с отверстиями, проколотыми повсюду, за исключением того, что вы можете изменять длину отверстий (или штрихов) для увеличения или уменьшения точности. Вы анимируете, рисуя от одной черточки к другой.
Точка в пути фокусируется на общей длине пути, в результате чего вы выполняете немного больше математических операций. Точка в пути всегда была для меня скорее запасным вариантом, поскольку она может анимировать в некоторых ситуациях, в которых штрих-тире не может, например, с градиентами. Но различия действительно незначительны. Некоторые считают, что лучше работать с canvas, webgl и т. д. На практике я не видел большой разницы в производительности ни в одной из этих техник.
Для D3.V3 вы загружаете свои спорные данные (обойти механизм загрузки D3 очень выполнимо, но вы должны преобразовать свои данные в хороший формат Geo-JSON перед внедрением), поскольку D3 дает много преимуществ для эффективности данных, которые он распознает. . Вещи, которые происходят в бэкэнде, обычно облегчают вашу жизнь в будущем. В другой статье я расскажу, как обойти загрузчик и внедрить собственные свернутые данные. На данный момент…

var Tiles = L.tileLayer(' {
  attribution: '<a href=" target="_blank">Terms &amp; Feedback</a>'
    });
 var map = L.map('map')
 	.addLayer(Tiles)
    .setView([33.215125313, -110.523415], 14);
 var svg = d3.select(map.getPanes().overlayPane).append("svg");
 var g = svg.append("g").attr("class", "leaflet-zoom-hide");

Вышеупомянутое устанавливает наш тайловый слой или фрагменты карты, карта добавляет слой, а setview фокусирует карту на области, добавленное число устанавливает уровень масштабирования. Svg добавляет слой d3 для рендеринга svg на карту, а g обрабатывает ошибку тени, если вы не включите g, вы увидите постоянные следы пути при увеличении или уменьшении масштаба.
После загрузки обычной практикой является сопоставление или фильтрация вашей коллекции объектов каким-либо методом, чтобы лучше всего обслуживать некоторое подмножество. Хорошее эмпирическое правило: используйте фильтр, чтобы обеспечить данные для каждой точки или точность точек (1e7 или сколько 0,000001 и т. д.), или, если у вас есть данные, ошибку расстояния, метод перемещения и т. д.

d3.json("points.geojson", function(features) {
    //from this point forward, your entire program
    //should be within this function
var data = features.collection.filter(function(d) {
  if ( d.id === points ){
    return d.id;
    };
})

})

Мы загрузили данные и отфильтровали их. Весь предстоящий код находится в функции загрузки d3.json.

var transform = d3.geo.transform({ point: projectPoint });
//these are changed if using V4, to d3.geoPath, incase you're using 
//a newer version and wonder why yours doesn't work
var path = d3.geo.path().projection(transform);
function projectPoint(x, y) {
  var point = map.latLngToLayerPoint(new L.LatLng(y, x));
    this.stream.point(point.x, point.y);
}
var toLine = d3.svg.line()
  .interpolate('linear')
    .x(function(d) { return applyLatLngToLayer(d).x })
    .y(function(d) { return applyLatLngToLayer(d).y });
    
function applyLatLngToLayer(d) {
  var y = d.geometry.coordinates[1]
    var x = d.geometry.coordinates[0]
    return map.latLngToLayerPoint(new L.LatLng(y, x))
}

Путь и преобразование принимают обычные координаты и превращают их в координаты svg. Project, применяет эти координаты обратно к слою карты листовки, используя текущий поток. Line интерполирует наши координаты между точками, а последняя функция — это метод, который мы используем для применения координат геометрии к мапплееру.

//Line to animate, between coordinates
var linePath = g.selectAll(".linePaths")
  .data([data])
    .enter()
    .append("path")
    .attr("class", "linePaths");
    
//This is the point to animate
var marker = g.append('circle')
  .attr('r', 6)
    .attr('id', 'marker')
    .attr('class', 'animatedMarker');
    
//now we add all the points, so we can animate to them
var allPoints = g.selectAll('circle')
  .data(data)
    .enter()
    .append('circle')
    .attr('r', 2)
    .attr('class', d => "waypoints " + "c" + d.properties.time) //e6 shorthand
    .style('opacity', 0);

Здесь много всего происходит, но также и много повторений. Вы заметите, что у маркера нет удаления данных, потому что мы создаем маркер с нуля, поэтому нам не нужно привязывать его к данным. Кроме того, он будет «сливаться» с данными других точек благодаря общему выбору окружности. Но заметьте, в данном случае создать — значит добавить, а в другом случае мы выбираем все точки, у нас нет фактического пути линии, поэтому, по сути, это те же данные, но мы создадим пути линий на иди, как увидишь.

//this just adjusts the paths and points whenever the view resets, so everything
//stays in relation
map.on("viewreset", reset);
//this draws on the map
reset();

//here is the actual reset function for user reposition of the map
function reset() {
  var bounds = path.bounds(features),
    	topLeft = bounds[0],
        bottomRight = bounds[1];
        
    svg.attr("width", bottomRight[0] - topLeft[0] + 120)
    	.attr("height", bottomRight[1] - topLeft[1] + 120)
        .style("left", topLeft[0] - 60 + "px")
        .style("top", topLeft[1] - 60 + "px");
        
    linePath.attr("d", toLine) //here is where we create the linePath
    g.attr("transform", "translate("+(-topLeft[0] + 60) + "," + (-topLeft[1] + 60) + ")");
}

Если ваш маркер/точки/пути не совпадают с вашей картой, это первое место, где нужно искать виновника. Регулировка больших чисел увеличит видимую «рамку» вокруг анимации, а меньшие числа сместят центральную точку на картографе. Я обнаружил, что общее сохранение чисел, делящихся друг на друга, является хорошей отправной точкой. IE делится на 30,40,50 и т.д. Значит, соответственно, это будет 60,80,100 и 120,160,200. И это должно подвести вас достаточно близко, чтобы внести любые незначительные корректировки, которые могут вам понадобиться.

function transition(path) {
  linePath.transition()
    	.duration(d => d.dur)
        .attrTween("stroke-dasharray", tweenDash) //there it is, the method for stroke-dasharray interpolation
}
function tweenDash() {
  return function
    	var l = linePath.node().getTotalLength(); //and here is the second method, getting length
        interpolate = d3.interpolateString("0," + l, l + "," + l); //this is the interpolate for the stroke-dash
        var marker = d3.select('#marker');
        var p = linePath.node().getPointAtLength(t * l); //and the other measurement, to locate the point in path
        marker.attr("transform", "translate(" + p.x + "," + p.y + ")");
        return interpolate
    }
}

И все, вы получаете как линейную, так и точечную анимацию. Вскоре я расскажу о v4+ и выделю изменения в коде. По какой-то причине v3 все еще широко используется. Лично у меня нет предпочтений. У них обоих есть некоторые компромиссы для увеличения версии или сохранения версии 3. Кстати, мы не вышли из загрузки json, поэтому убедитесь, что весь ваш код находится внутри этой функции. В следующем посте я покажу, как мы можем отказаться от изоляции всего внутри этого вызова.

Похожие записи

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *