欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

js基于div絲滑實(shí)現(xiàn)貝塞爾曲線

 更新時(shí)間:2022年09月21日 09:37:46   作者:_小九  
這篇文章主要為大家介紹了js基于div絲滑實(shí)現(xiàn)貝塞爾曲線示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

引言

今天遇到朋友發(fā)來的一個(gè)ui圖,詢問我如何實(shí)現(xiàn)下圖這樣的效果【vue項(xiàng)目】,(聽說是類似LED燈的展示效果),于是便有了今天的小demo,要實(shí)現(xiàn)一個(gè)類似下圖的動(dòng)效,上面的燈會(huì)一直重復(fù)滾動(dòng),但是這個(gè)并不是什么難點(diǎn),主要在于如何實(shí)現(xiàn)這種平滑的曲線,日常我們的開發(fā)的div在我們的腦海中通常就是一個(gè)網(wǎng)格狀,涉及到平滑曲線的往往是圖表,于是我們需要找一個(gè)方案來完成這種布局(非真實(shí)ui圖,是完成之后的效果)

分析

我們需要先簡(jiǎn)單分析一下這個(gè)ui,當(dāng)我們拿到這個(gè)UI圖的時(shí)候,腦海中的第一反應(yīng)是,一個(gè)大的DIV中間套了很多的小的DIV,并且小的的上下位置出現(xiàn)了偏移,但是偏移多少目前我們不得而知,但是基礎(chǔ)的布局方案已經(jīng)完成。

第二步我們考慮球體的顏色,可以看到,軌道是一種顏色,需要一直移動(dòng)的球體是另一種顏色,這個(gè)非常簡(jiǎn)單,我們定義兩組數(shù)據(jù),一組是軌道,一組是高亮的球,通過不段改變高亮的這組數(shù)據(jù),即可響應(yīng)式的完成燈的移動(dòng),第二點(diǎn)我們也解決了

第三點(diǎn),初始的時(shí)候考慮的是y的坐標(biāo)是0, 2, 4, 6, 8, 10 , 8, 6, 4 , 2 ..... ,但是很顯然,這樣的坐標(biāo)出來的形狀一定是一個(gè)折線圖,而不是平滑的曲線,于是我們需要用到數(shù)學(xué)知識(shí)了:需要使用到圓的弧度的概念,在javascript中有兩個(gè)方法**Math.sin()和Math.cos()**都是關(guān)于弧度的公式,關(guān)于這兩個(gè)方法,我們下面再說。

實(shí)現(xiàn)

布局

實(shí)現(xiàn)這個(gè)的布局非常簡(jiǎn)單,外層一個(gè)大的div,內(nèi)層很多小的span,通過flex一排即可到一排

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

如何計(jì)算y的偏移量

這一步是我們比較重要的一步,我們有一個(gè)400px的容器,容器中放置了20個(gè)span,現(xiàn)在他們?cè)谝慌牛覀冎恍枰o他動(dòng)態(tài)綁定樣式**transform: translateY(?px)**即可,重要的是我們?nèi)绾斡?jì)算這個(gè)的坐標(biāo),我們先來了解下兩個(gè)方法的用處:

Math.sin() 和 Math.cos()

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

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

可以看到其分別是x點(diǎn)的正弦,這兩個(gè)函數(shù)中的x指的是弧度而不是角度弧度的計(jì)算公式是:2π/360°

這里涉及到數(shù)學(xué)知識(shí),我們先看看這張圖

我們看我們關(guān)注的sin和cos

sin(∠A) = 對(duì)邊比斜邊(a / c)
cos(∠A) = 臨邊比斜邊 (b / c)

可大致了解一下即可,當(dāng)然,我們今天所需要使用的和這個(gè)關(guān)系不大,這里只是幫大家回顧一下高中知識(shí)(手動(dòng)狗頭)。

