Disabling all D3 animations (for testing)

2020-02-24 13:10发布

I'm looking for a D3 equivalent to jQuery.fx.off = true.

Say you are writing tests (with Mocha, QUnit, etc.) for an app that uses D3. The app has some D3 animations (with .transition()).

Animations are really bad for tests:

First, they are slow.

Second, because they are asynchronous, they can easily cause flickering tests. Ideally, you'd want to avoid any calls to setTimeout / setInterval / requestAnimationFrame.

Is there a way to disable all D3 animations, so that they instantly (and ideally, synchronously) jump to the end state? (Perhaps if there's not an option, we can hook into timer.js?)

4条回答
家丑人穷心不美
2楼-- · 2020-02-24 13:17

I do not know of a native way to do it in d3. But you can easily modify the d3 selector API to skip animations by augmenting the d3 prototypes:

HTML code to be animated:

<svg width="200" height="200">
    <rect x="1" y="1" width="0" height="100" />
</svg>

Animation and D3-augmentation code:

function animate(color){
    d3.selectAll("rect")
    .attr("width", 0).attr("fill", "white")
    .transition().duration(1000)
    .attr("width", 100).attr("fill", color)
}

function augment(){
    // add a duration function to the selection prototype
    d3.selection.prototype.duration   = function(){ return this }
    // hack the transition function of d3's select API
    d3.selection.prototype.transition = function(){ return this }
}

animate("red")
console.log("normal animation done")
setTimeout( function(){
        augment()
        console.log("d3 hacked!")
        animate("green")
        console.log("animation skipped")
}, 1200 )

Attention! This hack may not work as a complete solution for you. You may want to extend this solution with other transition().* functions that are not available on the d3.selection.prototype and that you use in your application. You may also consider other forms of animation supported by d3. Maybe there is more than <selection>.transition() that I am not aware of.

查看更多
萌系小妹纸
3楼-- · 2020-02-24 13:22

One approach you could take is to use a fake timer in your testing suite, like Sinon, which works with Mocha or QUnit. Jasmine also has a mock timer built in. I'd think this a better approach because it means the code you're testing is closer to the running code (as opposed to sabotaging the transition functions).

查看更多
Fickle 薄情
4楼-- · 2020-02-24 13:26

Seems like you can mock d3.timer function:

var d3timer = d3.timer;

d3.timer = function(callback, delay, then) {
    d3timer(callback, 0, 0);
};
查看更多
再贱就再见
5楼-- · 2020-02-24 13:35

An alternative to mocking out transitions is executing them synchronously directly to their final state.

With D3.js v4, use:

function flushAllD3Transitions() {
    var now = performance.now;
    performance.now = function() { return Infinity; };
    d3.timerFlush();
    performance.now = now;
 }

With D3.js v3 and previous, do:

function flushAllD3Transitions() {
    var now = Date.now;
    Date.now = function() { return Infinity; };
    d3.timer.flush();
    Date.now = now;
 }

See also d3 issue 1789.

查看更多
登录 后发表回答