【canvas】绘图和动画的总结

709 阅读4分钟

看到一篇《用初中数学知识撸一个canvas环形进度条》关于如何绘制canvas环形进度条的文章,深受启发;所以自己手把手照着葫芦画瓢一般写了一遍,总结一下!

canvas初始化

var cans = document.getElementById("myCanvas");
var context = cans.getContext("2d");

小程序中:

var context = tt.createCanvasContext("canvas");

//必须放在"methods:{}"的方法中,不能作为变量放在 page({})外部。因为需要等页面初始化完成后,再初始化 canvas


绘制图形:空心与实心

//绘制实心矩形:
context.fillStyle = 'blue';
context.fillRect(100,125,50,50); //(x,y,w,h)

//绘制空心矩形
context.strokeStyle = 'yellow';
context.strokeRect(100,180,50,50) //(x,y,w,h)

小程序中:

//绘制实心矩形:
context.setFillStyle('#ff0000');
context.arc(100,75,50,0,2*Math.PI);
//context.fillRect(100,125,50,50); //(x,y,w,h)
context.fill();

//绘制空心矩形
context.setStrokeStyle('#f00000');
context.arc(100,75,50,0,2*Math.PI);
//context.strokeRect(100,180,50,50) //(x,y,w,h)
context.stroke();


绘制文字

//画文字
ctx.font = '36px Arial,"Microsoft YaHei"'
ctx.fillStyle = "#f00000";
ctx.textAlign = 'center'
ctx.textBaseline = 'middle'
ctx.fillText('文字', w, h); //w,h 画布宽、高

小程序:

//画文字
context.setFontSize(36)
context.setFillStyle('#f0000');
context.setTextAlign('center')
context.setTextBaseline('middle')
context.fillText('文字', w, h); //w,h 画布宽、高

环形进度条示例

<!DOCTYPE html>
<html>
<body>
<canvas id="myCanvas"></canvas>

<script>

/*
//绘制实心矩形:
context.fillStyle = 'blue';
context.fillRect(100,125,50,50); //(x,y,w,h)

//绘制空心矩形
context.strokeStyle = 'yellow';
context.strokeRect(100,180,50,50) //(x,y,w,h)
*/



var cans = document.getElementById("myCanvas");
var ctx = cans.getContext("2d");


var percent = 0,//分数
	classifier = '分'//单位
var counts = 58
var gradient = null;
var circleRadius = 55, //圆环半径
	pointRadius = 5,   //圆点半径
	pointColor= '#FF4949', //圆点颜色
	circleWidth = 2, //圆环线宽
	lineWidth = 8,   //进度条线宽
	circleColor= '#f00000',
	lineColorStops = [
	  { percent: 0, color: '#FF9933' },
	  { percent: 1, color: '#FF4949' }
	];


//设置canvas尺寸
cans.width = canvasSize()
cans.height = canvasSize()


