【JavaScript】canvas实现可拖拽和可修改的分段进度条

半吊子前端,刚开始慢慢摸索JS,参考了很多代码,勉强实现功能,有更好的方法也希望大家能跟我分享,感激不尽!

实现效果解说:

要求实现一个如上图的进度条,用来设置及显示超速等级

初始显示已保存的数据及对应的进度条状态,不同等级进度条会显示不同的颜色

1: 鼠标按住圆形浮标拖动,可修改2中数值,同时进度条也会随之修改颜色,2也会随着移动 

2:这里显示数值。包括:①刚进入界面时,显示之前保存的数值    ②拖动浮标1时,随之一起变化的数值    ③此处同时是个输入框,可以输入想要的数值进行修改

3:此处显示超速等级范围

还有一个 保存按钮,进度条上的数据修改完成后,点击保存:123处都会进行修改到相应的位置或数据 

代码实现:

HTML代码:

        <div id="stage" style="width:800px;margin:30px auto 0px;position: relative;height:40px;"></div>

主要是给个id ,样式自己按需求设置

JS代码:   

data数据声明:

 data () {
      return {
        //颜色数组
        colors:['#769696', 'rgba(0,255,255,0.6)', 'rgba(255,229,0,0.75)', 'rgba(255,0,0,0.8)', '#769696'],

        //画布
        axis:'',
        axisContext:'',

        //游标点集
        points:[],
        //游标单位
        cursorUnit:'',

        //各种样式
        strokeStyle:'',
        fillStyle:'',
        isFill:'',
        
        height:'',
        width:'',
        id:'',
    }
},

注:第一个版本纯JS的代码是分成两个函数的,因为项目是VUE写的,所以我拆成了三个函数

draw()  :画图

slide()   :实现进度条

addListener()   :监听变化

下面是拆完的的代码,放在 methods方法里方便调用,之后我会上传代码,两种都会放在里面

canvas绘图函数:

再来啰嗦,该函数画的包括:进度条横轴 ,以及游标(分输入框下方的圆形及倒三角)

为了都可以用该函数画图 ,所以改的乱七八糟 ,有更好的写法请大神也教教我吧!!

先看看W3c的canvas教程

draw: function (id,points, strokeStyle, fillStyle, isStroke, isFill ,radius) {
        //id:给每个canvas标签一个唯一id  points:图案形状点数组(之后会详说) 
        //strokeStyle, fillStyle, isStroke,isFill:这都是样式 我这里为空
        //radius:这是为了画圆形游标,画横轴时没用

        var canvas = document.createElement('canvas');
        var context = canvas.getContext('2d');
        //上边时canvas必备 建议先看看W3c的canvas教程

        //这里遍历points点数组,求最大xy,用于设置画布
        var maxX = 0, maxY = 0;
        for (var i = 0; i <points.length; i++) {
          if (points[i][0] > maxX)
            maxX =points[i][0];
          if (points[i][1] > maxY)
            maxY =points[i][1];
        }

        //这是为游标需要设置,进度条横轴不需要
        if(radius==0) canvas.height = maxY;
        else canvas.height = maxY+radius;

        canvas.width = maxX;
        //画布大小设置完毕,开始画图
        context.beginPath();
        context.strokeStyle = strokeStyle;
        context.fillStyle = fillStyle;
        //先画第一个点 ,遍历数组画每个点,拼出图形
        context.moveTo(points[0][0],points[0][1]);      
        for (var i = 1; i <points.length; i++) {
          context.lineTo(points[i][0], points[i][1]);
        }
        //因为moveTo,lineTo不能画圆,用arc()来画
        if(radius!=0){
          context.arc(radius,3*radius/2,radius/2,0,2*Math.PI)
        }
        context.closePath()
        if (isFill)
          context.fill();
        if (isStroke)
          context.stroke();
       
        canvas.style.position = "absolute";
        canvas.id = id;
        //根据id获取div,把canvas作为子元素添加
        document.getElementById('stage').appendChild(canvas);
        return canvas;
      },
      

points点数组:

坐标轴的朝向如图,下面举例

我画的是个倒三角:  points= [[0, 0], [x* 2, 0], [x, x], [0, 0]];      //顺时针做的

若画个矩形   :points= [[0, 0], [x* 2, 0], [2x, x], [0, x],[0,0]];

