如何在d3.transition回调中适应较长的计算时间?

时间:2017-08-12 17:27:39

标签: d3.js

有时您需要在转换“开始”和“结束”事件回调中进行计算密集型工作(转换所需),这可能需要很长时间才能开始转换(或者当下一个链接转换开始时“结束“事件”,时间已超过转换开始时间,导致转换跳转。

问题可以在下面看到。第一个和第二个动画过渡都从其起始值直接跳到半完成。

<!DOCTYPE html>
<meta charset="utf-8">
<body>
<script src="//d3js.org/d3-selection.v1.min.js"></script>
<!-- This script is d3-timer.min.js with https://github.com/d3/d3-timer/pull/28 that fixes https://github.com/d3/d3-timer/issues/27 -->
<script>!function(t,n){"object"==typeof exports&&"undefined"!=typeof module?n(exports):"function"==typeof define&&define.amd?define(["exports"],n):n(t.d3=t.d3||{})}(this,function(t){"use strict";function n(){return h||(x(e),h=v.now()+y)}function e(){h=0}function o(){this._call=this._time=this._next=null}function i(t,n,e){var i=new o;return i.restart(t,n,e),i}function r(){n(),++_;for(var t,e=f;e;)(t=h-e._time)>=0&&e._call.call(null,t),e=e._next;--_}function u(){h=(d=v.now())+y,_=m=0;try{r()}finally{_=0,c(),h=0}}function l(){var t=v.now(),n=t-d;n>w&&(y-=n,d=t)}function c(){for(var t,n,e=f,o=1/0;e;)e._call?(o>e._time&&(o=e._time),t=e,e=e._next):(n=e._next,e._next=null,e=t?t._next=n:f=n);s=t,a(o)}function a(t){_||(m&&(m=clearTimeout(m)),t-h>24?(t<1/0&&(m=setTimeout(u,t-v.now()-y)),p&&(p=clearInterval(p))):(p||(d=v.now(),p=setInterval(l,w)),_=1,x(u)))}var f,s,_=0,m=0,p=0,w=1e3,d=0,h=0,y=0,v="object"==typeof performance&&performance.now?performance:Date,x="object"==typeof window&&window.requestAnimationFrame?window.requestAnimationFrame.bind(window):function(t){setTimeout(t,17)};o.prototype=i.prototype={constructor:o,restart:function(t,e,o){if("function"!=typeof t)throw new TypeError("callback is not a function");o=(null==o?n():+o)+(null==e?0:+e),this._next||s===this||(s?s._next=this:f=this,s=this),this._call=t,this._time=o,a()},stop:function(){this._call&&(this._call=null,this._time=1/0,a())}};t.now=n,t.timer=i,t.timerFlush=r,t.timeout=function(t,n,e){var i=new o;return n=null==n?0:+n,i.restart(function(e){i.stop(),t(e+n)},n,e),i},t.interval=function(t,e,i){var r=new o,u=e;return null==e?(r.restart(t,e,i),r):(e=+e,i=null==i?n():+i,r.restart(function n(o){o+=u,r.restart(n,u+=e,i),t(o)},e,i),r)},Object.defineProperty(t,"__esModule",{value:!0})});</script>
<script src="//d3js.org/d3-dispatch.v1.min.js"></script>
<script src="//d3js.org/d3-interpolate.v1.min.js"></script>
<script src="//d3js.org/d3-color.v1.min.js"></script>
<script src="//d3js.org/d3-ease.v1.min.js"></script>
<script src="//d3js.org/d3-transition.v1.min.js"></script>
<script src="//d3js.org/d3-scale.v1.min.js"></script>
<script src="//d3js.org/d3-zoom.v1.min.js"></script>
  <div style="text-align: center;"></div>
  <script>

var start = Date.now();

function elapsed() {
    return Date.now() - start;
}

function waste(time) {
    var t0 = Date.now();
    while (Date.now() - t0 < time)
        ;
}

var svg = d3.select("div").append("svg")
    .attr("width", 800)
    .attr("height", 500);
var g = svg.append("g");

var start1Waste = 2000;
var end1Waste = 2000;
var start2Waste = 0;
var end2Waste = 0;

g.selectAll("rect")
    .data([10, 100, 200])
  .enter()
  .append("rect")
    .attr("width", d => d)
    .attr("height", d => d)
    .attr("fill", "none")
    .attr("stroke", "black")

var rect = g.append("rect")
    .attr("width", 10)
    .attr("height", 10)
    .attr("fill", "#d62728")
  .transition()
    .delay(2000)
    .duration(4000)
    .ease(d3.easeLinear)
    .attr("width", 100)
    .attr("height", 100)
    .on("start", function () {
        console.log('Start event 1', elapsed() / 1000);
        waste(start1Waste);
        console.log('Start event 1 returning', elapsed() / 1000);
    })
    .on("end", function () {
        console.log('End event 1', elapsed() / 1000);
        waste(end1Waste);
        console.log('End event 1 returning', elapsed() / 1000);
    })
  .transition()
    .attr("width", 200)
    .attr("height", 200)
    .attr("fill", "#1f77b4")
    .on("start", function () {
        console.log('Start event 2', elapsed() / 1000);
        waste(start2Waste);
        console.log('Start event 2 returning', elapsed() / 1000);
    })
    .on("end", function () {
        console.log('End event 2', elapsed() / 1000);
        waste(end2Waste);
        console.log('End event 2 returning', elapsed() / 1000);
    })

</script>

注意:bug in d3-timerfixed, 但在撰写本文时尚未发布,需要此示例使用非官方d3-timer.min.js。

1 个答案:

答案 0 :(得分:0)

  • 对于“开始”事件,您不能。在创建转换之前计算密集型工作 ,或者如果不可能,则在单独的先前转换中,此转换为chained

  • 对于“结束”事件,您可以向第二个转换添加延迟,该延迟等于或大于第一个转换的结束事件回调的计算时间。如果您希望这对比您自己慢的客户端有效,请延迟更长时间。

<!DOCTYPE html>
<meta charset="utf-8">
<body>
<script src="//d3js.org/d3-selection.v1.min.js"></script>
<!-- This script is d3-timer.min.js with https://github.com/d3/d3-timer/pull/28 that fixes https://github.com/d3/d3-timer/issues/27 -->
<script>!function(t,n){"object"==typeof exports&&"undefined"!=typeof module?n(exports):"function"==typeof define&&define.amd?define(["exports"],n):n(t.d3=t.d3||{})}(this,function(t){"use strict";function n(){return h||(x(e),h=v.now()+y)}function e(){h=0}function o(){this._call=this._time=this._next=null}function i(t,n,e){var i=new o;return i.restart(t,n,e),i}function r(){n(),++_;for(var t,e=f;e;)(t=h-e._time)>=0&&e._call.call(null,t),e=e._next;--_}function u(){h=(d=v.now())+y,_=m=0;try{r()}finally{_=0,c(),h=0}}function l(){var t=v.now(),n=t-d;n>w&&(y-=n,d=t)}function c(){for(var t,n,e=f,o=1/0;e;)e._call?(o>e._time&&(o=e._time),t=e,e=e._next):(n=e._next,e._next=null,e=t?t._next=n:f=n);s=t,a(o)}function a(t){_||(m&&(m=clearTimeout(m)),t-h>24?(t<1/0&&(m=setTimeout(u,t-v.now()-y)),p&&(p=clearInterval(p))):(p||(d=v.now(),p=setInterval(l,w)),_=1,x(u)))}var f,s,_=0,m=0,p=0,w=1e3,d=0,h=0,y=0,v="object"==typeof performance&&performance.now?performance:Date,x="object"==typeof window&&window.requestAnimationFrame?window.requestAnimationFrame.bind(window):function(t){setTimeout(t,17)};o.prototype=i.prototype={constructor:o,restart:function(t,e,o){if("function"!=typeof t)throw new TypeError("callback is not a function");o=(null==o?n():+o)+(null==e?0:+e),this._next||s===this||(s?s._next=this:f=this,s=this),this._call=t,this._time=o,a()},stop:function(){this._call&&(this._call=null,this._time=1/0,a())}};t.now=n,t.timer=i,t.timerFlush=r,t.timeout=function(t,n,e){var i=new o;return n=null==n?0:+n,i.restart(function(e){i.stop(),t(e+n)},n,e),i},t.interval=function(t,e,i){var r=new o,u=e;return null==e?(r.restart(t,e,i),r):(e=+e,i=null==i?n():+i,r.restart(function n(o){o+=u,r.restart(n,u+=e,i),t(o)},e,i),r)},Object.defineProperty(t,"__esModule",{value:!0})});</script>
<script src="//d3js.org/d3-dispatch.v1.min.js"></script>
<script src="//d3js.org/d3-interpolate.v1.min.js"></script>
<script src="//d3js.org/d3-color.v1.min.js"></script>
<script src="//d3js.org/d3-ease.v1.min.js"></script>
<script src="//d3js.org/d3-transition.v1.min.js"></script>
<script src="//d3js.org/d3-scale.v1.min.js"></script>
<script src="//d3js.org/d3-zoom.v1.min.js"></script>
  <div style="text-align: center;"></div>
  <script>

var start = Date.now();

function elapsed() {
    return Date.now() - start;
}

function waste(time) {
    var t0 = Date.now();
    while (Date.now() - t0 < time)
        ;
}

var svg = d3.select("div").append("svg")
    .attr("width", 800)
    .attr("height", 500);
var g = svg.append("g");

var start1Waste = 0; // Can't compensate for wasted time at start
var end1Waste = 2000; // Can be compensated with delay in next transition
var start2Waste = 0; // Can't compensate for wasted time at start
var end2Waste = 0;


g.selectAll("rect")
    .data([10, 100, 200])
  .enter()
  .append("rect")
    .attr("width", d => d)
    .attr("height", d => d)
    .attr("fill", "none")
    .attr("stroke", "black")
        
var rect = g.append("rect")
    .attr("width", 10)
    .attr("height", 10)
    .attr("fill", "#d62728")
  .transition()
    .delay(2000)
    .duration(4000)
    .ease(d3.easeLinear)
    .attr("width", 100)
    .attr("height", 100)
    .on("start", function () {
        console.log('Start event 1', elapsed() / 1000);
        waste(start1Waste);
        console.log('Start event 1 returning', elapsed() / 1000);
    })
    .on("end", function () {
        console.log('End event 1', elapsed() / 1000);
        waste(end1Waste);
        console.log('End event 1 returning', elapsed() / 1000);
    })
  .transition()
    .delay(end1Waste)
    .attr("width", 200)
    .attr("height", 200)
    .attr("fill", "#1f77b4")
    .on("start", function () {
        console.log('Start event 2', elapsed() / 1000);
        waste(start2Waste);
        console.log('Start event 2 returning', elapsed() / 1000);
    })
    .on("end", function () {
        console.log('End event 2', elapsed() / 1000);
        waste(end2Waste);
        console.log('End event 2 returning', elapsed() / 1000);
    })

</script>