canvas绘制四分之slider圆弧

dragArc.js

class DragAcr {
    constructor(param) {
      this.initParam(param)
      this.draw(this.value)
    }
    initParam(param) {
      const {
        el,
        startDeg = 0,
        endDeg = 1,
        innerColor = "#51e6b6",
        outColor = "#c0c0c0",
        innerLineWidth = 1,
        outLineWidth = 20, // 圆弧槽宽度
        counterclockwise = true,
        slider = 10,
        color = ["#06dabc", "#33aaff"],
        sliderColor = "#fff",
        sliderBorderColor = "#33aaff",
        value = 0,
        change = (v)=> { console.log(v) },
        textShow = false
      } = param;
  
      this.el = el;
      this.width = el.offsetWidth;
      this.height = el.offsetHeight;
      this.centerX = counterclockwise ? 0 : this.width
      this.centerY = this.height / 2
      this.radius = this.width - 30; //滑动路径半径
      this.initCanvas(el);
  
      this.startDeg = startDeg;
      this.endDeg = endDeg;
      this.innerColor = innerColor;
      this.outColor = outColor;
      this.innerLineWidth = innerLineWidth;
      this.outLineWidth = outLineWidth;
      this.counterclockwise = counterclockwise;
      this.slider = slider;
      this.color = color;
      this.sliderColor = sliderColor;
      this.sliderBorderColor = sliderBorderColor;
      this.value = value;
      this.textShow = textShow;
      this.minVal = 15;
      this.maxVal = 35
  
      this.change = change;
  
      this.isDown = false;
      this.event(el)
  
    }
    initCanvas(dom) {
      this.canvas = document.createElement("canvas");
      this.canvas.setAttribute("id", "dragArc");
      this.canvas.setAttribute("width", this.width);
      this.canvas.setAttribute("height", this.height);
      dom.appendChild(this.canvas);
      this.ctx = this.canvas.getContext("2d");
      this.isMobile = /Android|webOS|iPhone|iPod|BlackBerry/i.test(navigator.userAgent);
    }
    //绘图
    draw(value) {
      this.ctx.clearRect(0, 0, this.width, this.height);
  
      this.ctx.save();
  
      let startDeg = this.counterclockwise ? Math.PI * (2 - this.startDeg) : Math.PI * this.startDeg
      let endDeg = this.counterclockwise ? Math.PI * (2 - this.endDeg) : Math.PI * this.endDeg
  
      // 绘制内层圆弧
      // this.ctx.beginPath();
      // this.ctx.lineWidth = 1;
      // this.ctx.arc(this.center, this.center, this.radius - 20, startDeg, endDeg-180, this.counterclockwise); // 绘制内层圆弧
      // this.ctx.strokeStyle = this.innerColor;
      // this.ctx.stroke();
  
      // 绘制外侧圆弧
      this.ctx.beginPath();
      this.ctx.arc(this.centerX, this.centerY, this.radius, startDeg, endDeg, this.counterclockwise); // 绘制外侧圆弧
      this.ctx.strokeStyle = this.outColor;
      this.ctx.lineCap = "round";
      this.ctx.lineWidth = this.outLineWidth;
      this.ctx.stroke();
  
      let Deg = this.valToDeg(value)
  
  
      // 绘制可变圆弧
      // eslint-disable-next-line valid-typeof
      let themeColor = (typeof this.color == "String") ? this.color : this.setLinearGradient()
      this.ctx.beginPath();
      this.ctx.arc(this.centerX, this.centerY, this.radius, startDeg, Deg, this.counterclockwise); // 可变圆弧
      this.ctx.strokeStyle =  themeColor;
      this.ctx.lineCap = "round";
      this.ctx.lineWidth = this.outLineWidth;
      this.ctx.stroke();
  
      // 绘制滑块
      this.P = this.DegToXY(Deg)
      this.ctx.beginPath();
      this.ctx.moveTo(this.centerX, this.centerY, );
      this.ctx.arc(this.P.x, this.P.y, this.slider + 5, 0, Math.PI * 2, false); // 绘制滑块内侧
      this.ctx.fillStyle = this.sliderBorderColor;
      this.ctx.fill();
      this.ctx.beginPath();
      this.ctx.moveTo(this.centerX, this.centerY);
      this.ctx.arc(this.P.x, this.P.y, this.slider, 0, Math.PI * 2, false); // 绘制滑块
      this.ctx.fillStyle = this.sliderColor;
      this.ctx.fill();
  
      // 文字
      // if(!this.textShow) return;
      // this.ctx.font = `${this.center/4}px serif`;
      // this.ctx.fillStyle = themeColor;
      // this.ctx.textAlign = "center"
      // this.ctx.textBaseline = "bottom";
      // this.ctx.fillText(this.value, this.center, this.center);
  
    }
    //将值转化为弧度
    valToDeg(v) {
      const range = this.endDeg - this.startDeg;
      const defaultRang = this.maxVal - this.minVal;
      let val = range / defaultRang * (v-this.minVal);
      if(this.counterclockwise && (val !=0) ) val = 2 -val;
      const startDeg = this.counterclockwise ? (2 - this.startDeg) : this.startDeg;
      return (startDeg + val) * Math.PI;
    }
    // 弧度转化为对应坐标值
    DegToXY(deg) {
      let d = 2 * Math.PI - deg;
      return this.respotchangeXY({
        x: this.radius * Math.cos(d),
        y: this.radius * Math.sin(d)
      })
    }
  
