关于canvas:面试官问我会canvas-我可以绘制一个烟花🎇动画

前言

在咱们日常开发中贝塞尔曲线无处不在:

  1. svg 中的曲线(反对 2阶、 3阶)
  2. canvas 中绘制贝塞尔曲线
  3. 简直所有前端2D或3D图形图表库(echarts,d3,three.js)都会应用到贝塞尔曲线

所以把握贝塞尔曲线势在必得。 这篇文章次要是实战篇,不会介绍和贝塞尔相干的常识, 如果有同学对贝塞尔曲线不是很分明的话:能够查看我这篇文章——深刻了解SVG

绘制贝塞尔曲线

第一步咱们先创立ctx, 用ctx 画一个二阶贝塞尔曲线看下。二阶贝塞尔曲线有1个控制点,一个终点,一个起点。

const canvas = document.getElementById( 'canvas' );
const ctx = canvas.getContext( '2d' );
ctx.beginPath();
ctx.lineWidth = 2;
ctx.strokeStyle = '#000';
ctx.moveTo(100,100)
ctx.quadraticCurveTo(180,50, 200,200)
ctx.stroke();

这样咱们就画好了一个贝塞尔曲线了。

绘制贝塞尔曲线动画

画一条线谁不会哇?接下来文章的主体内容。 首先试想一下动画咱们必定一步步画出曲线? 然而这个ctx给咱们全副画进去了是不是有点问题。咱们从新看下二阶贝塞尔曲线的实现过程动画,看看是否有思路。

从图中能够剖析得出贝塞尔上的曲线是和t有关系的, t的区间是在0-1之间,咱们是不是能够通过二阶贝塞尔的曲线方程去算出每一个点呢,这个专业术语叫离散化,然而这样的得进去的点的信息是不太准的,咱们先这样实现。

先看下方程:

咱们模仿写出代码如如下:

//这个就是二阶贝塞尔曲线方程
function twoBezizer(p0, p1, p2, t) {
  const k = 1 - t
  return k * k * p0 + 2 * (1 - t) * t * p1 + t * t * p2
}

//离散
function drawWithDiscrete(ctx, start, control, end,percent) {
    for ( let t = 0; t <= percent / 100; t += 0.01 ) {
        const x = twoBezizer(start[0], control[0], end[0], t)
        const y = twoBezizer(start[1], control[1], end[1], t)
        ctx.lineTo(x, y)
    }
}

咱们看下成果:

和咱们画的简直是截然不同,接下啦就用requestAnimationFrame 开始咱们的动画给出以下代码:

let percent = 0
function animate() {
    ctx.clearRect( 0, 0, 800, 800 );
    ctx.beginPath();
    ctx.moveTo(100,100)
    drawWithDiscrete(ctx,[100,100],[180,50],[200,200],percent)
    ctx.stroke();
    percent = ( percent + 1 ) % 100;
    id =  requestAnimationFrame(animate)
}
animate()

这里有两个要留神的是, 我是是percent 一直加1 和100 求余,所以呢 percent 会一直地从1-100 这样往返,OK所以咱们必须要动画之前做一次区域清理, ctx.clearRect( 0, 0, 800, 800 ); 这样就能够一直的从开始到完结周而复始,咱们看下成果:

看着样子是不是还不错哈哈哈😸。

绘制贝塞尔曲线动画办法2

你认为这样就完结了? 当然不是难道咱们真的没有方法画出某一个t的贝塞尔曲线了? 以后不是,这里放一下二阶贝塞尔方程的推导过程:

二阶贝塞尔曲线上的任意一点,都是能够通过同样比例取得。 在两点之间的任意一点,其实满足的一阶贝塞尔曲线, 一阶贝塞尔曲线满足的其实是线性变动。我给出以下方程

 function oneBezizer(p0,p1,t) {
      return p0 + (p1-p0) * t
  }

从我画的图能够看出,咱们只有 一直求A点 和C点就能够画出在某一时间段的贝塞尔了。

我给出以下代码和效果图:

function drawWithDiscrete2(ctx, start, control, end,percent) {
    const t = percent/ 100;
    // 求出A点
    const A = [];
    const C = [];
    A[0] = oneBezizer(start[0],control[0],t);
    A[1] = oneBezizer(start[1],control[1],t);
    C[0] = twoBezizer(start[0], control[0], end[0], t)
    C[1] = twoBezizer(start[1], control[1], end[1], t)
    ctx.quadraticCurveTo( 
        A[ 0 ], A [ 1 ],
        C[ 0 ], C[ 1 ]
    );
}

礼花🎉动画

上文咱们实现了一条贝塞尔线,咱们将这条贝塞尔的曲线的开始点作为一个圆的圆心,而后依照某个次数求出不同的完结点。 再写一个随机色彩,礼花成果就成了, 间接上代码,

for(let i=0; i<count; i++) {
    const angle = Math.PI * 2 / count * i;
    const x = center[ 0 ] + radius * Math.sin( angle );
    const y = center[ 1 ] + radius * Math.cos( angle );
    ctx.strokeStyle = colors[ i ];
    ctx.beginPath();
    drawWithDiscrete(ctx, center,[180,50],[x,y],percent)
    ctx.stroke();
}

function getRandomColor(colors, count) {
    // 生成随机色彩
    for ( let i = 0; i < count; i++ )  {
        colors.push( 
          'rgb( ' + 
            ( Math.random() * 255 >> 0 ) + ',' +
            ( Math.random() * 255 >> 0 ) + ',' + 
            ( Math.random() * 255 >> 0 ) + 
          ' )'
        );
    }
}

咱们看下动画吧:

结尾

本篇文章到这里就完结了,如果看了对你有帮忙的, 欢送点个赞👍和关注。 你的反对是我继续更新的最大能源。 所有代码都在我的github上。最初祝大家端午节高兴!

更多可视化常识都在我的公众号: 前端图形 欢送关注!!!

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理