此次項目用到了一些自定義的數據可視化組件,我把我作的部分抽出來幾個典型作個彙總。javascript
分爲以下:css
有些圖片(例以下面這個jpg動圖)太大,進行了必定程度的壓縮,有點模糊(^_^)。html
個人碎碎念(*^3^):
之因此出現兩個版本的緣由:原本用svg實現了一版,結果後來我這個星球的svg和同事的其餘動畫svg衝突了(⁎⁍̴̛ᴗ⁍̴̛⁎),發生了巨大改變,自己svg又臭又長,改的太累,乾脆用css3d從新畫一個了( ´▽`)。
方法一:svg的animateMotion屬性 + animateTransform屬性前端
//舉例一個星球的動畫
<animateMotion dur="6s" begin="0" repeatCount="indefinite">
<mpath xlinkHref="#Path-12" /> //軌跡動畫
</animateMotion>
<animateTransform //自身動畫,靠近個人時候星球變大,遠離我時變小
id="first"
attributeType="XML"
attributeName="transform"
type="scale"
begin="0;second.end "
from="1"
to="0.512"
dur="3s"
fill="freeze"
/>
<animateTransform
id="second"
attributeType="XML"
attributeName="transform"
type="scale"
begin="first.end"
from="0.512"
to="1"
dur="3s"
fill="freeze"
/>複製代碼
方法二:css3djava
參考連接:www.jianshu.com/p/2b85973ad…css3
<!-- 軌道 -->
<div class="orbit">
<!-- 行星 -->
<div class="planet planet1">
<!-- <span class="name"></span> -->
</div>
<div class="planet planet2">
<!-- <span class="name"></span> -->
</div>
</div>複製代碼
.orbit { //軌道旋轉,公轉
border: 5px solid red;
transform-style: preserve-3d;
padding: 65px;
width: 500px;
height: 500px;
border-radius: 50%;
animation: orbit-rotate 10s linear infinite;
}
.planet { //星球自轉
width: 50px;
height: 50px;
background: url('../../img/ball1.png') no-repeat;
background-size: 100% 100%;
border-radius: 50%;
animation: self-rotate 10s linear infinite;
}
// (1)rotateX 是爲了讓整個面傾斜,translateZ是爲了防止橢圓(border)由於傾斜發生鋸齒,
// (2)停頓效果的產生,其實我是走了野路子的。五個球,根據360/5=72,寫了五個不一樣的關於orbit的class,
// 0 + 72,....360依次增長72,直到360,利用setimeout每隔4秒,按順序切換一個class
@keyframes orbit-rotate {
0% {
transform: rotateX(70deg) rotateZ(0deg) translateZ(0);
}
100% {
transform: rotateX(70deg) rotateZ(-360deg) translateZ(0);
}
}
@keyframes self-rotate {
0% {
transform: rotateX(-90deg) rotateY(360deg) rotateZ(0deg);
}
100% {
transform: rotateX(-90deg) rotateY(0deg) rotateZ(0deg);
}
}
.planet1 { //肯定星球開始位置
position: absolute;
top: 65px;
right: 65px;
}
.planet2 { //肯定星球開始位置
position: absolute;
bottom: 65px;
right: 65px;
}複製代碼
改進版:大小和亮暗用gap控制,近大遠小,近亮遠暗。canvas
const orbitStyle = {
transform: `rotateX(70deg) rotateZ(${activeCircle * -72}deg) translateZ(0)`,
};
const planetStyle = (index, l) => {
// l是數組的長度
const average = l / 2; // 計算平均數
const gap = 0.8 ** (average - Math.abs(Math.abs(index - (activeCircle % l)) - average)); // 先求不一樣球不一樣時間的絕對值來計算點在區間的距離,再根據距離計算改變值
return {
transform: `rotateX(-90deg) rotateY(${360 - activeCircle * 72}deg) rotateZ(0deg) scale(${gap})`,
opacity: gap,
};
};
複製代碼
♪───O(≧∇≦)O────♪可愛的分割線♪───O(≧∇≦)O────♪api
個人碎碎念(*^3^):
奇葩的需求(゚o゚;;, 由於甲方認爲百度地圖等位置不許確,不許使用百度地圖和高德地圖的api,又不滿意天地圖的樣式,因此咱們採用的方案是ui畫地圖,導出svg,再讓前端根據svg作各類效果展現。
地圖文件以下:index.js主文件包含懸浮事件,index.less樣式文件,mapStyle.js存放背景地圖,pathStyle.js數組格式存放表明地圖上小塊的路徑數組
代碼以下:bash
根據接口給的數據,按照五個色系分別給不一樣的path填充(fill)不一樣的顏色
const colorMap = [
'rgba(89, 126, 247, 0.8)',
'rgba(0, 121, 254, 0.8)',
'rgba(0, 121, 254, 0.8)',
'rgba(38, 168, 254, 0.8)',
'rgba(192, 228, 253, 0.8)',
];複製代碼
render代碼以下:
鼠標移入事件:
鼠標移出事件:
♪───O(≧∇≦)O────♪可愛的分割線♪───O(≧∇≦)O────♪
個人碎碎念(*^3^):
由於echarts的餅圖都是一個參數緯度的餅圖,而此次ui要求兩個參數緯度的餅圖,只能本身畫了(´;ω;`)。由於以前用canvas畫過餅圖,原本覺得仍是簡單的,結果甲方爸爸看了成果,說要加自定義懸浮事件(剛開始prd沒有的),廢了3天畫了一個夠用版的。
追加:有人說echarts也能夠實現,我去試了試,echarts的ZRender能夠實現。
option.push=[{
color: color[i], //餅圖塊顏色
radius: item.revenueTaxAvg, //餅圖塊半徑
name: item.domainName, // 餅圖塊名稱
angle: item.companyCnt, //餅圖塊角度
}];複製代碼
這篇文章畫的是angle一個緯度,只要再增長另一個緯度radius就好。
canvas畫的文字和圖,會有必定程度的模糊,解決方案:把畫布的寬高增長2倍。
if(點到圓心的距離<圓的最大半徑
&&點到圓心的距離>圓的最小半徑
&&點到圓心的直線的角度>扇形的起始角度
&&點到圓心的直線的角度<扇形的結束角度){
點在扇形區域內//使用勾股定理計算這個點與圓心之間的距離
distanceFromCenter =
Math.sqrt(Math.pow(circle.x - clickX, 2) + Math.pow(circle.y - clickY, 2))
//α(弧度)= L (弧長)/ r(半徑),可是弧長,我求不出來。
(點到圓心的直線的角度)的範圍我主要使用sin(x),以下方法。
判斷不一樣區間的sin(x)值大小,推斷出懸浮區域所在的值是什麼。複製代碼
♪───O(≧∇≦)O────♪可愛的分割線♪───O(≧∇≦)O────♪
主要原理:兩個三角形 + 一個園 = 三棱錐
canvas.width = canvas.offsetWidth; //防止圖片變形
canvas.height = canvas.offsetHeight;
ctx.clearRect(0, 0, canvas.width, canvas.height); 清除畫布
const { height } = canvas; // 計算等邊三角形的高
//以下圖,第一個三角形 A-B-C
ctx.moveTo(100, 0); // 從A(100,0)開始
ctx.lineTo(0, height); // 從A(100,0)開始,畫到B (0,height)結束
ctx.lineTo(144, height); // B(0,height)-C(144, height)
//第二個三角形 A-C-D
ctx.moveTo(100, 0); // 從A(100,0)開始
ctx.lineTo(143, height); // A-C
ctx.lineTo(210, height); // C-D
//第三個畫圓
ctx.arc(100, 23 , 23, 0, Math.PI * 2, false); // 畫圓
<canvas id={`pyramid${id}`} height={itemHeight} /> //計算itemHeight複製代碼
假設輸入
data = [0, 1, 2, 3, 4, 5],x爲其中任意值;
maxHeight 爲最大高度;
輸出
itemHeight(0 <= itemHeight< maxHeight),成對數增加
//求最大值
const max = MAX(data)
//排除 x === 0 的狀況
由於logmax(max)= 1,且x > 0
由上圖可得 0 < logmax(x)< 1
因此 0 < logmax(x) * maxHeight < maxHeight
可知 logmax(x) * maxHeight 成對數變化
又由於logmax(x) = loge(x) / loge(max)
//寫成代碼爲
const max =data.reduce((a, b) => {
return a > b ? a : b;
}, 0);
itemHeight = x===0 ? 0 : Math.log(x) / Math.log(max) * maxHeight
複製代碼
y軸計算採用指數增加,由於任意max的0次方 = 1, 因此單獨判斷 i <= 0的狀況
i > 0 ? Math.round(max ** (i * 0.25)) : 0
html
<div id="cube">
<figure class="front">1</figure>
<figure class="back">2</figure>
<figure class="right">3</figure>
<figure class="left">4</figure>
<figure class="top">5</figure>
<figure class="bottom">6</figure>
</div>複製代碼
css
#box.show-front { transform: translateZ( -50px ) rotateY( 0deg ); }
#box.show-back { transform: translateZ( -50px ) rotateX( -180deg ); }
#box.show-right { transform: translateZ( -150px ) rotateY( -90deg ); }
#box.show-left { transform: translateZ( -150px ) rotateY( 90deg ); }
#box.show-top { transform: translateZ( -100px ) rotateX( -90deg ); }
#box.show-bottom { transform: translateZ( -100px ) rotateX( 90deg ); } 複製代碼
//求數據的和
const sum =data.reduce((a, b) => {
return a + b;
}, 0);
itemHeight = x <= min ? min : min + (max-min) * x /sum;複製代碼
♪───O(≧∇≦)O────♪可愛的分割線♪───O(≧∇≦)O────♪
(1) 傳入傳輸
percent // 佔比
複製代碼
(2) 畫不一樣顏色的圓
const circles = [
{ r: 37, stroke: '#0A63D6', lineWidth: 1 },
{ r: 43, stroke: 'rgba(79, 4, 175, 1)', lineWidth: 10 },
{ r: 53, stroke: '#0A63D6', lineWidth: 15 },
{ r: 63, stroke: '#0088F3', lineWidth: 20 },
{ r: 70, stroke: 'rgba(11, 84, 166, 0.5)', lineWidth: 70 },
];
const startAngle = 0.5 * Math.PI;
const endAngle = Math.PI * 2 * percent + startAngle;
for (const item of circles) {
const { r, stroke, lineWidth } = item;
const circle = new zrender.Arc({
shape: {
cx,
cy,
r,
startAngle,
endAngle,
},
style: {
fill: 'transparent',
stroke,
lineWidth,
},
});
zr.add(circle);
}
複製代碼
(3)畫園外面的藍色的邊:第一條位置固定,第一二條經過旋轉相應角度實現
const borderStyle={
shape : {
x1: cx,
y1: cy + 37,
x2: cx,
y2: cy + 103.5,
},
style: {
stroke: '#0A63D6',
lineWidth: 1,
},
}
const path1 = new zrender.Line(borderStyle);
const path2 = new zrender.Line({
origin: [cx, cy],
rotation: -Math.PI * 2 * percent,
...borderStyle
});複製代碼
我第一次寫這麼多字的總結技術的文章,排版有點亂,(╯°□°)╯︵ ┻━┻。大部分的內容其實很簡單,用到的基本上是初中、高中裏面最基礎的數學(其實難了,我也不會了_φ(・_・)。
厚着臉皮說,我可能文字功底不咋地,可是每一個例子的中心思想應該都表達了。
最後的最後,看在我第一次寫了這麼多字的份上,給個讚唄(///▽///)。