花里胡哨的Canvas动画作用

我正在参加「启航方案」

作者的话

最近五一放假,原本方案在家好好科研的,可是的确不是科研的料,就不想科研,然后为了打发时刻做了点前端小Demo,然后看见有码上的竞赛,就选了赛题一方案着手做一个美观的动画作用,花了半天的时刻完结了 。

五一总结,假日的确高产,可能也是由于找到暑期实习了吧,然后也想着放松放松,假日回去后就要开始好好把科研推进一下了,然后就趁假日写了三篇博客(由于找实习找了一个多月,良久没写博客了 ):

  • 「启航方案」仿网易云主页获取图片的主题色
  • 「启航方案」花了一天完结的简略又风趣的马赛克小工具
  • 2024届前端暑期实习总结

作用展示

「掘金启航计划」参加码上掘金编程比赛中实现花里胡哨的Canvas散点动画效果

假如我们看了作用比较喜爱,那作者还有个不情之请,假如喜爱能不能帮助给我的著作点点赞或许点点保藏或许阅读一下,下面附上链接:码上,在玩转动画板块中哦

「掘金启航计划」参加码上掘金编程比赛中实现花里胡哨的Canvas散点动画效果

功用介绍

  1. 【功用1】:本动画主要运用Canvas完结,动画作用为粒子在页面上首要呈现为乱序的作用,接着在相一起刻内,一切的粒子都运动到指定的方位上,终究拼成方针文字,假如有多行文字,会在指定时刻间隔下相继展示出来。

  2. 【功用2】:本动画作用能够自适应横屏和竖屏,也便是在PC端阅读该作用文字是横向摆放,假如是在移动端阅读该作用文字是纵向摆放。

  3. 【功用3】:自适应文字大小,假如设置的文字太大超出屏幕,该作用会自动调整字体至最大占满页面的大小。

完结思路

这儿大致说一下完结的思路吧,由于具体说明比较复杂,就简略介绍一下:

  1. 正如我们看到的作用相同(能够看封面动图,或许在代码中运转都可),这个动画由许多的点运动组成,因而需求界说一个点类,来操控每一步点的运动轨迹;

  2. 接着便是如何确认每个点的终究方位,那就需求先把文字写在自己的(不是终究显现的Canvas,自己能够额外创建一个)Canvas上,然后获取当时Canvas的像素信息,判别是否不为透明的像素,然后记载该像素的坐标和色彩

  3. 然后操控每个点由随机方位在相一起刻内一起运动到指定的方位上,其实也便是把每一帧的图画出来,鄙人一帧的时候清空画布,再把当时帧的方位画出来,这样就能有一个动画的形式,那么操控画图的函数能够运用setInterval或许requestAnimationFrame,这个看自己的挑选吧

总结一下:其实很简略,便是获得方针文字在Canvas上的坐标和色彩,然后把每个点运动到正确的方位上即可。

界说每个点的类

这个类主要操控点的制作和更新功用,由于每个点需求在每一帧更新当时的坐标,然后制作出来。

由于之前也说过要制作每个点点方位,因而需求知道如下几个参数:

  1. 开始坐标的X和Y
  2. 完毕坐标的X和Y
  3. 每个点在X和Y方向上的移动速度
  4. 每个点的色彩和半径

下面展示一下具体的类的结构:

class Dot {
  constructor(config) {
    const { x, y, duration, color, radius } = config
    // ...
    // 点的色彩
    this.color = color;
    // 点的半径
    this.radius = radius;
    // 点的X开始方位
    this.startX = Math.random() * width;
    // 点的Y开始方位
    this.startY = Math.random() * height;
    // 点的X结尾方位
    this.endX = x;
    // 点的Y结尾方位
    this.endY = y;
    // 每帧动画中点的X方向的速度
    this.speedX = (this.endX - this.startX) / duration / 60;
    // 每帧动画中点的Y方向的速度
    this.speedY = (this.endY - this.startY) / duration / 60;
  }
  // 画出点的方位
  draw() {
    // ...
  }
  // 更新点的方位
  updated(flag) {
   // ...
  }
}

Draw函数比较简略,便是简略的Canvas画图操作,这儿不再细说,具体代码能够在代码片段里检查;然后便是update函数,其实只要每次将当时点的X和Y坐标增加在该方向上的移动速度即可,这儿需求留意的是由于计算机存储的原因,可能不能彻底增加到彻底持平,因而需求进行判别,作者这儿就进行了简略的判别:

