“我正在参加「码上挑战赛」概况请看:码上挑战赛来了!”

今日遇到朋友发来的一个ui图,问询我怎么完结下图这样的作用【vue项目】,(听说是相似LED灯的展现作用),所以便有了今日的小demo,要完结一个相似下图的动效,上面的灯展一向重复翻滚,可是这个并不是什么难点,主要在于怎么完结这种滑润的曲线,日常咱们的开发的div在咱们的脑海中一般便是一个网格状,涉及到滑润曲线的往往是图表,所以咱们需求找一个方案来完结这种布局(非实在ui图,是完结之后的作用)

js完结贝塞尔曲线,div也能如此丝滑?

剖析

咱们需求先简略剖析一下这个ui,当咱们拿到这个UI图的时分,脑海中的第一反应是,一个大的DIV中间套了许多的小的DIV,并且小的的上下方位呈现了偏移,可是偏移多少现在咱们不得而知,可是根底的布局方案现已完结。

第二步咱们考虑球体的色彩,能够看到,轨迹是一种色彩,需求一向移动的球体是另一种色彩,这个十分简略,咱们界说两组数据,一组是轨迹,一组是高亮的球,经过不段改动高亮的这组数据,即可响应式的完结灯的移动,第二点咱们也处理了

第三点,初始的时分考虑的是y的坐标是0, 2, 4, 6, 8, 10 , 8, 6, 4 , 2 ….. ,可是很显然,这样的坐标出来的形状一定是一个折线图,而不是滑润的曲线,所以咱们需求用到数学常识了:需求运用到圆的弧度的概念,在javascript中有两个办法**Math.sin()和Math.cos()**都是关于弧度的公式,关于这两个办法,咱们下面再说。

完结

布局

完结这个的布局十分简略,外层一个大的div,内层许多小的span,经过flex一排即可到一排

<template>
  <div class="container">
    <div class="content">
      <span class="circle" v-for="(item,index) in list" :key="index"></span>
    </div>
  </div>
</template>

怎么核算y的偏移量

这一步是咱们比较重要的一步,咱们有一个400px的容器,容器中放置了20个span,现在他们在一排,咱们只需求给他动态绑定款式**transform: translateY(?px)**即可,重要的是咱们怎么核算这个的坐标,咱们先来了解下两个办法的用途:

Math.sin() 和 Math.cos()

Math.sin(x) x 的正玄值。回来值在 -1.0 到 1.0 之间;

Math.cos(x)x 的余弦值。回来的是 -1.0 到 1.0 之间;

能够看到其分别是x点的正弦,这两个函数中的x指的是弧度而不是视点弧度的核算公式是:2/360

这里涉及到数学常识,咱们先看看这张图

js完结贝塞尔曲线,div也能如此丝滑?

咱们看咱们关注的sin和cos

sin(∠A) = 对边比斜边(a / c)
cos(∠A) = 临边比斜边 (b / c)

可大致了解一下即可,当然,咱们今日所需求运用的和这个关系不大,这里仅仅帮咱们回忆一下高中常识(手动狗头)。

好了这里直接推荐一个在线网站,图形核算器能够直接在线调试各种曲线

咱们看看根底的正弦余弦曲线

正弦曲线

js完结贝塞尔曲线,div也能如此丝滑?

余弦曲线

js完结贝塞尔曲线,div也能如此丝滑?
咱们知道圆周率(), 1=1802=360,便是一周,所以咱们只需求截图(0-2)一个周期的曲线即可,后续不管要什么曲线,都在这个上面进行改换即可,经过上面比照,发现正弦曲线的起始点是(0,0),比余弦的(0,1)更好核算,咱们就直接用正弦吧,那么咱们列出已知条件:

  1. 在曲线中 y = cos(x)

  2. 在曲线中,曲线的宽度是2

  3. 在曲线中,曲线的高度最高点到最低点是2

  4. 在咱们的需求中,总宽度是400px

  5. 在咱们需求中, 共有二十个圆圈,所以咱们能够算出每个球的宽度平均是20px,所以坐标便是(index+1)*20

    现在咱们知道了许多信息,咱们就能够核算出更多信息了