    //canvas坐标转化为中心坐标
    spotchangeXY(point) {
      const spotchangeX = (i) => {
        return i - this.centerX
      }
      const spotchangeY = (i) => {
        return this.centerY - i
      }
      return {
        x: spotchangeX(point.x),
        y: spotchangeY(point.y)
      }
    }
  
    //中心坐标转化为canvas坐标
    respotchangeXY(point) {
      const spotchangeX = (i) => {
        return i + this.centerX
      }
      const spotchangeY = (i) => {
        return this.centerY - i
      }
      return {
        x: spotchangeX(point.x),
        y: spotchangeY(point.y)
      }
    }
  
    setLinearGradient(){
      const grad  = this.ctx.createLinearGradient(0,0, 0,this.width);
      this.color.forEach((e, i) => {
          if(i == 0){
              grad.addColorStop(0, e)
          }else  if(i == this.color.length - 1){ 
              grad.addColorStop(1, e)
          }else{
              grad.addColorStop(1/this.color.length * (i+1), e);
          }
      });
      return grad;
    }
  
    event(dom) {  //事件绑定
      if(this.isMobile){
          dom.addEventListener("touchstart", this.OnMouseDown.bind(this), false);
          dom.addEventListener("touchmove", this.throttle(this.OnMouseMove.bind(this)), false);
          dom.addEventListener("touchend", this.OnMouseUp.bind(this), false);
          return
      }
      dom.addEventListener("mousedown", this.OnMouseDown.bind(this), false);
      dom.addEventListener("mousemove", this.throttle(this.OnMouseMove.bind(this)), false);
      dom.addEventListener("mouseup", this.OnMouseUp.bind(this), false);
    }
  
    OnMouseMove(evt) {
      if (!this.isDown) return;
      let evpoint = {};
      evpoint.x = this.getx(evt);
      evpoint.y = this.gety(evt);
      let point = this.spotchangeXY(evpoint);
      let deg = this.XYToDeg(point.x, point.y);
      deg = this.counterclockwise ? deg : Math.PI * 2 - deg;
      let val = (deg/ Math.PI - this.startDeg) / (this.endDeg - this.startDeg)  * this.maxVal + this.minVal
      if(val<0) val = this.maxVal + val;
      if(val >= this.maxVal) val = this.maxVal;
      if(val <= this.minVal) val = this.minVal;
      if(Math.abs (val - this.value) > 10) return;
      this.animate = requestAnimationFrame(this.draw.bind(this,val));
      if(this.value != Math.round(val)){
          this.value = Math.round(val);
          this.change(this.value)
      }
    }
  
    OnMouseDown(evt) {
      let range = 10;
      let X = this.getx(evt);
      let Y = this.gety(evt);
      let P = this.P 
      let minX = P.x - this.slider - range;
      let maxX = P.x + this.slider + range;
      let minY = P.y - this.slider - range;
      let maxY = P.y + this.slider + range;
      if (minX < X && X < maxX && minY < Y && Y < maxY) {   //判断鼠标是否在滑块上 
        this.isDown = true;
      } else {
        this.isDown = false;
      }
    }
  
    OnMouseUp() {  //鼠标释放
      const _this = this
      cancelAnimationFrame(_this.animate);
      this.isDown = false
    }
  
