?? 前言
- 事情是這樣的,前段時間拿到公司的資料大屏原型圖讓我一天內把一整個頁面做出來,
- 簡單看了看,就是一個
3840*1840的大屏然后幾個串列幾個圖例看起來也沒有多復雜, - 唰!很快啊加了一會班把整個頁面調整好了信心十足拿給產品經理看,
- 產品經理皺了皺眉頭:你這詞云不會動啊??
🌤? 之前的效果
- 聽到這話我發現情況不對,我尋思著這原型圖的詞云也看不出他要沒要求我動啊,而且明明我做的是會動的呀!
🎢 關系圖
-
一開始我用的是
echarts的graph關系圖,這種圖的特點是一開始會因為每個詞的斥力會互相分開,在一開始會有一些動態效果,但是因為力引導布局會在多次迭代后才會穩定,所以到后面就不會繼續運動了,

-
我:是吧我沒騙人吧?確實是會動的,
-
產品經理:這樣效果不好,沒有科技感,而且我要字體大小每個都不同的,明天要拿給客戶看一版,比較急,算了你別做動的了就讓他詞云填滿然后每個詞的大小要不一樣,

🎠 詞云圖
-
做不動詞云的那不就簡單了,直接使用
echarts的wordCloud圖啊,直接唰唰配置一下就好了,

-
產品經理:客戶看完了,整體還不錯,但是詞云這塊我還是想它動起來,這樣吧,你想個辦法整整,

🚄 自己手寫
- 對于這個詞云,我一開始真的是死腦筋了,認定要用
echarts來做,但實際上wordCloud官網也沒有提供資料了,好像確實也沒有辦法讓它動起來, - 思量片刻…等會,詞云要不同大小不同顏色然后要在區域內隨機移動,既然我不熟
canvas,那我是不是可以用js和css來寫一個2d的呢,說白了就是一個詞語在一個容器內隨機運動然后每個詞語都動起來撒,好像能行…開干,
🚅 ToDoList
- 準備容器和需要的配置項
- 生成所有靜態詞云
- 讓詞云動起來
🚈 Just Do It
- 由于我這邊的技術堆疊是
vue 2.x的所以接下來會用vue 2.x的語法來分享,但實際上換成原生js也沒有什么難度,相信大家可以接受的,
🚎 準備容器和需要的配置項
- 首先建立一個容器來包裹我們要裝的詞云,我們接下來的所有操作都圍繞這個容器進行,
<template>
<div class="wordCloud" ref="wordCloud">
</div>
</template>

- 因為我們的詞云需要有不同的顏色我們需要實作準備一個詞語串列和顏色串列,再準備一個空陣列來存盤之后生成的詞語,
...
data () {
return {
hotWord: ['萬事如意', '事事如意 ', '萬事亨通', '一帆風順', '萬事大吉', '吉祥如意', '步步高升', '步步登高', '三羊開泰', '得心應手', '財源廣進', '陶未媲美', '闔家安康', '龍馬精神'],
color: [
'#a18cd1', '#fad0c4', '#ff8177',
'#fecfef', '#fda085', '#f5576c',
'#330867', '#30cfd0', '#38f9d7'
],
wordArr: []
};
}
...
- 準備的這些詞語都是想對現在在讀文章的你說的~如果覺得我說得對的不妨讀完文章后給一個
贊~ - 好了不開玩笑,現在準備作業完成了,開始生成我們的詞云,
🚒 生成所有靜態詞云
- 我們如果想讓一個容器里面充滿詞語,按照正常我們切圖的邏輯來說,每個詞語占一個
span,那么就相當于一個div里面有n(hotWord數量)個詞語,也就是容器里面有對應數量的span標簽即可, - 如果需要不同的顏色和大小,再分別對
span標簽分別加不同樣式即可,
...
mounted () {
this.init();
},
methods: {
init () {
this.dealSpan();
},
dealSpan () {
const wordArr = [];
this.hotWord.forEach((value) => {
// 根據詞云數量生成span數量設定字體顏色和大小
const spanDom = document.createElement('span');
spanDom.style.position = 'relative';
spanDom.style.display = "inline-block";
spanDom.style.color = this.randomColor();
spanDom.style.fontSize = this.randomNumber(15, 25) + 'px';
spanDom.innerHTML = value;
this.$refs.wordCloud.appendChild(spanDom);
wordArr.push(spanDom);
});
this.wordArr = wordArr;
},
randomColor () {
// 獲取隨機顏色
var colorIndex = Math.floor(this.color.length * Math.random());
return this.color[colorIndex];
},
randomNumber (lowerInteger, upperInteger) {
// 獲得一個包含最小值和最大值之間的亂數,
const choices = upperInteger - lowerInteger + 1;
return Math.floor(Math.random() * choices + lowerInteger);
}
}
...
- 我們對
hotWord熱詞串列進行遍歷,每當有一個詞語就生成一個span標簽,分別使用randomColor()和randomSize()設定不同的隨機顏色和大小, - 最后再將這些
span都依次加入div容器中,那么完成后是這樣的,