好了這里直接推薦一個(gè)在線網(wǎng)站,圖形計(jì)算器可以直接在線調(diào)試各種曲線

我們看看基礎(chǔ)的正弦余弦曲線

正弦曲線

余弦曲線

我們知道圓周率(π), 1π=180°,2π=360°,就是一周,所以我們只需要截圖(0-2π)一個(gè)周期的曲線即可,后續(xù)不管要什么曲線,都在這個(gè)上面進(jìn)行變換即可,通過上面對(duì)比,發(fā)現(xiàn)正弦曲線的起始點(diǎn)是(0,0),比余弦的(0,1)更好計(jì)算,我們就直接用正弦吧,那么我們列出已知條件:

  • 在曲線中 y = cos(x)
  • 在曲線中,曲線的寬度是
  • 在曲線中,曲線的高度最高點(diǎn)到最低點(diǎn)是2
  • 在我們的需求中,總寬度是400px
  • 在我們需求中, 共有二十個(gè)圓圈,所以我們可以算出每個(gè)球的寬度平均是20px,所以坐標(biāo)就是(index+1)*20
  • 現(xiàn)在我們知道了很多信息,我們就可以計(jì)算出更多信息了

計(jì)算更多信息

我們知道曲線的寬度和我們的物理實(shí)際寬度就可以得出寬度比: 400 / 2π

這個(gè)時(shí)候我們需要通過這個(gè)比例計(jì)算出物理的x坐標(biāo)對(duì)應(yīng)的曲線中的x坐標(biāo),那么 物理寬度/x坐標(biāo) = 2π/曲線中x坐標(biāo)

/* 400 / x = 2π / y, 我們的x是已知的,等下自己可以拿,這樣拿到了曲線中實(shí)際的x坐標(biāo) */
const z = 400 x / 400 * Math.PI*2

有個(gè)曲線中的對(duì)應(yīng)x坐標(biāo),通過公式我們就可以拿到其曲線中實(shí)際y坐標(biāo)了

/* 這樣就拿到了曲線中的y坐標(biāo) */
y = Math.sin(z)

拿到了曲線中的y坐標(biāo),那么們又知道,曲線中的總高是2,通過xy的坐標(biāo)對(duì)比,我們可以計(jì)算出我們所需的真實(shí)的y

/* 真實(shí)寬度400/曲線寬度2π = 真實(shí)高度y/曲線中的y 通過對(duì)比得到真實(shí)的y點(diǎn) */
Y = Math.sin(z) * 400 / Math.PI * 2 / 2

然后通過這樣的一個(gè)計(jì)算公式把這個(gè)y值賦值給我們的y點(diǎn)就可以得到這樣的曲線

完善剩余