//***设置动画***
function animateDrawArc(){


var startDeg = 270;
var nextDeg = getTargetDegByPercentage(percent)
var startArc = deg2Arc(startDeg);
var nextArc = deg2Arc(nextDeg);


// 根据角度获取点的位置
function getPositionsByDeg(deg) {
    let x = 0;
    let y = 0;
    if (deg >= 0 && deg <= 90) {
        // 0~90度
        x = circleRadius * (1 + Math.cos(deg2Arc(deg)))
        y = circleRadius * (1 + Math.sin(deg2Arc(deg)))
    } else if (deg > 90 && deg <= 180) {
        // 90~180度
        x = circleRadius * (1 - Math.cos(deg2Arc(180 - deg)))
        y = circleRadius * (1 + Math.sin(deg2Arc(180 - deg)))
    } else if (deg > 180 && deg <= 270) {
        // 180~270度
        x = circleRadius * (1 - Math.sin(deg2Arc(270 - deg)))
        y = circleRadius * (1 - Math.cos(deg2Arc(270 - deg)))
    } else {
        // 270~360度
        x = circleRadius * (1 + Math.cos(deg2Arc(360 - deg)))
        y = circleRadius * (1 - Math.sin(deg2Arc(360 - deg)))
    }
    return { x, y }
}


// 根据开始角度和进度百分比求取目标角度
function getTargetDegByPercentage(percentage) {
	if (percentage === 100) {
		return startDeg + 360
	} else {
		const targetDeg = (startDeg + 360 * percentage / 100) % 360
		return targetDeg
	}
}


	//画圆环
	ctx.beginPath();
	ctx.strokeStyle= circleColor;
	ctx.lineWidth = circleWidth;
	ctx.arc(outerRadius(), outerRadius(), circleRadius, 0, deg2Arc(360))
	ctx.stroke();



	//画文字
	ctx.font = '36px Arial,"Microsoft YaHei"'
	ctx.fillStyle = "#f00000";
	ctx.textAlign = 'center'
	ctx.textBaseline = 'middle'
	ctx.fillText(percent, outerRadius(), outerRadius());
	ctx.font = '15px Arial,"Microsoft YaHei"'
	ctx.fillText(classifier, outerRadius() + 30, outerRadius() + 5);


	//画进度条/设置渐变色
	gradient = ctx.createLinearGradient(circleRadius, 0, circleRadius, circleRadius * 2);
	lineColorStops.forEach(item => {
		gradient.addColorStop(item.percent, item.color);
	});

	//画进度弧
	ctx.strokeStyle = gradient;
	ctx.lineWidth = lineWidth;
	ctx.beginPath();
	ctx.arc(outerRadius(), outerRadius(), circleRadius, startArc, nextArc);
	ctx.stroke();



	// 画进度圆点
	const pointPosition = getPositionsByDeg(nextDeg);
	ctx.fillStyle = pointColor;
	ctx.beginPath();
	ctx.arc(pointPosition.x + pointRadius, pointPosition.y + pointRadius, pointRadius, 0, deg2Arc(360));
	this.ctx.fill();


}



// 外围半径
function outerRadius() {
	return circleRadius + pointRadius
}

// canvas宽/高
function canvasSize() {
	return 2 * outerRadius()
}

// deg转弧度
function deg2Arc(deg) {
    return deg / 180 * Math.PI
}



//循环调用动画
var countDown = function () {
	// 清空画布
    ctx.clearRect(0, 0, cans.width, cans.height);
	
	percent ++
	console.info('动画', percent)
	animateDrawArc()
	
	if ( percent == counts) {
		return false
	}else {
		count()
	}
}
var count = function (){
	setTimeout(function(){
		countDown()
	}, 10)
}

//初始化调用
countDown()

</script> 
</body>
</html>


小程序:

<!--canvas环形进度条-->
<canvas canvas-id="canvas" class="canvas" style="width: {{canvasSize}}px; height: {{canvasSize}}px;"></canvas>

// pages/component/circle/circle.js

var startDeg = 270;
var gradient = null
var percents = 0 //赋值分数