if (Math.abs(end - start) < Number.EPSILON) {
  // ...
}

这儿计算终究方位end与当时方位start的差值,假如小于一个比较小的数字(Number.EPSILON),就认为抵达终究方位了。

科普时刻

这儿插一句,介绍一下Number.EPSILON ,它是 JavaScript 中一个常量,表示浮点数的计算精度。它的值是 2 的负 52 次方,即 2^-52,约等于 0.0000000000000002220446049250313。

在进行浮点数比较时,能够运用 Number.EPSILON 这个极小值来判别两个数是否持平。例如:

function isEqual(a, b) {
  return Math.abs(a - b) < Number.EPSILON;
}
console.log(isEqual(0.1 + 0.2, 0.3)); // true

这儿运用 Math.abs() 函数来获取两个数之间的绝对值,然后与 Number.EPSILON 进行比较,假如小于 Number.EPSILON 就认为两个数持平。这是由于在进行浮点数计算时,由于计算精度的限制,可能会出现一些微小的差错,运用 Number.EPSILON 能够消除这种差错的影响。

画图的类

这个类说起来比较费事,这儿就介绍一下大约的履行流程吧,假如想要源码,能够去看代码片段

履行流程:

flowchart TD
init -->|初始化画布| F
G -->|改动|H
F --> |"有使命 [1]"|M
F --> L
D --> |完结当时使命|N(pop)
    subgraph init
        H(初始化画布参数)-->I(设置文本渐变)-->J(设置font size)-->K[是否超过画布]
        K --> |"是 [2]"|J
        K -->|否|L(Canvas制作文本)
    end
    subgraph run
        F[isRunning] --> |无使命|G[文本是否改动]--> |不改动|B(getTextCoordinates) -->|坐标和色彩| C(draw) --> |画出每个点|D(move)
    end
    subgraph taskQueue
        M(push)-->|压入使命|E("task=[]")
        N-->|弹出使命|G
    end

[1] 这儿运用的是队列的思想,一起也学习了Vue2的异步更新战略

[2] 这儿是调整字体的font size直到最大可能的占满页面,搜索时运用的是二分查找的思想,使得时刻复杂度降至 log2Nlog_2N

自适应屏幕

也便是完结了功用2,能够自适应横屏和竖屏,其实判别横竖屏不难,这个功用比较难的地方是怎么把文字竖着显现,然后获取到转化后的坐标。其实有想过运用ctx.rotate(90deg)然后再把转化后的坐标的横坐标X再加上Canvas的height就能够得到竖屏时的坐标。

假如我们有什么更好的主意能够评论区留言,让我学习一下

因而,作者想直接运用公式完结rotate(-90deg)translateX(canvas.height)这两个过程,然后想起了CSS中的matrix,因而那就用线性代数的矩阵变换来完结。

现在回想一下学过的线性代数,rotate(-90deg)的变换矩阵如下:

(0−10100001)(xy1)=(−yx1)\begin{pmatrix} 0 & -1 & 0 \\ 1 & 0 & 0 \\ 0 & 0 & 1 \\ \end{pmatrix} \begin{pmatrix} x \\ y \\ 1 \\ \end{pmatrix} = \begin{pmatrix} -y \\ x \\ 1 \\ \end{pmatrix}

其间xxyy是每个点的横纵坐标,同理能够得到rotate(-90deg) translateX(canvas.height)的变换矩阵为:

(0−1width100001)\begin{pmatrix} 0 & -1 & width \\ 1 & 0 & 0 \\ 0 & 0 & 1 \\ \end{pmatrix}

width为竖屏时的页面宽度

公式推导

假如不想看能够跳过

transform: rotate(-90deg)的推导

至于为什么rotate(-90deg)的变换矩阵为:

(0−10100001)\begin{pmatrix} 0 & -1 & 0 \\ 1 & 0 & 0 \\ 0 & 0 & 1 \\ \end{pmatrix}

下面进行一下数学公式的简略推导,如下是二维的笛卡尔坐标系,在榜首象限中有原来的点(x,y)(x,y),在第二象限有逆时针旋转变换后的坐标(x′,y′)(x’,y’),在榜首象限与y的正方向的夹角为\alpha,在第二象限与y的正方向的夹角为\beta,且满足+=\alpha+\beta=\theta,具体参数如下图所示:

