写点什么

面试官问我会 canvas? 我可以绘制一个烟花🎇动画

用户头像
阿飞
关注
发布于: 1 小时前
面试官问我会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 = 0function 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上。最后祝大家快乐!

喜欢数据可视化的, 可以关注我的公众号 【前端图形】。

发布于: 1 小时前阅读数: 2
用户头像

阿飞

关注

公众号 [前端图形] 2020.04.05 加入

喜欢数据可视化的前端工程师

评论

发布
暂无评论
面试官问我会canvas? 我可以绘制一个烟花🎇动画