note

This article is based on the example of D3.js author Mike Bostock

The text is divided into three parts, here the author integrates them into a convenient reading.

This effect is based on D3.js and mainly uses D3-Selection. If the basic logic for using D3-Selection is not clear, refer to this article.

rendering

  • Step1 first the code will randomly generate a string, the character in green into the screen.

  • Step2 next, the code randomly generates a new string, which is compared to the original string:

    2.1 The same letters in the new string and the original string will turn black and remain on the screen

    2.2 Letters that are present in the original string but not in the new string will turn red and be removed from the screen

    2.3 Letters that are present in the new string but not in the original string turn green and are added to the screen

Code implementation

1. Character switching

The first step to achieve the effect is:

  • The basic character switch is complete
  • Green when entering, black when not changing
  • Removed characters are removed directly from the interface

Let’s start with the code,Let me run

<script>
var alphabet = "abcdefghijklmnopqrstuvwxyz".split("");

var svg = d3.select("svg"),
    width = +svg.attr("width"),
    height = +svg.attr("height"),
    g = svg.append("g").attr("transform"."translate(32," + (height / 2) + ")");

function update(data) {

  // DATA JOIN
  // Join new data with old elements, if any.
  var text = g.selectAll("text")
    .data(data);

  // UPDATE
  // Update old elements as needed.
  text.attr("class"."update");

  // ENTER
  // Create new elements as needed.
  //
  // ENTER + UPDATE
  // After merging the entered elements with the update selection,
  // apply operations to both.
  text.enter().append("text")
      .attr("class"."enter")
      .attr("x".function(d, i) { return i * 32; })
      .attr("dy".".35em")
    .merge(text)
      .text(function(d) { return d; });

  // EXIT
  // Remove old elements as needed.
  text.exit().remove();
}

// The initial display.
update(alphabet);

// Grab a random sample of letters from the alphabet, in alphabetical order.
d3.interval(function() {
  update(d3.shuffle(alphabet)
      .slice(0.Math.floor(Math.random() * 26))
      .sort());
}, 1500);

</script>
Copy the code

The code is not long, so step by step the code logic is analyzed:

First, get the width and height information for the SVG. And create an element to carry on the character (element) to be created next.

var svg = d3.select("svg"),
    width = +svg.attr("width"),
    height = +svg.attr("height"),
    g = svg.append("g").attr("transform"."translate(32," + (height / 2) + ")");
Copy the code

In the update() method, we pass in the string data ** (data consists of a random selection of 26 letters). First we select all the text elements and bind them to data** :

var text = g.selectAll("text")
    .data(data);
Copy the code

Then we process the Enter collection (that is, the newly added character), add a text element to each new character, add the class attribute to it, and calculate its horizontal and vertical coordinates using index:

text.enter().append("text")                            // Add an SVG text element
      .attr("class"."enter")                          // Add the class attribute (green)
      .attr("x".function(d, i) { return i * 32; })    // Calculate x coordinates for each character at 32 pixels
      .attr("dy".".35em")                             // Set the y offset
Copy the code

Next we process both the Enter and update collections, filling the text element with character data:

  .merge(text)
      .text(function(d) { return d; });
Copy the code

Finally, we deal with the Exit collection, which we temporarily remove directly from the screen:

text.exit().remove();
Copy the code

We use d3.interval(callback, timeInterval) to periodically call update() to refresh characters. Now we have the following effect:

2. Set key values for characters

If you look carefully, you may have noticed that the first step is always the last character added. This is obviously not what we want, and the position of the new character should be random. So what are the reasons for this effect?

The answer lies in the fact that we bind character data: data(data) does not specify the key value of data, so D3 uses index as the key by default, which is why the newly added characters always appear last.

So we add a key accessor to the character:

  var text = g.selectAll("text")
    .data(data, function(d) { return d; });
Copy the code

Now that the key value is bound, the existing characters in the text will not change during the update, but the coordinates need to be recalculated, so we make the following changes:

text.enter().append("text")
      .attr("class"."enter")
      .attr("dy".".35em")
      .text(function(d) { return d; })                 // Move the text assignment to Enter
    .merge(text)
      .attr("x".function(d, i) { return i * 32; });   // Move coordinate calculation after merge (enter & update)
Copy the code

Now our code looks like this,Click on me to run online:

<script>

var alphabet = "abcdefghijklmnopqrstuvwxyz".split("");

