上一篇,我們討論了如何通過canvas繪制一個折線圖
接下來,我們將給這個折線圖實現鼠標交互效果
首先,鼠標在我們的折線圖上移動時,我們要一個藍色的豎線跟隨鼠標移動
要實現這個效果,我們一步一步來
1、我們需要在canvas的mousemove事件方法中,根據鼠標位置clientX/Y,和容器的top/left值,計算出鼠標所在處的canvas坐標;
2、根據鼠標坐標判斷鼠標是否在折線圖上,如果鼠標在折線圖上,將鼠標x軸坐標(即橫向坐標)保存在mousemovePositionx變量中,如果鼠標不在折線圖上,將mousemovePositionx變量設置為null;
3、drow方法中判斷mousemovePositionx變量是否為null,若不為null,則根據mousemovePositionx的值繪制藍色線段
在線展示及代碼
然后,我們分解一下鼠標拖動選擇時間區間這個操作
1、鼠標按下; 2、鼠標移動; 3、鼠標抬起
實際上我們只需要在鼠標按下時記錄鼠標按下的位置,鼠標抬起時,根據鼠標抬起位置和之前記錄的鼠標按下的位置,便可以得到拖拽動作選擇的區間
但是這樣做,鼠標移動時沒有任何交互效果
為了更好的用戶體驗,我們可以在鼠標移動方法中,通過鼠標位置與鼠標按下位置,將已選擇區間記錄下來,供draw方法繪制相應交互效果
mousedown = e => {
const x = e.clientX - L,
y = e.clientY - T;
if (y > chartTop && y < chartBottom && x > chartLeft && x < chartRight ) {
mouseDownZB = x;
} else {
mouseDownZB = null;
}
}
mousemove = e => {
const x = e.clientX - L,
y = e.clientY - T;
if (y > chartTop && y < chartBottom && x > chartLeft && x < chartRight ) {
mouseMovePosition = x;
if (mouseDownZB !== null) {
mouseSelected = [mouseDownZB, x];
} else {
mouseSelected = null;
}
} else {
mouseMovePosition = null;
}
}
mouseup = e => {
mouseDownZB = null;
mouseSelected = null;
}
drawOther = () => {
...
if (mouseSelected !== null) {
ctx.save();
ctx.fillStyle = "rgba(55, 183, 248, 0.5)";
ctx.beginPath();
ctx.rect(mouseSelected[0], chartTop, Math.abs(mouseSelected[0] - mouseSelected[1]), ylength);
ctx.fill();
}
}
上面這段代碼,實現了鼠標拖拽選擇區間,mouseup中將mouseDownZB,mouseSelected兩個變量置為null,此時我們已經選擇了一個區間,需要將沒有選擇的區間置為灰色,
因此我需要在將變量mouseSelected置為null前,賦值變量hasSelected = mouseSelected
drawOther方法中添加代碼
if (hasSelected !== null) {
ctx.save();
ctx.strokeStyle = '#CCCCCC';
ctx.fillStyle = 'rgba(230, 230, 230, 0.8)';
ctx.beginPath();
ctx.rect(chartLeft, chartTop, hasSelected[0] - chartLeft, ylength);
ctx.fill();
ctx.stroke();
ctx.beginPath();
ctx.rect(hasSelected[1], chartTop, chartRight - hasSelected[1], ylength);
ctx.fill();
ctx.stroke();
ctx.restore();
}
查看代碼,細心的同學可能已經發現了,第一次選擇區間后,再選擇區間,偶爾在上部會出現一道灰線,如下圖
這個問題與canvas劃線的方式有關,有興趣的同學自行百度,這里我們只需修改一下清除畫布方法即可
ctx.clearRect(chartLeft, chartTop - 1, xlength, ylength + 1);//清除變動區域
標記出已選擇部分還不夠,我們需要計算出選擇區域起止點具體時間
data[Math.ceil((hasSelected[0] - chartLeft) / xstep)].date;
data[Math.floor((hasSelected[1] - chartLeft) / xstep)].date
這樣一個簡單的附帶區間選擇的折線圖就完成了
查看es6簡化版在線示例及代碼
查看es5完整版版在線示例及代碼