    // 将坐标点转化为弧度
    XYToDeg(lx, ly) {
      let adeg = Math.atan(ly / lx)
      let deg;
      if (lx >= 0 && ly >= 0) {
        deg = adeg ;
      }
      if (lx <= 0 && ly >= 0) {
        deg = adeg + Math.PI;
      }
      if (lx <= 0 && ly <= 0) {
        deg = adeg + Math.PI;
      }
      if (lx > 0 && ly < 0) {
        deg = adeg + Math.PI * 2;
      }
      return deg
    }
  
    //获取鼠标在canvas内坐标x
    getx(ev) {
      if(!this.isMobile) return ev.clientX - this.el.getBoundingClientRect().left;
      return ev.touches[0].pageX - this.el.getBoundingClientRect().left;
    }
  
    //获取鼠标在canvas内坐标y
    gety(ev) {
      if(!this.isMobile) return ev.clientY - this.el.getBoundingClientRect().top;
      return ev.touches[0].pageY - this.el.getBoundingClientRect().top;
    }
    
    //节流
    throttle(func) {
      let previous = 0;
      return function() {
          let now = Date.now();
          let context = this;
          let args = arguments;
          if (now - previous > 10) {
              func.apply(context, args);
              previous = now;
          }
      }
    }
}

export default DragAcr

drawSlider.vue


<template>
    <div :id="nodeId" class="sliderMain"></div>
</template>
<script>
import DragAcr from './dragAcr.js'
export default {
    name: 'drawSlider',
    props: {
        // 绘制圆弧的id名称
        nodeId: {
            type: String,
            default: 'content'
        },
        // 绘制的起始角度
        // 3点钟方向为0,依次绘制90°后加0.5)
        startDeg: {
            type: Number,
            default: 0
        },
        // 绘制的结束角度
        endDeg: {
            type: Number,
            default: 0
        },
        // 未滑动过的圆弧槽颜色
        outColor: {
            type: String,
            default: '#eee'
        },
        // 绘制方向 顺时针 false, 逆时针 true
        counterclockwise: {
            type: Boolean,
            default: false
        },
        // 默认滑动的值
        value: {
            type: Number,
            default: 15
        },
    },
    mounted () {
        this.initDraw();
    },
    methods: {
        //  绘制圆弧初始化
        initDraw(){
            const {
                nodeId,
                startDeg,
                endDeg,
                outColor,
                counterclockwise,
                value
            } = this
            const dom = document.getElementById(nodeId)
            new DragAcr({
                el: dom,
                startDeg,
                endDeg,
                outColor,
                counterclockwise,
                value,
                change: (val)=> {
                    this.$emit('changeVal', val)
                }
            })
        },
    }
}
</script>
<style lang="scss" scoped>
    .sliderMain{
        margin : auto;
        height: 400px;
        width: 200px;
    }
</style>

index.vue



<template>
    <div>
        <div class="sliderWrap">
            <drawSlider :value="leftVal" :nodeId="'content'" :startDeg="1" :endDeg="1.46" @changeVal="leftChange">
            </drawSlider>
            <drawSlider :value="rightVal" :nodeId="'content2'" :startDeg="0" :endDeg="0.46" :counterclockwise="true"
                @changeVal="rightChange"></drawSlider>
        </div>
        <div>
            <p>leftVal: {{ this.leftVal }}</p>
            <p>rightVal: {{ this.rightVal }}</p>
        </div>
    </div>
</template>
<script>
import drawSlider from './drawSlider.vue';
export default {
    name: 'slidertest',
    components: { drawSlider },
    data() {
        return {
            leftVal: 18,
            rightVal: 20,
            // 控制定时器
            leftTimes: '',
            rightTimes: ''
        }
    },
    methods: {
        leftChange(val) {
            this.leftVal = val
            this.throttleFn('leftTimes', 1500, val)
        },
        rightChange(val) {
            this.rightVal = val
            this.throttleFn('rightTimes', 1500, val)
        },
        // 防抖函数
        throttleFn(timesType, de, val) {
            if (this[timesType] !== '') {
                clearTimeout(this[timesType])
            }
            this[timesType] = setTimeout(() => {
                this.ajaxFn(val)
            }, de)
        },
        // 模拟ajax请求,业务逻辑代码
        ajaxFn(val) {
            console.log('value', val);
        }
    }
}
</script>
<style lang="scss" scoped>
.sliderWrap {
    background-color: #fff;
    height: 400px;
    border-radius: 50%;
    display: flex;
    justify-content: center;
}
</style>

最终效果如下:
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值