🚓 讓詞云動起來
- 詞語是添加完了,接下來我們需要讓他們動起來,那么該怎么動呢,我們自然而然會想到
transform的translateX和translateY屬性,我們首先要讓一個詞語先動起來,接下來所有的都應用這種方式就可以了,
先動一下x軸
- 怎么動呢?我們現在要做的是一件無限回圈的事情,就是一個元素無限的移動,既然是無限,在
js中用定時器可不可以實作呢?確實是可以的,但是會巨卡,萬一詞語一多你的電腦會爆炸,在另一方面撰寫影片回圈的關鍵是要知道延遲時間多長合適,如果太長或者太短都不合適所以不用定時器, - 然后一不小心發現了
window.requestAnimationFrame這個API,requestAnimationFrame不需要設定時間間隔,
requestAnimationFrame 會把每一幀中的所有DOM操作集中起來,在一次重繪或回流中就完成,并且重繪或回流的時間間隔緊緊跟隨瀏覽器的重繪頻率,一般來說,這個頻率為每秒60幀,
- 也就是說當我們回圈無限的讓一個元素在
x軸或者y軸移動,假設每秒向右移動10px那么它的translateX就是累加10px,每個元素都是如此那么我們需要給span元素新增一個屬性來代表它的位置,
data () {
return {
...
timer: null,
resetTime: 0
...
};
}
methods: {
init () {
this.dealSpan();
this.render();
},
dealSpan () {
const wordArr = [];
this.hotWord.forEach((value) => {
...
spanDom.local = {
position: {
x: 0,
y: 0
}
};
...
});
this.wordArr = wordArr;
},
render () {
if (this.resetTime < 100) {
//防止“堆疊溢位”
this.resetTime = this.resetTime + 1;
this.timer = requestAnimationFrame(this.render.bind(this));
this.resetTime = 0;
}
this.wordFly();
},
wordFly () {
this.wordArr.forEach((value) => {
//每次回圈加1
value.local.position.x += 1;
// 給每個詞云加影片過渡
value.style.transform = 'translateX(' + value.local.position.x + 'px)';
});
},
},
destroyed () {
// 組件銷毀,關閉定時執行
cancelAnimationFrame(this.timer);
},
- 這時候我們給每個元素加了個
local屬性里面有它的初始位置,每當我們執行一次requestAnimationFrame的時候它的初始位置都+1,再把這個值給到translateX這樣我們每次回圈都相當于移動了1px,現在我們來看看效果,

