2017-08-12 14 views
1

は時々あなたは、このような長い時間がかかる場合がありますイベントコールバックを「開始」と「終了」の移行で(遷移によって必要)、計算集約的な作業を行う必要があり、その際に遷移開始(または次のチェーンの移行は、「終わりのために開始したとき"イベント)、遷移の開始時間を過ぎて既に時間が経過し、遷移がジャンプする。d3.transitionコールバックで長い計算時間に対応するにはどうすればよいですか?

問題は以下見ることができます。 1回目と2回目のアニメートされたトランジションの両方が、開始値からハーフドスに直接ジャンプします。


<!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>


注:fixed、 ですが、まだリリースされていない、この記事の執筆時点では、非公式D3-タイマーを使用するには、この例を必要としbug in d3-timer .min.js。

+0

a)のウェブワーカーで計算集約タスクを実行してください。 b)アニメーション化する代わりにSMILを使用すると、ブラウザは自動的にすべてのタイミングを処理し、遅れを考慮に入れます。 –

+0

@RobertLongson a)私は自分でWebワーカーを考えましたが、この例では、遷移に計算結果が必要な場合、どのように使用できますか? b)SMILを使用しないと、d3.transitionの使用を中止することを暗示するでしょうか?またはそれらを組み合わせることはできますか? – magjac

+0

@RobertLongson質問の遷移によって計算が必要であることを明確にしました – magjac

答えて

0
  • "開始"イベントの場合、できません。遷移が作成される前に、計算集約的な作業を行い、またはこれは、別の前の移行で、可能でない場合は、この移行はchainedです。

  • 「終了」イベントでは、最初の遷移の終了イベントコールバックの計算時間以上の遅延を2番目の遷移に追加できます。自分のクライアントよりも遅いクライアントでこれを動作させたい場合は、遅延をさらに長くしてください。

<!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>

+0

誰かがより良い解決策を持っているかどうかは非常に興味があります。 – magjac