實現一個文字顏色隨機,字型大小成正態分佈,整體呈菱形的排列的標籤列表;
如何實現一個如下圖隨機排布的標籤列表(vue語法):
dome示例: http://39.106.166.212/tag
首先假設我們可以拿到一個標籤列表的陣列,這裡將一步步實現如圖效果:
1、建立tag元件,編寫 隨機顏色方法 和 大小成正態分佈的方法 實現顏色和大小隨機的文字標籤;
(1)編寫隨機顏色方法$RandomColor
通過隨機生成一個有效範圍內的rgb值即可實現;
/*隨機顏色rgb*/
const $RandomColor = function(){
var r=Math.floor(Math.random()*256);
var g=Math.floor(Math.random()*256);
var b=Math.floor(Math.random()*256);
return "rgb("+r+','+g+','+b+")";
}
複製程式碼
為了防止與背景顏色重合也可hsl模式生成,降低明度防止與背景重合;
/*隨機顏色hsl*/
const $RandomColor2 = function() {
return "hsl(" +
Math.round(Math.random() * 360) + "," +
Math.round(Math.random() * 100) + '%,' +
Math.round(Math.random() * 80) + '%)';
}
複製程式碼
(2)編寫實現正態分佈的方法$Normal
js中只有隨機分佈,通過網上查詢方法可以通過Box-muller演算法將兩個隨機分佈拼接為一個正態分佈:
參考:
https://www.cnblogs.com/zztt/p/4025207.html
https://en.wikipedia.org/wiki/Box%E2%80%93Muller_transform,寫成js為
入參為均值和方差:
/*利用Box-Muller方法極座標形式 使用兩個均勻分佈產生一個正態分佈*/
const $Normal = function(mean,sigma){
var u=0.0, v=0.0, w=0.0, c=0.0;
do{
//獲得兩個(-1,1)的獨立隨機變數
u=Math.random()*2-1.0;
v=Math.random()*2-1.0;
w=u*u+v*v;
}while(w==0.0||w>=1.0)
c=Math.sqrt((-2*Math.log(w))/w);
return mean+u*c*sigma;
}
複製程式碼
自此一個隨機顏色和大小的tag元件完成
2、將陣列隨機排列為菱形(中間多兩頭少的列表)
首先在created中從介面獲取到如下一個陣列列表,這裡我們不使用最大值引數,預設為剩餘的一半;
(1)編寫隨機拆分一個數的方法$RandomSplit獲取一組和為列表長度的陣列;
/*
* 隨機拆分一個數
* params 總和,個數,最大值 {num}
*/
const $RandomSplit = function(total,nums,max) {
let rest = total;
let result = Array.apply(null, { length: nums })
.map((n, i) => nums - i)
.map(n => {
const v = 1 + Math.floor(Math.random() * (max | rest / n * 2 - 1));
rest -= v;
return v;
});
result[nums - 1] += rest;
return result;
}
複製程式碼
下圖是將44隨機拆分為8個數的和,
(2)編寫$NormalSort方法將上面獲取到的數隨機排列為兩端小中間大陣列;
大概思路為先排序,然後每次去兩個數分別放置在兩端,放置的過程彙總計算兩端和,判斷大小選擇性放置,防止兩端排布不均勻
/*類正態排序*/
const $NormalSort = function(arr){
var temp = [], i = 0, l = arr.length,leftTo=0,rightTo=0,
sortArr = arr.sort(function(a,b){return b-a}); //先將陣列從大到小排列得到 [7,6,5,5,4,3,2,1]
while(arr.length>1){
let a = arr.pop();
let b= arr.pop();
if(leftTo<rightTo){
temp[i] = b;
temp[l-(i+1)] = a;
}else{
temp[i] = b;
temp[l-(i+1)] = a;
}
i++;
}
return temp;
}
複製程式碼
我們該方法將上一步的陣列重新排序為:
(3)最後將list資料按照上一步獲取的資料結構從新生成一個可供v-for使用的資料結構;
computed:{
tags(){
this.list = $NormalSort($RandomSplit(this.tagList.length,8));//獲取資料結構
let temp = this.tagList.sort(function(a,b){ //重新隨機排序
return Math.random()>.5 ? -1 : 1;
}).concat();
return this.list.map((v,k) => {//根據list生成資料結構
return temp.splice(0,v);
})
}
},
複製程式碼
html程式碼
<div v-for="(item,index) in tags" :key="index" class="tag-body-tags-li">
<Tag v-for="(tag,index) in item" :key="tag.id" :tname="tag.name" ></Tag>
</div> 複製程式碼