前言:

这次项目用到了一些自定义的数据可视化组件,我把我做的部分抽出来几个典型做个汇总。
分为如下:

  • 星球环绕旋转图

    • 方法一: svg:animateMotion+ animateTransform

    • 方法二:css3d

  • 地图

    • svg 渲染 + div 悬浮框 + js 事件

  • 二维饼图(风车图)

    • canvas: dragCircle 、 stopDragging

  • 三棱锥

    • canvas + 对数排列

  • 长方体

    • css3d + 增量增长

星球环绕旋转图

效果展示:

a6145b6906cf05f2b1ba5c4938ce2e9e.gif

有些图片(例如上面这个 jpg 动图)太大,进行了一定程度的压缩,有点模糊(^_^)。

我的碎碎念(*^3^): 之所以出现两个版本的原因:本来用 svg 实现了一版,结果后来我这个星球的 svg 和同事的其他动画 svg 冲突了(⁎⁍̴̛ᴗ⁍̴̛⁎),发生了巨大改变,本身 svg 又臭又长,改的太累,干脆用 css3d 重新画一个了( ´▽`)。

步骤:

//举例一个星球的动画

"6s" begin="0" repeatCount="indefinite">

 "#Path-12" /> //轨迹动画

 id="first"

 attributeType="XML"

 attributeName="transform"

 type="scale"

 begin="0;second.end "

 from="1"

 to="0.512"

 dur="3s"

 fill="freeze"

/>

 id="second"

 attributeType="XML"

 attributeName="transform"

 type="scale"

 begin="first.end"

 from="0.512"

 to="1"

 dur="3s"

 fill="freeze"

/>

作者:溯朝

链接:https://juejin.im/post/5b690a66f265da0f820254bd

来源:掘金

著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。


参考链接:https://www.jianshu.com/p/2b85973ad1ed

  • html:

class="orbit">

 

class="planet planet1">

 

class="planet planet2">
  • css:

.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;

}

♪───O(≧∇≦)O────♪可爱的分割线♪───O(≧∇≦)O────♪

地图

f7511d64a784644ef0efb2ada14c9551.gif

我的碎碎念(*^3^): 奇葩的需求(゚o゚;;, 因为甲方认为百度地图等位置不准确,不准使用百度地图和高德地图的api,又不满意天地图的样式,所以我们采用的方案是ui画地图,导出svg,再让前端根据svg做各种效果展示。

步骤:

  • 文件内容

地图文件如下:index.js主文件包含悬浮事件,index.less样式文件,mapStyle.js存放背景地图,pathStyle.js数组格式存放代表地图上小块的路径

4543a237dc1cf8f41734025b011a3172.png

84a2d37a894d2e574f90ba20f8a060f7.png

  • 渲染地图

代码如下:

c1bbc0b9df9efe5b7f5cdaabe98b5024.png
根据接口给的数据,按照五个色系分别给不同的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 代码如下:346c344f1ba53b24d2f8a1fa72409041.png

  • 鼠标移入事件:

c32a38f3c0ec3e184bc37ddce2de8456.png

  • 鼠标移出事件

c8d5e98e0f7847bf3cbf4af290ec70b6.png♪───O(≧∇≦)O────♪可爱的分割线♪───O(≧∇≦)O────♪

二维饼图(风车图)

效果展示:

7200cfe8f144b455fc4ce5d2efa17eac.png

我的碎碎念(*^3^):因为 echarts 的饼图都是一个参数纬度的饼图,而这次ui要求两个参数纬度的饼图,只能自己画了(´;ω;`)。因为之前用 canvas 画过饼图,本来以为还是简单的,结果甲方爸爸看了成果,说要加自定义悬浮事件(刚开始 prd 没有的),废了3天画了一个够用版的。

步骤:

  • 传入参数

option.push=[{

  color: color[i], //饼图块颜色

  radius: item.revenueTaxAvg, //饼图块半径

  name: item.domainName, // 饼图块名称

  angle: item.companyCnt, //饼图块角度  

}];

  • 画饼图,PieCanvas.drawPieCanvas('econComposChart', option);

    • 怎么画饼图?,可以参考我以前写的一篇文章:https://juejin.im/post/5b1e27726fb9a01e345ba2e1

    • ==/* 注意 */==:这篇文章画的是 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)值大小,推断出悬浮区域所在的值是什么。

c109447cbb45ca8814c7cd1ab9ccb7cc.png♪───O(≧∇≦)O────♪可爱的分割线♪───O(≧∇≦)O────♪

三棱锥

效果展示:

2314956c451d7c26410bee6000471c18.png

步骤:

主要原理:两个三角形 + 一个园 = 三棱锥243f9ad19a5e06076ff8e08d6c487a90.png

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); // 画圆

`pyramid${id}`} height={itemHeight} /> //计算itemHeight

对数增长--三棱锥高度(itemHeight)计算:

72355ec06e249dec681e57f84acec47c.png

假设输入

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

长方体

效果展示:

0872fde6dc59fa488ce6f1b1a5a93c62.png

步骤:

html

id="cube">

       

class="front">1

       

class="back">2

       

class="right">3

       

class="left">4

       

class="top">5

       

class="bottom">6

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 ); }

增量增长--长方形高度(itemHeight)计算:

//求数据的和

const sum =data.reduce((a, b) => {

 return a + b;

}, 0);

itemHeight = x <= min ? min : min + (max-min) * x /sum;

♪───O(≧∇≦)O────♪可爱的分割线♪───O(≧∇≦)O────♪

总结

我第一次写这么多字的总结技术的文章,排版有点乱,(╯°□°)╯︵ ┻━┻。大部分的内容其实很简单,用到的基本上是初中、高中里面最基础的数学(其实难了,我也不会了φ(・・)。
厚着脸皮说,我可能文字功底不咋地,但是每个例子的中心思想应该都表达了。
最后的最后,看在我第一次写了这么多字的份上,给个赞呗(///▽///)。

f8d26efc2a5b170d548a2465ba2ad7d0.png

Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