Component({
  data: {
    canvasSize: 0
  },
  properties: {
    //接收分数
    percent: {
      type: Number,
      value: 60
    },

    fontSize: {
      type: Number,
      value: 36
    },
    fontColor: {
      type: String,
      value: '#3B77E3'
    },

    circleRadius: {
      type: Number,
      value: 55
    },
    pointRadius: {
      type: Number,
      value: 5
    },
    circleWidth: {
      type: Number,
      value: 2
    },
    lineWidth: {
      type: Number,
      value: 8
    },
    pointColor: {
      type: String,
      value: '#3B77E3'
    },
    circleColor: {
      type: String,
      value: '#3B77E3'
    },
    lineColorStops: {
      type: Object,
      value: [
          { percent: 0, color: '#13CDE3' },
          { percent: 1, color: '#3B77E3' }
      ]
    },


  },
  methods: {

    // 外围半径
    outerRadius: function() {
      return this.properties.circleRadius + this.properties.pointRadius
    },

    // canvas宽/高
    canvasSize: function(e) {
      console.log('properties', e)
      this.setData({
        canvasSize: 2 * this.outerRadius()
      })

      this.countDown()
    },

    //循环调用动画
    countDown: function () {
      let that = this

      percents ++
      console.info('动画', percents)
      this.animateDrawArc()
      
      if ( percents == that.properties.percent) {
          return false
      }else {
          that.count()
      }
    },
    count: function (){
      let that = this
      setTimeout(function(){
          that.countDown()
      }, 30)
    },


    //设置动画
    animateDrawArc: function(){
      var that = this;
      var context = tt.createCanvasContext("canvas");

      var nextDeg = this.getTargetDegByPercentage(percents);
      var startArc = this.deg2Arc(startDeg);
      var nextArc = this.deg2Arc(nextDeg);

      var outerRadius = this.outerRadius(),
          circleRadius = this.properties.circleRadius,
          pointRadius = this.properties.pointRadius;

          //画圆环
          context.beginPath();
          context.setStrokeStyle(that.properties.circleColor);
          context.setLineWidth(that.properties.circleWidth);
          context.arc(outerRadius, outerRadius, circleRadius, 0, that.deg2Arc(360));
          context.stroke();

          //画文字
          context.setFontSize(that.properties.fontSize)
          context.setFillStyle(that.properties.fontColor);
          context.setTextAlign('center')
          context.setTextBaseline('middle')
          context.fillText(percents, outerRadius, outerRadius);
          context.setFontSize(14)
          context.fillText('分', outerRadius + 30, outerRadius + 4);

          //画进度条//设置渐变色
          gradient = context.createLinearGradient(circleRadius, 0, circleRadius, circleRadius * 2)
          that.properties.lineColorStops.forEach(item => {
              gradient.addColorStop(item.percent, item.color);
          });
          //画进度弧
          context.setStrokeStyle(gradient);
          context.setLineWidth(that.properties.lineWidth);
          context.beginPath();
          context.arc(outerRadius, outerRadius, circleRadius, startArc, nextArc);
          context.stroke();

          //画圆点
          const pointPosition = that.getPositionsByDeg(nextDeg);
          context.setFillStyle(that.properties.pointColor);
          context.beginPath();
          context.arc(pointPosition.x + pointRadius, pointPosition.y + pointRadius, pointRadius, 0, that.deg2Arc(360));
          context.fill();
      
      //画布绘制
      context.draw();

    },

    //根据开始角度和进度百分比求取目标角度
    getTargetDegByPercentage: function(percentage) {
      if (percentage === 100) {
        return startDeg + 360
      } else {
        const targetDeg = (startDeg + 360 * percentage / 100) % 360
        return targetDeg
      }
    },

    // 根据角度获取点的位置
    getPositionsByDeg: function(deg) {
        let x = 0;
        let y = 0;
        if (deg >= 0 && deg <= 90) {
            // 0~90度
            x = this.properties.circleRadius * (1 + Math.cos(this.deg2Arc(deg)))
            y = this.properties.circleRadius * (1 + Math.sin(this.deg2Arc(deg)))
        } else if (deg > 90 && deg <= 180) {
            // 90~180度
            x = this.properties.circleRadius * (1 - Math.cos(this.deg2Arc(180 - deg)))
            y = this.properties.circleRadius * (1 + Math.sin(this.deg2Arc(180 - deg)))
        } else if (deg > 180 && deg <= 270) {
            // 180~270度
            x = this.properties.circleRadius * (1 - Math.sin(this.deg2Arc(270 - deg)))
            y = this.properties.circleRadius * (1 - Math.cos(this.deg2Arc(270 - deg)))
        } else {
            // 270~360度
            x = this.properties.circleRadius * (1 + Math.cos(this.deg2Arc(360 - deg)))
            y = this.properties.circleRadius * (1 - Math.sin(this.deg2Arc(360 - deg)))
        }
        return { x, y }
    },

    //deg转弧度
    deg2Arc: function(deg) {
      return deg / 180 * Math.PI
    }


  },

  //组件生命周期函数,在组件布局完成后执行
  ready: function() { 
    this.canvasSize(this.properties)
  },


})