此次项目用到了一些自定义的数据可视化组件,我把我作的部分抽出来几个典型作个汇总。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
});复制代码
我第一次写这么多字的总结技术的文章,排版有点乱,(╯°□°)╯︵ ┻━┻。大部分的内容其实很简单,用到的基本上是初中、高中里面最基础的数学(其实难了,我也不会了_φ(・_・)。
厚着脸皮说,我可能文字功底不咋地,可是每一个例子的中心思想应该都表达了。
最后的最后,看在我第一次写了这么多字的份上,给个赞呗(///▽///)。