看起來有點(diǎn)意思了,這就是一個(gè)完整的2π,或者我們理解為就是曲線的一個(gè)周期,但是很明顯曲線的度數(shù)不對(duì),我們?nèi)绾握{(diào)整呢,回到剛剛的那個(gè)網(wǎng)站之中,我們要想曲線更加平滑,只需要對(duì)sin()除以/x即可,x最大線越平,我們到剛剛的網(wǎng)站去自己調(diào)試到自己理想的高度,

我們調(diào)試發(fā)現(xiàn)除以4就得到了差不多我們想要的曲線,所以我們只需要在上面的基礎(chǔ)上/4就得到了我們真正想要的y。

此時(shí)我們的曲線就已經(jīng)完成了,所以其實(shí)是不是就是我們的高中數(shù)學(xué)知識(shí)呢

完成跑馬燈制作

前面的曲線畫完,后面就已經(jīng)不難了,我們只需要定義一段高亮的下標(biāo)數(shù)組,我們寫一個(gè)方法,創(chuàng)建一個(gè)自己想要高亮幾個(gè)就生成0-x的數(shù)組

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

然后在給span動(dòng)態(tài)綁定一個(gè)背景顏色。當(dāng)index屬于高亮的時(shí)候就給高亮的顏色,不是則反之,然后我們寫一個(gè)定時(shí)器一直修改這個(gè)高亮的數(shù)組即可,每次讓其里面所有元素加1,就可以讓他一直跑下去了,當(dāng)然邊界的時(shí)候我們需要對(duì)他進(jìn)行歸0

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

最后我們啟動(dòng)即可,就實(shí)現(xiàn)了我們開頭想要的效果。

至此這個(gè)需求算是完成了,這只是一個(gè)小的場(chǎng)景通過這樣的方式我們可以繪制出更多好玩的東西,你可以改變各種參數(shù)對(duì)齊進(jìn)行調(diào)整修改,看看是不是你想要的效果

貝塞爾曲線

我們知道,前端的動(dòng)畫經(jīng)常出現(xiàn)一個(gè)名詞貝塞爾曲線,就是動(dòng)畫的執(zhí)行過程,我們剛剛的曲線其實(shí)就是同理,如果此時(shí)我們需要去手動(dòng)書寫一個(gè)貝塞爾曲線我們應(yīng)該怎么做呢,剛剛我們知道,我們?nèi)萜鞯目倢挾仁?00,曲線的周長(zhǎng)是2π,比例就是400/2π,同理,當(dāng)我們換算成時(shí)間的時(shí)候,假如動(dòng)畫是1秒。

那么我們需要60幀,一幀動(dòng)畫的時(shí)間就是1000/60=16.7ms,我們通過2π/60就知道我們每一幀動(dòng)畫在什么位置了,當(dāng)我們手寫貝塞爾曲線的時(shí)候,利用差不多的公式一樣可以完成。

簡(jiǎn)單封裝一下方法

看起來似乎很復(fù)雜,但是實(shí)際上我們所需要的其實(shí)只是利用真實(shí)的x點(diǎn),拿到對(duì)應(yīng)曲線求出我們y的坐標(biāo),所以我們需要的參數(shù)有,我們真實(shí)場(chǎng)景的總寬,總寬之中的個(gè)數(shù),我們所需要的曲線的倍率,三個(gè)參數(shù)即可,我們盡量分開步驟寫,這樣你看會(huì)理解的更清楚

js中π就是Math.PI

function getCoordinate(width, count,  mag = 1){
      /* 通過總寬和個(gè)數(shù)計(jì)算出一個(gè)單個(gè)的寬 */
      const singleWidth = width / count
      /* 通過物理寬度/曲線周長(zhǎng)計(jì)算出比率 */
      const ratio = 400 / Math.PI*2
      /* 上面實(shí)例代碼我們是動(dòng)態(tài)一次計(jì)算一個(gè),而現(xiàn)在是方法,我們應(yīng)該一次去拿到所有,所以我們返回一個(gè)數(shù)組對(duì)象記錄xy */
      let result = new Array(count).fill({})
      /* 遍歷總長(zhǎng)度的dom個(gè)數(shù),在數(shù)組中填充寬高 */
      result = result.map( (item,index) => {
          /* x的坐標(biāo) */
          const x = (index + 1) * singleWidth
          /* 定義變量z計(jì)算曲線中x的坐標(biāo) */
          const z = x / width * Math.PI*2
          /* 計(jì)算出真實(shí)的y的坐標(biāo) */
          let y = Math.sin(z) / 4  * 400 / Math.PI * 2 / 2    
          /* y還需要通過倍率改變曲線,得到最終我們想要的y */
          y = y / mag
          /* 寫入數(shù)組對(duì)象中 */
          return {x, y}
      })
      return result;
    } 

完整示例

style

.container {
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  height: 100vh;
  background-color: #000;
}
.content{
  display: flex;
  justify-content: space-between;
  align-items: center;
  width: 400px;
  height: 50px;
}
.container .circle{
  width: 15px;
  height: 15px;
  border-radius: 50%;
  background-color: #befbf7;
}

