在Vue中用canvas实现多进度条

在我准备在年末发布的网站改进中,添加了首页banner切换的进度条。

说一下我想实现的东西。我要求切换3张图,某个图此时正在播放的时候,只有对应的那个进度条运作,其他进度条为0%状态。

首先我们就需要定时,比如我定时10秒钟切换一次。那么我就先要定义一个循环的定时器。

var interval = setInterval(function () {}.bind(this),50)

每50毫秒,执行一次函数。如果我要求10秒钟切换一次,那么我总共需要计数200次,添加一个计数变量per(这个per之后顺便实现其他事情),条件判断里边执行相应的数据修改操作,per++,然后再判断per是否达到200,达到清零。

在Vue中,我是通过组件切换来切换banner的。也就是<component :is='img'></component>;因此条件判断里边只需要修改img为相应的组件名即可。现在已经完成了图片切换。

现在需要添加进度条了。我选择了canvas来实现进度条,因为canvas的灵活度非常大,可以随便整。用canvas画进度条还是非常轻松的,只需要简单看一下HTML 5 Canvas 参考手册就可以画出来。

我要求整个进度条(3个进度条一起)的宽度为整个窗口宽度,每个进度条平分宽度。目前最容易的莫过于用flex布局。因此给整个进度条一个div,然后里边再装3个div,每个div里装一个canvas元素。由于三个进度条是完全一样的,因此我直接选择组件,节省时间又方便。(选择装canvas的div为模板的根元素)

进度条的进度自然是和时间有关系的,因此在组件中,也需要定义一个定时器(这个计时器可以不设置太高的频率,但是就会影响进度条的“分辨率”,看完下文就明白了)。当时间记满10秒,进度自然满了,所以进度条的宽度和计数变量per之间有联系,需要从父实例那里读取per,所以在子组件中的props中添加一个per(名字随便取便于辨别,我选择per),以便父实例传入参数。

现在还有一个问题就是,因为canvas的宽度和高度不是自适应的,需要自设,因此我们至少还需要读取canvas的父元素div的宽度(高度直接给几px也可以,但是宽度是根据窗口大小而定的),所以这个时候还需要获得div自己的宽度,查阅Vue的API文档,可以知道通过this.$el可以获取当前实例/组件对应的根元素,所以说this.$el.offsetWidth就是div的宽度。这个时候从生命周期钩子函数里把宽度值赋给数据,然后在canvas元素上用:width='width'就可以把宽度传到canvas里边了。

说一下canvas的基本操作,canvas是通过JS来画图形的。首先,通过var ctx = this.$refs.ctx.getContext('2d')得到画布canvas(在Vue的组件中,我先给canvas添加一个ref属性,值为’ctx’,然后就可以通过this.$refs.ctx得到canvas元素,然后通过getContext(‘2d’)得到画布。也可以用原生JS代码来选中这个元素。),之后ctx.beginPath()开一个新路径,意思就是要拿出一支笔,然后用ctx.strokeStylectx.lineWidth设置线条的样式和宽度,再用ctx.moveTo(,)ctx.lineTo(,)选择线段的起点和终点(两个参数,对应坐标轴的位置,第一个填x轴位置,第二个填y轴位置),最后ctx.stroke()画线。完成。

因此,进度条的宽度改变来自于lineTo中的第一个参数,当per达到200时,此时进度条满,所以说进度条的宽度和总宽度的关系为per/200*width。每次进度条组件的计时器的执行函数中,就需要调用一次整个画画的过程,所以建议在每次ctx.beginPath()之前,先执行ctx.clearRect(,,,)清除画板,四个参数,前两个为清除区域矩形的左上角的点的坐标,后两个为清除区域矩形右下角的点的坐标,直接清除整个canvas即可,ctx.clearRect(0,0,width,height)。

根据时间画进度条已经实现,但是,现在如果不是这个进度条的showtime呢?照上面的做法,三个进度条都会同时进行,显然没有达到我的目的。

因此,还需要一个flag,来指示现在的进度条运行状态。把这个flag作为实例的数据,然后传入三个进度条组件,组件判断flag的值(比如0,1,2),是否对应目前的进度条,所以,最简单的方法是再给每个进度条组件传入id,id写成xx0,xx1,xx2,通过判断id是否为’xx’ + flag来确认目前的状态,如果是,执行画画步骤,如果不是则执行clearRect(,,,)。

现在已经完全达到了我的目标,如果你熟悉Vue,做出这个进度条应该不会有什么问题。

总结一下,整个实现过程可以视为一个实例定时器和三个进度条定时器的故事。实例给三个下属进度条发送信号per和flag,对应进度状态和进度条运行状态。三个下属拥有同样的接收装置,但是下属需要一个特别标志id才能对flag做出判断。同样,接收装置可以设置更低的接收频率,当然,会影响进度条的“分辨率”,导致进度条看起来不那么顺滑,但是因为减少了执行次数,降低了浏览器的负载。在做的过程中,用到了很多Vue里边的内容,比如说prop(传参数),ref和$el(获取元素),transtion(过渡,主要用于图片切换,和本次进度条实现无关)这些,也算是初学Vue的一次比较不错的实践。另外,模块化真的是方便和顺眼。

我现在对网站的风格依然很迷茫,不知道我的网站应该做成什么样子,还没有自己的想法。可能是学的知识还不够,脑洞还不太够。

发表评论

电子邮件地址不会被公开。 必填项已用*标注