d3 / d3-transition

Animated transitions for D3 selections.
https://d3js.org/d3-transition
ISC License
223 stars 65 forks source link

Opacity transition does not work properly for % values #147

Open matthew-h-wang opened 7 months ago

matthew-h-wang commented 7 months ago

When using transitions with the opacity style, values given in "%" units do not correctly transition from the prior value, but instead always transition from "0%". By contrast, using numbers for opacity values do correctly transition between opacity values.

Here's a simple example, where the intended effect is 5 circles who smoothly change to random opacities every update:

let data = [];

//Produce 5 points with increasing x values and random o values in range [0,100)
function updateData() {
    data = [];
    for(let i=0; i<5; i++) {
        data.push({x: i * 100 + 50, o: Math.floor(Math.random() * 100)});
    }
}

function update() {
    d3.select('svg')
    .selectAll('circle')
        .data(data)
        .join('circle')
        .attr('cy', 50)
        .attr('r', 40)
        .attr('cx', (d) => d.x)
        .transition()
        .duration(500)
        .style('opacity', (d) => d.o +"%"); // using percent units
}

function updateAll() {
    updateData();
    update();
}

updateAll();

The actual resulting effect is each update, the circles fade in from 0 opacity, rather than smoothly from the prior opacity value:

https://github.com/d3/d3/assets/7028714/c3b9a9c4-f1ac-41d4-8ff6-9ff1fcddeace

By changing the final line in update() to this:

        .style('opacity', (d) => d.o * 0.01); // using decimal units

we get the intended effect:

https://github.com/d3/d3/assets/7028714/f44aefd8-4b24-4622-bee5-3d76bfb70125

I have not tested transitioning other style properties that use percent units, but it is possible this is an issue beyond just opacity.

mbostock commented 7 months ago

I think this probably isn’t fixable and is the same fundamental issue as https://github.com/d3/d3-transition/issues/72#issuecomment-319202005 which is that transition.style depends on the computed value of the style property. In the case of opacity, the computed value is a number between 0 and 1. So you always need to specify the target value of the transition in the same manner as the computed value for the default interpolator to work correctly.

matthew-h-wang commented 7 months ago

After reading the documentation for transition.style, that makes more sense. It does feel like an easy pitfall that others might run into because many style properties work just fine with alternative units when transitions are not involved. Perhaps the docs could include some warnings/guidelines on what units are appropriate for certain common style properties.