svg、canvas、css3d实现数据可视化(伪3d)
前言:
这次项目用到了一些自定义的数据可视化组件,我把我做的部分抽出来几个典型做个汇总。
分为如下:
- 星球环绕旋转图 -- 方法一: svg:animateMotion+ animateTransform 方法二:css3d
- 地图 -- svg渲染 + div悬浮框 + js事件
- 二维饼图(风车图) -- canvas: dragCircle 、 stopDragging
- 三棱锥 -- canvas + 对数排列
- 长方体 -- css3d + 增量增长
- 雨刮器(扇型饼图) -- zrender
星球环绕旋转图
效果展示:
有些图片(例如下面这个jpg动图)太大,进行了一定程度的压缩,有点模糊(^_^)。
我的碎碎念(*^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" /> <animateTransformid="second"attributeType="XML"attributeName="transform"type="scale"begin="first.end"from="0.512"to="1"dur="3s"fill="freeze" />
方法二:css3d
参考链接:https://www.jianshu.com/p/2b85973ad1ed
- html:
<!-- 轨道 --> <div class="orbit"><!-- 行星 --><div class="planet planet1"><!-- <span class="name"></span> --></div><div class="planet planet2"><!-- <span class="name"></span> --></div> </div>
- 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; }
改进版:大小和亮暗用gap控制,近大远小,近亮远暗。
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────♪
地图
效果展示:
我的碎碎念(*^3^):
奇葩的需求(゚o゚;;, 因为甲方认为百度地图等位置不准确,不准使用百度地图和高德地图的api,又不满意天地图的样式,所以我们采用的方案是ui画地图,导出svg,再让前端根据svg做各种效果展示。
步骤:
- 文件内容
地图文件如下:index.js主文件包含悬浮事件,index.less样式文件,mapStyle.js存放背景地图,pathStyle.js数组格式存放代表地图上小块的路径
- 渲染地图
代码如下:
根据接口给的数据,按照五个色系分别给不同的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, //饼图块角度 }];
- 画饼图,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)值大小,推断出悬浮区域所在的值是什么。
♪───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
对数增长--三棱锥高度(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 ); }
增量增长--长方形高度(itemHeight)计算:
//求数据的和 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});
总结
我第一次写这么多字的总结技术的文章,排版有点乱,(╯°□°)╯︵ ┻━┻。大部分的内容其实很简单,用到的基本上是初中、高中里面最基础的数学(其实难了,我也不会了_φ(・_・)。
厚着脸皮说,我可能文字功底不咋地,但是每个例子的中心思想应该都表达了。
最后的最后,看在我第一次写了这么多字的份上,给个赞呗(///▽///)。
svg、canvas、css3d实现数据可视化(伪3d)相关推荐
- 使用HTML5 canvas和光线投影算法创建伪3D 游戏
为什么80%的码农都做不了架构师?>>> 作者 Jacob Seidelin · 2008年11月28日 本文翻译自 Creating pseudo 3D games with ...
- Canvas与web数据可视化
什么是Canvas Canvas是HTML5新增的一个元素,该元素用于2d图像的绘制,Canvas元素本身并没有绘图的能力,它需要结合javascript使用官方提供的API进行绘制图形. Canva ...
- 使用HTML5的Canvas和raycasting创建一个伪3D游戏(part1)
使用HTML5的Canvas和raycasting创建一个伪3D游戏(part1) 刚来这找到一篇好文,自己翻译了下:(原文:http://dev.opera.com/articles/view/cr ...
- css3制作八棱锥_svg、canvas、css3d实现数据可视化(伪3D效果)
前言: 这次如算上处定面一这我作问汇u应色会进灯样近项目用到了一些自定义的数据可视化组件,我把我做的部分抽出来几个典型功一新说讲为其年次供.发了架人据模制理个通似会业文告个了者到作会也转动和矿大一效做 ...
- 数据可视化——绘制3D图表和绘制地图
一.使用mplot3d绘制3D图表 1.1.mplot3d概述 matplotlib不仅专注于二维图表的绘制,也具有绘制3D图表.统计地图的功能,并将这些功能分别封装到工具包mpl_toolkits. ...
- python三维图能画地图_Python数据可视化:3D动态图,让你的足迹实现在地图上
本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理. 以下文章来源于python数据分析之禅 ,作者小dull鸟 今天给大家带来一篇3 ...
- Qt 数据可视化之3D图形
前段时间在Qt事例中看到了3D图形,就参考案例,做了一个相似的demon. 不多说了,先看做的两个效果图吧. 这里主要讲解第一个,第二个的代码会打包上传.感兴趣可以下载. 工程文件: QT += da ...
- PoPo数据可视化第8期
PoPo数据可视化 聚焦于Web数据可视化与可视化交互领域,发现可视化领域有意思的内容.不想错过可视化领域的精彩内容, 就快快关注我们吧 :) 微信订阅号:popodv_com 谷歌决定关闭云可视化服 ...
- 数据可视化的实现技术和工具比较(HTML5 canvas(Echart)、SVG、webGL等等)
http://www.cnblogs.com/zhangdi/p/3690284.html?utm_source=tuicool&utm_medium=referral 最近一直在研究数据可视 ...
最新文章
- 【解决方案】module 'cv2.cv2' has no attribute 'xfeatures2d'
- 收藏!这些 IDE 使用技巧,你都知道吗
- STM32开发 -- 4G模块开发详解(2)
- eclipse CTRL+F Find/Replace使用正则表达式
- 如何设置运行在Virtualbox内的Ubuntu虚拟机的静态ip地址
- MySQL数据库(10)----IN 和 NOT IN 子查询
- HNUCM 1388:高中数学
- 细说在兄弟连搞上PHP的那些事儿
- Java简易小说阅读器
- python2.7详细安装教程_python2.7安装图文教程
- 谈判如何在谈判中_谈判工作的十大规则
- 打算做知识付费,所有了解一下视频加密
- cad动态块制作翻转_cad动态块拉伸制作方法,单开门动态块制作教程具体分析
- 什么是商业智能(BI)?
- 解决 VMware 无法复制粘贴问题
- android获取热点主机ip和连接热点手机ip
- 数据库SQL实战-查找描述信息中包括robot的电影对应的分类名称以及电影数目(mysql)
- rviz进行kitti数据集可视化时加载小车模型报错
- onedrive手机客户端_OneDrive手机版
- 服务器装什么操作系统好?
热门文章
- 解决localhost和IP访问在IE内核浏览器中访问不一致的办法
- python花瓣飘零_小白请上车 | Python抓取花瓣网高清美图
- 基于JSP+SSM的网上商城购物网站设计
- 单片机 0xff是什么意思
- sublime c语言插件,sublime text c语言开发环境插件集合
- NTT的Another Me技术助力创造歌舞伎演员中村狮童的数字孪生体,将在 “Cho Kabuki 2022 Powered by NTT”舞台剧中首次亮相
- Tea – 简洁的云笔记
- CRCC认证申请程序
- python编程语言汇总-最全的编程语言汇总,程序员你可要存好了!
- rsync服务器文件增量备份