Progressing in a connected sequence of changes on a linear graph with distinct points and a pause

A jsfiddle has been created here.

Looking to move a circle along a sine wave graph, triggered by a click event. The circle should stop at specific x and y value pairs on the graph before moving to the last point and looping back to the first (ideally until a stop button is pressed).

The issue currently is that the circle only moves horizontally and the delay effect is only noticeable at the start.

This is the relevant code snippet (full example in the provided link):

Circle creation:

// circle to be moved along the graph
  var circle = svg.append("circle")
                .attr("id", "concindi")
                .attr("cx", x_scale(xval[0]))
                .attr("cy", y_scale(yval[0]))
                .attr("transform", "translate(" + (0) + "," + (-1 * padding + 15) + ")")
                .attr("r", 6)
                .style("fill", 'red');

Movement process:

var coordinates = d3.zip(xval, yval);

svg.select("#concindi").on("click", function() {

    coordinates.forEach(function(ci, indi){
      
      if (indi < (coordinates.length - 1)){
        
        console.log(coordinates[indi + 1][0]);
        console.log(coordinates[indi + 1][1]);
        d3.select("#concindi")
          .transition()
          .delay(2000)
          .duration(5000)
          .ease("linear")
          .attr("cx", x_scale(coordinates[indi + 1][0]))
          .attr("cy", y_scale(coordinates[indi + 1][1]));
      }

    });

The loop might not be utilized correctly here. The goal is to move from one x/y pair to the next every 5 seconds after a 2-second pause. Currently, the delay is only visible initially and the movement is horizontal.

Any suggestions on how to correct this?

Answer №1

Have you considered utilizing Bostock's translateAlong function for this task?

function translateAlong(path) {
    var length = path.getTotalLength();
    return function(data, index, array) {
        return function(time) {
            var point = path.getPointAtLength(time * length);
            return "translate(" + point.x + "," + point.y + ")";
        };
    };
}

Take a look at the demonstration below:

// A function to generate some data
function getSinValue(value) {
  return 30 * Math.sin(value * 0.25) + 35;
}

var width = 400;
var height = 200;
var padding = 50;

var svg = d3.select("body")
  .append("svg")
  .attr("width", width)
  .attr("height", height);

var xRangeMin = 0;
var xRangeMax = 50;

var yRangeMin = 0;
var yRangeMax = 100;

var xScale = d3.scale.linear()
  .domain([xRangeMin, xRangeMax])
  .range([padding, width - padding * 2]);

var yScale = d3.scale.linear()
  .domain([yRangeMin, yRangeMax])
  .range([height - padding, padding]);

// Creating the data
var xValues = d3.range(xRangeMin, xRangeMax, 1);
var yValues = xValues.map(getSinValue);

// Zip coordinates for convenience
var coordinatePairs = d3.zip(xValues, yValues);

// Defining the line graph
var lineGraph = d3.svg.line()
  .x(function(d) {
    return xScale(d[0]);
  })
  .y(function(d) {
    return yScale(d[1]);
  })
  .interpolate("linear");

// Drawing the graph
var sineGraph = svg.append("path")
  .attr("d", lineGraph(coordinatePairs))
  .attr("stroke", "blue")
  .attr("stroke-width", 2)
  .attr("fill", "none");

// The circle that will move along the graph
var movingCircle = svg.append("circle")
  .attr("id", "movingCircle")
  .attr("transform", "translate(" + (xScale(xValues[0])) + "," + (yScale(yValues[0])) + ")")
  .attr("r", 6)
  .style("fill", 'red');

svg.select("#movingCircle").on("click", function() {
  d3.select(this).transition()
    .duration(5000)
    .attrTween("transform", translateAlong(sineGraph.node()));
});

// Returns an attrTween for translating along the specified path element.
function translateAlong(path) {
  var length = path.getTotalLength();
  return function(data, index, array) {
    return function(time) {
      var point = path.getPointAtLength(time * length);
      return "translate(" + point.x + "," + point.y + ")";
    };
  };
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>

It's important to note that using forEach will execute instantaneously through the array. Therefore, jumping from one coordinate to another with your current method is not feasible (as mentioned here: "I am pretty sure that I use the loop in a wrong manner").

If you wish to introduce a 2-second delay between points, consider chaining transitions. Below is an example where the delay and duration times are reduced for better visibility:

var indexCounter = 0;
animate();

function animate() {
    indexCounter++;
    d3.select(this).transition()
        .delay(500)
        .duration(500)
        .attr("transform", "translate(" + (xScale(coordinatePairs[indexCounter][0])) 
            + "," + (yScale(coordinatePairs[indexCounter][1])) + ")")
        .each("end", animate);
}

View the updated demo below:

// Function to generate data
function getSinValue(value) {
  return 30 * Math.sin(value * 0.25) + 35;
}

var width = 400;
var height = 200;
var padding = 50;

var svg = d3.select("body")
  .append("svg")
  .attr("width", width)
  .attr("height", height);

var xRangeMin = 0;
var xRangeMax = 50;

var yRangeMin = 0;
var yRangeMax = 100;

var xScale = d3.scale.linear()
  .domain([xRangeMin, xRangeMax])
  .range([padding, width - padding * 2]);

var yScale = d3.scale.linear()
  .domain([yRangeMin, yRangeMax])
  .range([height - padding, padding]);

// Generating the data
var xValues = d3.range(xRangeMin, xRangeMax, 1);
var yValues = xValues.map(getSinValue);

// Zipping coordinates for convenience
var coordinatePairs = d3.zip(xValues, yValues);

// Defining the line graph
var lineGraph = d3.svg.line()
  .x(function(d) {
    return xScale(d[0]);
  })
  .y(function(d) {
    return yScale(d[1]);
  })
  .interpolate("linear");

// Drawing the graph
var sineGraph = svg.append("path")
  .attr("d", lineGraph(coordinatePairs))
  .attr("stroke", "blue")
  .attr("stroke-width", 2)
  .attr("fill", "none");

// The circle to move along the graph
var animatedCircle = svg.append("circle")
  .attr("id", "animatedCircle")
  .attr("transform", "translate(" + (xScale(xValues[0])) + "," + (yScale(yValues[0])) + ")")
  .attr("r", 6)
  .style("fill", 'red');

svg.select("#animatedCircle").on("click", function() {
  var indexCounter = 0;
  var self = this;
  animate();
  
  function animate() {
    indexCounter++;
    d3.select(self).transition()
      .delay(500)
      .duration(500)
      .attr("transform", "translate(" + (xScale(coordinatePairs[indexCounter][0])) + "," + (yScale(coordinatePairs[indexCounter][1])) + ")")
      .each("end", animate);
  }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Creating a password with two distinct numbers using regular expressions

In javascript I am struggling to create a password that meets the criteria of having at least eight characters, including two SEPARATE digits, one uppercase and one lowercase letter, as well as one special character (-, @, #, $, &, *, +) but not /, !, ? ...

Modify the name of the selected option value

I am working on an html code where I need to replace the values 0, 1, and 2 with USA, Australia, and Canada respectively. I also need to know the name of these values in my MySQL database. Thanks! HTML <form method="post" id="countriesForm" name="cou ...

Preparing the data before displaying it

I have a data display in the view that needs to be formatted first. Previously, I used val.toFixed(2) which worked fine for numbers, but when letters were included with the numbers, the formatting didn't account for them and only displayed the numbers ...

Extracting public data from social media profiles as displayed in Smartr

Is there any pre-existing API or reference material available for achieving this task? I am interested in accessing public social data without the need for users to manually link their accounts to our site. ...

How can I select a checkbox dynamically during runtime?

I am working on a JavaScript code that needs to add the checked option to a checkbox if it has an id or value of 2 at runtime. I have tried the following code, but unfortunately, I am unable to check the checkbox. Do you have any ideas on how to solve th ...

Highcharts Maps - Circular zoom controls

Currently implementing the Highcharts API for a special project. All features are functioning well except for one issue. I am looking to make the zoom in/out buttons appear rounded. I have attempted using border-radius with 50%, as well as modifying the r ...

Ajax transmitting data with concealed characters

I'm trying to send data from one PHP site to another. On the first site, everything looks good. The string appears as --show="author,book,text/n However, when I check the string after receiving it on the second site, it shows --show="author,b ...

The custom component I created seems to be unaffected by the inline styles in React

Having an issue with a custom component where I am trying to add margin-top in one of my uses. I attempted using <MyComponent style={{ marginTop: '10px' }}> and also const myStyle = { marginTop: '10px' }; `<MyComponent style= ...

Integrating additional JavaScript into an Ionic 2 project

Imagine we have a foo.js file containing a variable, function, and class that are not yet part of the project. Now suppose we want to access these elements in our home.ts method or make them globally available for use within a home.ts method. How can this ...

Slerping with quaternions in Three.js can produce undesirable outcomes when near the poles

Check out this video example: https://drive.google.com/file/d/18Ep4i1JMs7QvW9m-3U4oyQ4sM0CfIFzP/view In the demonstration, I showcase how I determine the world position of a ray hitting a globe under the mouse cursor. By utilizing lookAt() with a THREE.Gr ...

Utilizing Node Js for Form Data Transmission and Retrieval

I've successfully created two separate JS files, each responsible for sending and retrieving form data to and from the database. My dilemma is whether it's more practical or feasible to leave these two JS files as they are and serve their individ ...

What is the process of duplicating form fields using PHP?

Currently, I am facing an issue with my clients' landing page setup. The landing page is designed to input any new signups into Salesforce. However, the information flow is primarily directed towards my system, which requires specific form field ids. ...

Challenges encountered when inserting nested data into MongoDB

I am currently in the process of constructing a database for a giveaway bot using MongoDB. When a new giveaway is initiated, the bot executes the following code to add the giveaway details to the database: const {mongoose} = require("mongoose") c ...

Ember application experiencing trouble displaying Facebook Like Box

I’m currently facing an issue with integrating the like box into our ember app, specifically in a template named about. The problem arises when users enter the ember app from a different route, instead of directly accessing the about route. In such cases ...

What is the best way to update the content of a particular div and its associated link ID whenever the link is clicked?

I need to update the content of a div by clicking on an href link that includes an id named data. The issue I'm facing is that I can't access the id value within my script because it's passed within a function. How can I pass the data variab ...

Switching images through onmouseover, onmouseout, and onclick interactions

I have two images named img1 and img2. Here is my code: function roll(id, img_name, event_name, img_id) { var state ; if(event_name == 'mouseover') { state = false;rollover();} else if(event_name == 'mouseout') { ...

What steps are needed to enable the keyboard on a Otree application for mobile devices?

I previously utilized an Implicit Association Task (IAT) in an experiment conducted on computers. However, I now need to adapt this IAT for use on tablets or mobile devices. Here is how the IAT appears on a cellular device: https://i.stack.imgur.com/kNwo ...

Discover the step-by-step guide for inserting personalized HTML into the current widget screen on Odoo 12 with

For the past 3 days, I've been stuck trying to figure out how to print order items. My goal is to have a custom HTML code added to a div with the class 'order-print' when the Order button is clicked. I am using odoo 12 and facing issues wit ...

What could be causing the PAGE CSS to malfunction?

I am looking to export HTML text as a word document with A4 size and portrait orientation. My JavaScript currently allows me to export the text, but it appears like a webpage format rather than in A4 or portrait mode. I have tried adding @page CSS styling ...

What is the best way to show the current date and time?

To filter out and display only the date from the array that is the largest compared to the current date, all dates are displayed. const states = States.select().exec() var curr = new Date(); for (var i = 0; i < states.length; i++) { var maxDate = ...