上一篇,我们讨论了如何通过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完整版版在线示例及代码
网友评论