調整范圍
- 嘿!好家伙,動是動起來了,但是怎么還過頭了呢?
- 我們發現每次
translateX都+1了但是沒有給一個停止的范圍給他,所以我們需要給一個讓他到容器的邊緣就開始掉頭的步驟, - 那怎么樣讓他掉頭呢?既然我們可以讓他每次往右移動
1px那么我們是不是可以檢測到當它的x軸位置大于這個容器的位置時x軸位置小于這個容器的位置時并且換個方向就好換個方向我們只需要用正負數來判斷即可,
init () {
this.dealSpan();
this.initWordPos();
this.render();
},
dealSpan () {
const wordArr = [];
this.hotWord.forEach((value) => {
...
spanDom.local = {
position: {
// 位置
x: 0,
y: 0
},
direction: {
// 方向 正數往右 負數往左
x: 1,
y: 1
}
};
...
});
this.wordArr = wordArr;
},
wordFly () {
this.wordArr.forEach((value) => {
// 設定運動方向 大于邊界或者小于邊界的時候換方向
if (value.local.realPos.minx + value.local.position.x < this.ContainerSize.leftPos.x) {
value.local.direction.x = -value.local.direction.x;
}
if (value.local.realPos.maxx + value.local.position.x > this.ContainerSize.rightPos.x) {
value.local.direction.x = -value.local.direction.x;
}
//每次右移1個單位 當方向為負數時就是-1個單位也就是向左移1個單位
value.local.position.x += 1 * value.local.direction.x;
// 給每個詞云加影片過渡
value.style.transform = 'translateX(' + value.local.position.x + 'px)';
});
},
initWordPos () {
// 計算每個詞的真實位置和容器的位置
this.wordArr.forEach((value) => {
value.local.realPos = {
minx: value.offsetLeft,
maxx: value.offsetLeft + value.offsetWidth
};
});
this.ContainerSize = this.getContainerSize();
},
getContainerSize () {
// 判斷容器大小控制詞云位置
const el = this.$refs.wordCloud;
return {
leftPos: {
// 容器左側的位置和頂部位置
x: el.offsetLeft,
y: el.offsetTop
},
rightPos: {
// 容器右側的位置和底部位置
x: el.offsetLeft + el.offsetWidth,
y: el.offsetTop + el.offsetHeight
}
};
}
- 我們一開始先用
initWordPos來計算每個詞語現在處于的位置并把它的位置保存起來,再使用getContainerSize獲取我們的外部容器的最左側最右側最上最下的位置保存起來, - 給我們每個
span添加一個屬性direction方向,當方向為負數則往左,方向為正則往右,注釋我寫在代碼上了,大家如果不清除可以看一下, - 也就是說我們的詞云會在容器里面反復橫跳,那我們來看看效果,
隨機位移
- 很不錯,是我們想要的效果!!!
- 當然我們每次位移不可能寫死只位移
1px我們要做到那種凌亂美,那就需要做一個隨機位移, - 那怎么來做隨機位移呢?可以看出我們的詞語其實是在做勻速直線運動而勻速直線運動的公式大家還記得嗎?
- 如果不記得的話這邊建議回去翻一下物理書~ 勻速直線運動的位移公式是
x=vt - 這個
x就是我們需要的位移,而這個t我們就不用管了因為我上面也說了這個requestAnimationFrame會幫助我們設定時間,那我們只需要控制這個v初速度是隨機的就可以了,
dealSpan () {
const wordArr = [];
this.hotWord.forEach((value) => {
...
spanDom.local = {
velocity: {
// 每次位移初速度
x: -0.5 + Math.random(),
y: -0.5 + Math.random()
},
};
...
});
this.wordArr = wordArr;
},
wordFly () {
this.wordArr.forEach((value) => {
...
//利用公式 x=vt
value.local.position.x += value.local.velocity.x * value.local.direction.x;
...
});
},
- 我們給每個詞語
span元素一個初速度,這個初速度可以為-也可以為+代表向左或者向右移動,當我們處理這個translateX的時候他就會隨機處理了,現在我們來看看效果,

完善y軸
-
現在
x軸已經按照我們想的所完成了,想讓詞云們上下左右都動起來那么我們需要按照x軸的方法來配一下y軸即可, -
由于代碼長度問題我就不放出來啦,我下面會給出原始碼,大家有興趣可以去下載看看~我們直接來看看成品!小盧感謝您的閱讀,那我就在這里祝您:

-
至此一個簡單的詞云影片就完啦,具體原始碼我放在這里,
👋 寫在最后
- 首先感謝大家看到這里,其實很多時候不同的效果有很多不同的解決方式,不能太死板于一種插件或方法,
- 前端世界太過奇妙,只有細心的人才能發現其樂趣,希望可以幫到有需要的人,
- 如果您覺得這篇文章有幫助到您的的話不妨🍉一鍵三連+評論+轉發🍉支持一下喲~~😛😛我們下期再見👋
轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/303900.html
標籤:其他
下一篇:vuex的五個屬性及使用方法