SCript

<template>
  <div class="container">
    <div class="content">
      <span class="circle" v-for="(item,index) in list" :key="index" :style="{transform: `translateY(${calcY(index)}px)`,backgroundColor: getCurrentBgColor(index)}"></span>
    </div>
     <div class="content" style="margin-top: 50px">
      <span class="circle" v-for="(item,index) in list" :key="index" :style="{transform: `translateY(${calcY(index)}px)`,backgroundColor: getRandomBgColor(index)}"></span>
    </div>
  </div>
</template>
<script>
export default {
  data() {
    return {
      list: [], //定義總長(zhǎng)度
      activeIndex: [], // 運(yùn)動(dòng)中的球的顏色
      interval: 300, //運(yùn)動(dòng)速度
      colors: { // 定義軌道顏色和高亮顏色
        active: '#2b88ff',
        basic: '#b6f3f7' 
      },
      cache: []
    };
  },
  methods: {
    init() {
      this.list = new Array(20).fill(0)
      this.start()
    },
    getCurrentBgColor(index){
      return this.activeIndex.includes(index) ? '#6e9cae' : this.getRandomColor()
    },
    getRandomBgColor(index){
      const color = this.activeIndex.includes(index) ? 'active' : 'basic'
      return this.colors[color]
    },
    start(){
      this.activeIndex = this.createActiveIndex()
      setInterval(() => this.changeIndex(), this.interval)
    },
    changeIndex(){
      this.activeIndex = this.activeIndex.map( item => item === this.list.length - 1 ? 0 : item + 1)
    },
    /* 生成需要?jiǎng)拥那虻膫€(gè)數(shù) */
    createActiveIndex(len = 6){
      return Array.from({length:len}, (v,k) => k)
    },
    getRandomColor(){
        return `#${Math.floor(Math.random() * 0xffffff) .toString(16)}`;
    },
    getCoordinate(width, count,  mag = 1){
      /* 通過總寬和個(gè)數(shù)計(jì)算出一個(gè)單個(gè)的寬 */
      const singleWidth = width / count
      /* 通過物理寬度/曲線周長(zhǎng)計(jì)算出比率 */
      const ratio = 400 / Math.PI*2
      /* 上面實(shí)例代碼我們是動(dòng)態(tài)一次計(jì)算一個(gè),而現(xiàn)在是方法,我們應(yīng)該一次去拿到所有,所以我們返回一個(gè)數(shù)組對(duì)象記錄xy */
      let result = new Array(count).fill({})
      /* 遍歷總長(zhǎng)度的dom個(gè)數(shù),在數(shù)組中填充寬高 */
      result = result.map( (item,index) => {
          /* x的坐標(biāo) */
          const x = (index + 1) * singleWidth
          /* 定義變量z計(jì)算曲線中x的坐標(biāo) */
          const z = x / width * Math.PI*2
          /* 計(jì)算出真實(shí)的y的坐標(biāo) */
          let y = Math.sin(z) / 4  * 400 / Math.PI * 2 / 2    
          /* y還需要通過倍率改變曲線,得到最終我們想要的y */
          y = y / mag
          /* 寫入數(shù)組對(duì)象中 */
          return {x, y}
      })
      return result;
    } 
  },
  created(){
    this.cache = this.getCoordinate(400, 20, 1)
    this.init()
  },
  computed:{
    calcY(){
      return (index) => {
        /* 使用封裝的方法計(jì)算 */
        // return this.cache[index].y
        const x = (index + 1) * 20
        const z = x / 400 * Math.PI*2
        const y = Math.sin(z) * 400 / Math.PI * 2 / 2 / 4
        return y
      }
    }
  }
};
</script>

以上就是js基于div絲滑實(shí)現(xiàn)貝塞爾曲線的詳細(xì)內(nèi)容,更多關(guān)于js div實(shí)現(xiàn)貝塞爾曲線的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評(píng)論