上一篇,我們討論了如何通過(guò)canvas繪制一個(gè)折線圖
接下來(lái),我們將給這個(gè)折線圖實(shí)現(xiàn)鼠標(biāo)交互效果
首先,鼠標(biāo)在我們的折線圖上移動(dòng)時(shí),我們要一個(gè)藍(lán)色的豎線跟隨鼠標(biāo)移動(dòng)
要實(shí)現(xiàn)這個(gè)效果,我們一步一步來(lái)
1、我們需要在canvas的mousemove事件方法中,根據(jù)鼠標(biāo)位置clientX/Y,和容器的top/left值,計(jì)算出鼠標(biāo)所在處的canvas坐標(biāo);
2、根據(jù)鼠標(biāo)坐標(biāo)判斷鼠標(biāo)是否在折線圖上,如果鼠標(biāo)在折線圖上,將鼠標(biāo)x軸坐標(biāo)(即橫向坐標(biāo))保存在mousemovePositionx變量中,如果鼠標(biāo)不在折線圖上,將mousemovePositionx變量設(shè)置為null;
3、drow方法中判斷mousemovePositionx變量是否為null,若不為null,則根據(jù)mousemovePositionx的值繪制藍(lán)色線段
在線展示及代碼
然后,我們分解一下鼠標(biāo)拖動(dòng)選擇時(shí)間區(qū)間這個(gè)操作
1、鼠標(biāo)按下; 2、鼠標(biāo)移動(dòng); 3、鼠標(biāo)抬起
實(shí)際上我們只需要在鼠標(biāo)按下時(shí)記錄鼠標(biāo)按下的位置,鼠標(biāo)抬起時(shí),根據(jù)鼠標(biāo)抬起位置和之前記錄的鼠標(biāo)按下的位置,便可以得到拖拽動(dòng)作選擇的區(qū)間
但是這樣做,鼠標(biāo)移動(dòng)時(shí)沒(méi)有任何交互效果
為了更好的用戶體驗(yàn),我們可以在鼠標(biāo)移動(dòng)方法中,通過(guò)鼠標(biāo)位置與鼠標(biāo)按下位置,將已選擇區(qū)間記錄下來(lái),供draw方法繪制相應(yīng)交互效果
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();
}
}
上面這段代碼,實(shí)現(xiàn)了鼠標(biāo)拖拽選擇區(qū)間,mouseup中將mouseDownZB,mouseSelected兩個(gè)變量置為null,此時(shí)我們已經(jīng)選擇了一個(gè)區(qū)間,需要將沒(méi)有選擇的區(qū)間置為灰色,
因此我需要在將變量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();
}
查看代碼,細(xì)心的同學(xué)可能已經(jīng)發(fā)現(xiàn)了,第一次選擇區(qū)間后,再選擇區(qū)間,偶爾在上部會(huì)出現(xiàn)一道灰線,如下圖

這個(gè)問(wèn)題與canvas劃線的方式有關(guān),有興趣的同學(xué)自行百度,這里我們只需修改一下清除畫(huà)布方法即可
ctx.clearRect(chartLeft, chartTop - 1, xlength, ylength + 1);//清除變動(dòng)區(qū)域
標(biāo)記出已選擇部分還不夠,我們需要計(jì)算出選擇區(qū)域起止點(diǎn)具體時(shí)間
data[Math.ceil((hasSelected[0] - chartLeft) / xstep)].date;
data[Math.floor((hasSelected[1] - chartLeft) / xstep)].date
這樣一個(gè)簡(jiǎn)單的附帶區(qū)間選擇的折線圖就完成了
查看es6簡(jiǎn)化版在線示例及代碼
查看es5完整版版在線示例及代碼