核算更多信息

  1. 咱们知道曲线的宽度和咱们的物理实践宽度就能够得出宽度比: 400 / 2

  2. 这个时分咱们需求经过这个比例核算出物理的x坐标对应的曲线中的x坐标,那么 物理宽度/x坐标 = 2/曲线中x坐标

    /* 400 / x = 2 / y, 咱们的x是已知的,等下自己能够拿,这样拿到了曲线中实践的x坐标 */
    const z = 400 x / 400 * Math.PI*2
    
  3. 有个曲线中的对应x坐标,经过公式咱们就能够拿到其曲线中实践y坐标了

    /* 这样就拿到了曲线中的y坐标 */
    y = Math.sin(z)
    
  4. 拿到了曲线中的y坐标,那么们又知道,曲线中的总高是2,经过xy的坐标比照,咱们能够核算出咱们所需的实在的y

    /* 实在宽度400/曲线宽度2 = 实在高度y/曲线中的y 经过比照得到实在的y点 */
    Y = Math.sin(z) * 400 / Math.PI * 2 / 2
    

    然后经过这样的一个核算公式把这个y值赋值给咱们的y点就能够得到这样的曲线

js完结贝塞尔曲线,div也能如此丝滑?

完善剩下

看起来有点意思了,这便是一个完整的2,或者咱们了解为便是曲线的一个周期,可是很明显曲线的度数不对,咱们怎么调整呢,回到刚刚的那个网站之中,咱们要想曲线更加滑润,只需求对sin()除以/x即可,x最大线越平,咱们到刚刚的网站去自己调试到自己抱负的高度,

js完结贝塞尔曲线,div也能如此丝滑?

咱们调试发现除以4就得到了差不多咱们想要的曲线,所以咱们只需求在上面的根底上/4就得到了咱们真正想要的y。

js完结贝塞尔曲线,div也能如此丝滑?
此刻咱们的曲线就现已完结了,所以其实是不是便是咱们的高中数学常识呢

完结跑马灯制作

前面的曲线画完,后边就现已不难了,咱们只需求界说一段高亮的下标数组,咱们写一个办法,创建一个自己想要高亮几个就生成0-x的数组

createActiveIndex(len = 6){
  return Array.from({length:len}, (v,k) => k)
},

然后在给span动态绑定一个背景色彩。当index归于高亮的时分就给高亮的色彩,不是则反之,然后咱们写一个定时器一向修正这个高亮的数组即可,每次让其里边一切元素加1,就能够让他一向跑下去了,当然鸿沟的时分咱们需求对他进行归0

changeIndex(){
   this.activeIndex = this.activeIndex.map( item => item === this.list.length - 1 ? 0 : item + 1)
},

最终咱们发动即可,就完结了咱们最初想要的作用。

js完结贝塞尔曲线,div也能如此丝滑?

至此这个需求算是完结了,这仅仅一个小的场景经过这样的办法咱们能够制作出更多好玩的东西,你能够改动各种参数对齐进行调整修正,看看是不是你想要的作用

贝塞尔曲线

咱们知道,前端的动画经常呈现一个名词贝塞尔曲线,便是动画的履行进程,咱们刚刚的曲线其实便是同理,如果此刻咱们需求去手动书写一个贝塞尔曲线咱们应该怎么做呢,刚刚咱们知道,咱们容器的总宽度是400,曲线的周长是2,比例便是400/2,同理,当咱们换算成时间的时分,假设动画是1秒。那么咱们需求60帧,一帧动画的时间便是1000/60=16.7ms,咱们经过2/60就知道咱们每一帧动画在什么方位了,当咱们手写贝塞尔曲线的时分,利用差不多的公式一样能够完结。

简略封装一下办法

看起来好像很复杂,可是实践上咱们所需求的其实仅仅利用实在的x点,拿到对应曲线求出咱们y的坐标,所以咱们需求的参数有,咱们实在场景的总宽,总宽之中的个数,咱们所需求的曲线的倍率,三个参数即可,咱们尽量分隔过程写,这样你看会了解的更清楚

js中便是Math.PI

function getCoordinate(width, count,  mag = 1){
      /* 经过总宽和个数核算出一个单个的宽 */
      const singleWidth = width / count
      /* 经过物理宽度/曲线周长核算出比率 */
      const ratio = 400 / Math.PI*2
      /* 上面实例代码咱们是动态一次核算一个,而现在是办法,咱们应该一次去拿到一切,所以咱们回来一个数组目标记录xy */
      let result = new Array(count).fill({})
      /* 遍历总长度的dom个数,在数组中填充宽高 */
      result = result.map( (item,index) => {
          /* x的坐标 */
          const x = (index + 1) * singleWidth
          /* 界说变量z核算曲线中x的坐标 */
          const z = x / width * Math.PI*2
          /* 核算出实在的y的坐标 */
          let y = Math.sin(z) / 4  * 400 / Math.PI * 2 / 2    
          /* y还需求经过倍率改动曲线,得到最终咱们想要的y */
          y = y / mag
          /* 写入数组目标中 */
          return {x, y}
      })
      return result;
    } 

如果后期你也有这种需求,能够在上面的根底上进行修正,根底的算法现已写出来了,如果您有疑问,欢迎评论

在线体会

最终版别在此体会,