var svg = d3.select("svg"),
    width = +svg.attr("width"),
    height = +svg.attr("height"),
    g = svg.append("g").attr("transform"."translate(32," + (height / 2) + ")");

function update(data) {

  // DATA JOIN
  // Join new data with old elements, if any.
  var text = g.selectAll("text")
    .data(data, function(d) { return d; });

  // UPDATE
  // Update old elements as needed.
  text.attr("class"."update");

  // ENTER
  // Create new elements as needed.
  //
  // ENTER + UPDATE
  // After merging the entered elements with the update selection,
  // apply operations to both.
  text.enter().append("text")
      .attr("class"."enter")
      .attr("dy".".35em")
      .text(function(d) { return d; })
    .merge(text)
      .attr("x".function(d, i) { return i * 32; });

  // EXIT
  // Remove old elements as needed.
  text.exit().remove();
}

// The initial display.
update(alphabet);

// Grab a random sample of letters from the alphabet, in alphabetical order.
d3.interval(function() {
  update(d3.shuffle(alphabet)
      .slice(0.Math.floor(Math.random() * 26))
      .sort());
}, 1500);

</script>
Copy the code

Now we get:

3. Add animations

Animation enables us to better observe the changing process and state of elements, and to give different animations to elements in different states can more intuitively display our data.

Now we animate the character changes and fill in the color changes when the character is removed.

First we define a transition variable and set its animation interval to 750

var t = d3.transition()
      .duration(750);
Copy the code

Using animation in D3 is very simple, specifying some attributes of an element before the animation, calling the animation, and specifying some attributes after the animation. D3 will automatically generate animation based on the interpolator.

The following code handles characters that leave the screen (exit Selection) :

text.exit()
      .attr("class"."exit")          // Before the animation, set the class property and the font becomes red
    .transition(t)                    // Set the animation
      .attr("y".60)                  // Set the y coordinate so that the element exits the screen downward (y: 0 => 60)
      .style("fill-opacity".1e-6)    // Set opacity so that elements disappear (opacity: 1 => 0)
      .remove();                      // Finally move it out of the interface
Copy the code

Similarly, we handle Enter and Update Selection:

// UPDATE old elements present in new data.
  text.attr("class"."update")
      .attr("y".0)
      .style("fill-opacity".1)
    .transition(t)
      .attr("x".function(d, i) { return i * 32; });

  // ENTER new elements present in new data.
  text.enter().append("text")
      .attr("class"."enter")
      .attr("dy".".35em")
      .attr("y".- 60)
      .attr("x".function(d, i) { return i * 32; })
      .style("fill-opacity".1e-6)
      .text(function(d) { return d; })
    .transition(t)
      .attr("y".0)
      .style("fill-opacity".1);
Copy the code

So we end up with code that looks like this,Let me run

<script>

var alphabet = "abcdefghijklmnopqrstuvwxyz".split("");

var svg = d3.select("svg"),
    width = +svg.attr("width"),
    height = +svg.attr("height"),
    g = svg.append("g").attr("transform"."translate(32," + (height / 2) + ")");

function update(data) {
  var t = d3.transition()
      .duration(750);

  // JOIN new data with old elements.
  var text = g.selectAll("text")
    .data(data, function(d) { return d; });

  // EXIT old elements not present in new data.
  text.exit()
      .attr("class"."exit")
    .transition(t)
      .attr("y".60)
      .style("fill-opacity".1e-6)
      .remove();

  // UPDATE old elements present in new data.
  text.attr("class"."update")
      .attr("y".0)
      .style("fill-opacity".1)
    .transition(t)
      .attr("x".function(d, i) { return i * 32; });

  // ENTER new elements present in new data.
  text.enter().append("text")
      .attr("class"."enter")
      .attr("dy".".35em")
      .attr("y".- 60)
      .attr("x".function(d, i) { return i * 32; })
      .style("fill-opacity".1e-6)
      .text(function(d) { return d; })
    .transition(t)
      .attr("y".0)
      .style("fill-opacity".1);
}

// The initial display.
update(alphabet);

// Grab a random sample of letters from the alphabet, in alphabetical order.
d3.interval(function() {
  update(d3.shuffle(alphabet)
      .slice(0.Math.floor(Math.random() * 26))
      .sort());
}, 1500);

</script>
Copy the code

End result:

Want to learn more about D3.js?

Here is my D3.js, Github address for data visualization, welcome to Start & fork :tada:

D3-blog

If you like it, check out the link below:)

Making the home page

Zhihu column

The Denver nuggets

Welcome to follow my official account: