此文章爲原創文章,原文地址:http://www.javashuo.com/article/p-wjcfkfzr-kn.htmlhtml
效果如上圖所示。前端
本項目使用主要d3.js v4製做,能夠用來選擇兩年的時間範圍,兩端按鈕切換年,在時間軸上標註能夠選擇的時間範圍和關鍵時間點。時間數據能夠在前端配置,也能夠從後端請求。git
此程序相對比較簡單,主要涉及d3的比例尺和拖動處理。github
1)d3的比例尺其實就是把一個範圍的數據映射到另外一個範圍的數據上後端
此處,咱們使用線性比例尺:d3.scaleLinear()app
它能夠把一段連續的值域映射到另外一段連續的值域,好比dom
1 var scale = d3.scaleLinear() 2 .domain([0, 730]) 3 // startPos是時間橫條的開始x座標,endPos是結束x座標 4 .range([startPos, endPos]);
若是咱們想知道兩年中的具體某天在時間橫條上對應點的x座標,就能夠把天數做爲參數傳給scale(),返回值就是對應的x座標。函數
固然,d3.js中還有不少其餘比例尺,詳細信息能夠查詢API文檔:https://github.com/d3/d3-scale#scaleLinearthis
2)咱們這裏的拖動主要分兩部分,首先是單獨拖動滑塊,而後是兩個滑塊一塊兒拖動。spa
單獨拖動滑塊的邏輯:
a.若是兩個滑塊被同時拖動過,則移除兩個滑塊間的黃線。
b.使用class名稱來判斷拖動的是哪一個滑塊。
c.計算鼠標x座標,而後用比例尺反向求出對應的天數位置。
// xPos爲x軸座標 var index = scale().invert(xPos);
d.移動對應滑塊到鼠標位置。
var dragFun = function () { // 移除拖拽連線元素 if (stickDragLine) { stickDragLine.remove(); stickDragLine = null; } // 得到被點擊元素class var className = d3.select(this).attr('class'); // 計算鼠標x座標,要減去滑塊寬度的二分之一 var pos = d3.event.x - slipBlockWidth / 2; // 計算鼠標index, var index = getIndex(pos); var blockIndex; // 當前塊位置,能夠配置默認位置,也可從後端請求 if (className === 'slip-left') { blockIndex = splitBlockIndex.left; } if (className === 'slip-right') { blockIndex = splitBlockIndex.right; } // 滑塊只能在0到maxIndex之間滑動,即上層橫條內 if (blockIndex >= 0 && blockIndex <= maxIndex) { if (className === 'slip-left') { // 移動左滑塊和相關背景和文字到鼠標位置 changeLeftBlock(index); } if (className === 'slip-right') { // 移動左滑塊和相關背景和文字到鼠標位置 changeRightBlock(index); } } // 滑塊拖動 var slipBlockDrag = d3.drag() .on('drag', dragFun); // 滑塊元素調用拖拽方法 slipBlockLeft.call(slipBlockDrag); slipBlockRight.call(slipBlockDrag);
兩個滑塊一塊兒拖動則在它們中間增長1個黃色連線。最後使用時間條元素調用拖拽方法。
// 主橫條上處理兩個滑塊一塊兒拖動事件 var stickDrag = d3.drag() .on('drag', function () { // 計算移動前兩個滑塊位置 var leftEventX = scale(splitBlockIndex.left), rightEventX = scale(splitBlockIndex.right); if (d3.event.x > rightEventX || d3.event.x < leftEventX) { return; } // 添加拖動線條 if (!stickDragLine) { stickDragLine = _stickG.append('line') .attr('x1', leftEventX + 2) .attr('y1', stickTop + stickHeight / 2) .attr('x2', rightEventX + 2) .attr('y2', stickTop + stickHeight / 2) .attr('stroke-width', 1) .attr('stroke', 'yellow'); } // 移動後的x var leftAfterX = leftEventX + d3.event.dx, rightAfterX = rightEventX + d3.event.dx, // 移動後的index leftIndex = Math.floor(scale().invert(leftAfterX)), rightIndex = Math.floor(scale().invert(rightAfterX)); if ((leftIndex >= 0 && rightIndex < maxIndex) && (rightIndex >= 0 && leftIndex < maxIndex)) { // 保存移動後的index splitBlockIndex.left = leftIndex; splitBlockIndex.right = rightIndex; // 移動各元素 changeLeftBlock(leftAfterX, splitBlockIndex.left); changeRightBlock(rightAfterX, splitBlockIndex.right); stickDragLine .attr('x1', leftAfterX + 2) .attr('x2', rightAfterX + 2); } }); _stickG.call(stickDrag);
文中代碼用來講明邏輯結構,具體功能函數實現起來很容易,因此沒有給出。