进度条实现函数:

      Slide:function(height, width, blockCount,  cursorUnit) {
                    //画布的高、宽、进度条分段数、游标尺寸基本单位(相当于上面的x1)
        this.height = height;
        this.width = width;
        this.cursorUnit = cursorUnit;
        
        //定义游标形状  这是倒三角
        var cursor = [[0, 0], [cursorUnit * 2, 0], [cursorUnit, cursorUnit], [0, 0]];

        //横轴  画进度条
        this.axis = this.draw('axis',[[0, 0], [width, 0], [width, height], [0, height], [0, 0]], 'blue', undefined, true, false,0);
        this.axis.style.borderRadius = 20 + "px";    //圆角样式

        //游标的数组   画游标及上方的输入框
        this.points = [];      //存储画好的游标

        for (var i = 0; i < blockCount; i++) {

          //调用画图函数,画游标
          var kk= this.draw(i,cursor, undefined, 'white', false, true,cursorUnit);
          this.points[i]=kk;

          //调整垂直位置
          this.points[i].style.top = -(cursorUnit/2 + this.height) + 'px';
         
          //每个游标的描述块 即上方的输入框 ,可根据需要改textarea,button什么的都行
          var description = document.createElement('input');

          //设置描述块 id、类型、样式
          description.id = i + 'description';
          description.type = "text";
          description.style.position = "absolute";
          description.style.width = 70 + 'px';
          description.style.height = 50 + 'px';
          description.style.backgroundColor = "#fff";
          description.style.color = "#011a44";
          description.style.borderRadius = 5 + 'px';
          description.style.textAlign = 'center';
          description.style.fontSize = 25 + 'px';
          description.style.top = -(cursorUnit  + 50) + 'px';

           //读取数据 设置初始值 这里的this.都是数据库读取的数据,可根据需求修改
            //或者直接改成 25,50,75,100
          if (i == 0){
            description.value = this.waynstart + '%';}
          else if(i==1){
            description.value = this.commonstart + '%';
          } else if(i==2){
            description.value = this.severitystart + '%';
          }else if(i==3){
            description.value = this.severityend + '%';
          }

          //求该点在横轴上,应该的位置
          var nowP = parseInt(description.value)*0.01*width
          //调整游标及输入框到相应位置
          this.points[i].style.left = nowP- this.cursorUnit + 'px';
          description.style.left = nowP - 35 + 'px';

          document.getElementById('stage').appendChild(description);
        }

        //横轴填色 
        this.axisContext = this.axis.getContext('2d');

        for (var i = 0; i < blockCount; i++) {       
          //读取颜色数组颜色
          this.axisContext.fillStyle = this.colors[i];

          //设置要填色的位置,相对于横轴
          if(i==0)    var prePoint=0
          else{
            var prePoint = parseInt(document.getElementById(i-1+'description').value)*0.01*width
          }
          var nowPoint = parseInt(document.getElementById(i+'description').value)
          nowPoint = nowPoint*0.01*width

          //开始填色
          this.axisContext.fillRect(prePoint, 0, nowPoint-prePoint, this.height);
           
          if(i==blockCount-1){            //补充最后的颜色
            this.axisContext.fillStyle = this.colors[++i];
            this.axisContext.fillRect(nowPoint, 0, width-nowPoint, this.height);
          }
        }
      },

监听进度条函数

addListener :function() {
        //当前拖动游标
        var drager = undefined;
        //鼠标前一个时刻的横坐标
        var pre = undefined;
        //当前拖动的游标可活动范围的最左坐标
        var left = undefined;
        //当前拖动的游标可活动范围的最右坐标
        var right = undefined;
        //当前拖动游标的ID
        var id = undefined;
        var temp = this;

        //每个游标添加鼠标按下事件
        for (var i = 0; i < temp.points.length; i++) {
          temp.points[i].onmousedown = function(e) {
        //获取选择游标的相关信息
            drager = this;
            pre = e.clientX - this.cursorUnit;
            id = parseInt(this.id);

        //游标左右邻的位置
            left = (id == 0 ? -temp.cursorUnit : parseInt(temp.points[id - 1].style.left) + temp.cursorUnit);
            right = (id == temp.points.length - 1 ? parseInt(temp.width) - temp.cursorUnit : parseInt(temp.points[id + 1].style.left) - temp.cursorUnit);

          }
        }

        //松键设置
        document.onmouseup = function(e) {
          if (drager != undefined) {

            drager = undefined;
            pre = undefined;
            id = undefined;
          }
        }
        //鼠标移动设置
        document.onmousemove = function(e) {
          if (drager != undefined) {
            //获取鼠标位置,计算此刻在横轴的位置
            //clientX 事件属性返回当事件被触发时鼠标指针向对于浏览器页面(或客户区)的水平坐标
            var tmp = parseInt(drager.style.left) + e.clientX - pre;

            //拖动位置不可越过左右邻
            if (tmp >= left && tmp <= right) {
            //更新游标位置
              drager.style.left = tmp + 'px';

            //更新描述块位置、显示数值
              var description = document.getElementById(id + 'description');
              description.style.left = tmp - 20 + 'px';
              description.value = parseInt(Math.round((tmp + temp.cursorUnit) / temp.width * 10000) / 100) + '%';

            //更新进度条分段颜色
              temp.axisContext.fillStyle = temp.colors[id];
              temp.axisContext.fillRect(left, 0, tmp - left + temp.cursorUnit, temp.height);
              temp.axisContext.fillStyle = temp.colors[id + 1];
              temp.axisContext.fillRect(tmp + temp.cursorUnit, 0, right - tmp, temp.height);
            }
            pre = e.clientX;
          }
        }
      },

调用方式:

我放在了 钩子函数Updated中执行

updated() {
      this.Slide(10, 800, 4, 15)            //根据需求设置
      //添加游标响应事件
      this.addListener()            //也可以直接放到slide函数中
    }

有个问题:

第一次拖动时,例如【40%游标】会在靠近【61%游标】出现如图的一块小阴影区 ,当拖动【61%游标】后会消失,之后再拖动也不会出现,只在第一次有

第一次拖动【61%游标】也是一样 ,再次拖动就不会有这个问题

不知道怎么解决,请会的大神一定评论区教教我!!!

 

代码下载:https://download.csdn.net/download/sunshine641/14032635

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值