「掘金启航计划」参加码上掘金编程比赛中实现花里胡哨的Canvas散点动画效果

那么依据简略的三角函数性质能够得到如下等式:

{l⋅sin=xl⋅cos=yl⋅sin=x′l⋅cos=y′\left\{ \begin{aligned} & l \cdot sin\alpha=x \\ & l \cdot cos\alpha=y \\ & l \cdot sin\beta=x’ \\ & l \cdot cos\beta=y’ \end{aligned} \right.

依据+=\alpha+\beta=\theta,能够得到如下等式:

{l⋅sin(−)=−x′l⋅cos(−)=y′\left\{ \begin{aligned} & l \cdot sin(\theta-\alpha)=-x’ \\ & l \cdot cos(\theta-\alpha)=y’ \end{aligned} \right.

利用三角函数公式将上述等式展开,能够得到:

{l⋅(sincos−cossin)=−x′l⋅(coscos+sinsin)=y′\left\{ \begin{aligned} & l \cdot (sin\theta cos\alpha-cos\theta sin\alpha)=-x’ \\ & l \cdot (cos\theta cos\alpha+sin\theta sin\alpha)=y’ \end{aligned} \right.

将上述公式进行化简能够得到:

{−y⋅sin+x⋅cos=x′y⋅cos+x⋅sin=y′\left\{ \begin{aligned} & -y \cdot sin\theta +x \cdot cos\theta=x’ \\ & y \cdot cos\theta +x\cdot sin\theta=y’ \end{aligned} \right.

那么依据上述的公式,能够得到逆时针旋转\theta角度的矩阵计算公式:

(cos−sin0sincos0001)(xy1)=(x′y′1)\begin{pmatrix} cos\theta & -sin\theta & 0 \\ sin\theta & cos\theta & 0 \\ 0 & 0 & 1 \\ \end{pmatrix} \begin{pmatrix} x \\ y \\ 1 \\ \end{pmatrix} = \begin{pmatrix} x’ \\ y’ \\ 1 \\ \end{pmatrix}

因而,rotate(-90deg)的变换矩阵能够把=90\theta=90\degree 代入能够得到:

(−cos(90)sin(90)0sin(90)cos(90)0001)=(0−10100001)\begin{pmatrix} -cos(90\degree) & sin(90\degree) & 0 \\ sin(90\degree) & cos(90\degree) & 0 \\ 0 & 0 & 1 \\ \end{pmatrix} = \begin{pmatrix} 0 & -1 & 0 \\ 1 & 0 & 0 \\ 0 & 0 & 1 \\ \end{pmatrix}

这儿为什么把9090\degree带入而不是把−90-90\degree代入,这是由于推导的时候现已默许\theta是正值,然后公式现已默许推导的是逆时针旋转的公式,因而只需求代入9090\degree即可。

transform: translateX(canvas.height)的推导

同理,能够得到translateX(canvas.height)的变换矩阵如下:

(10width010001)\begin{pmatrix} 1 & 0 & width \\ 0 & 1 & 0 \\ 0 & 0 & 1 \\ \end{pmatrix}

这儿变成width是由于先在横屏中写文字,因而横屏Canvas的height就等于变换后竖屏的width。

transform: rotate(-90deg) translateX(canvas.height)的推导

依据上面的推导我们现已得到了rotate(-90deg)translateX(canvas.height)的变换矩阵,因而transform: rotate(-90deg) translateX(canvas.height)就等于rotate(-90deg)的变换矩阵乘translateX(canvas.height)的变换矩阵,那么:

(10width010001)(0−10100001)=(0−1width100001)\begin{pmatrix} 1 & 0 & width \\ 0 & 1 & 0 \\ 0 & 0 & 1 \\ \end{pmatrix} \begin{pmatrix} 0 & -1 & 0 \\ 1 & 0 & 0 \\ 0 & 0 & 1 \\ \end{pmatrix} = \begin{pmatrix} 0 & -1 & width \\ 1 & 0 & 0 \\ 0 & 0 & 1 \\ \end{pmatrix}

写在最终

我们有什么问题或许主张能够评论区讨论一下,假如喜爱的话也能够点赞➕保藏 ,感谢我们的支撑。

最终,再求一波点赞或许保藏,给作者的【花里胡哨的散点动画】点个小星星 吧!