1.带你入门three.js——从0到1实现一个3d可视化地
2.canvaså°å¾äººç©ä½ç½®
3.跪求javascript大神帮助,地图地图跑酷类地图背景自动循环!源码!地图地图
4.å¦ä½å¨html5çcanvasç»å¶å°å¾
带你入门three.js——从0到1实现一个3d可视化地
前言
终于到周末了,源码前几篇的地图地图文章一直给大家介绍2d,canvas 和svg的源码qq查询源码一些东西。7月份我打算输出3篇万字长文带大家系统地学习可视化表达的地图地图3种方式,svg、源码canvas、地图地图webgl。源码所以这是地图地图第一篇文章3d的。 读完本篇文章,源码你可以学到什么
3d框架的地图地图选择——three.js
为什么选择three.js
three.js 对 WebGL 进行了封装,让前端开发人员在不需要掌握很多数学知识和绘图知识的源码情况下,也能够轻松进行web 3D开发,地图地图降低了门槛,同时大大提升了效率。你不懂计算机图形学,只要理解了three.js的一些基本概念就可以。
Threejs 的android 源码 同步基本要素——场景
定义如下:
「场景」:是一个三维空间,所有物品的容器。你可以往里面添加物体,比如正方体、矩形等。three.js 整个之间的关系是一个「树形结构」。
Threejs 的基本要素——相机
「相机」:用来确定位置、方向、角度,相机看到的内容就是我们最终在屏幕上看到的内容。three.js 中的相机分为两种,一种是正交相机 和透视相机。
透视相机
视锥体由6个裁剪面围成,构成视锥体的4个侧面称为上左下右面,分别对应屏幕的四个边界。为了防止物体离摄像机过近,设置近切面,同时为了防止物体离摄像机太远而不可见,设置远切面。透视相机最大的点单源码特点就是符合我们人眼观察事物的特点, 近大远小。
正交相机
正交相机的特点就是视椎体是一个立方体。在这种投影模式下,无论物体距离相机距离远或者近,在最终渲染的中物体的大小都保持不变。
Threejs 的基本要素——网格
网格由三角形组成的网格来描述。一条弧线是由多条线段得到,线段的数量越多,越接近弧线。在计算机的世界里,一条弧线是由有限个点构成的有限条线段连接得到的。three.js 提供了一些常见的几何形状,有三维的也有二维的。
2d与3d
有了形状,可能渲染出来的图形没有美丽的样子,这时候材质就出来了。材质+几何体就是一个 mesh。three.js 提供了一些比较有代表性的材质,常用的球队管理 源码用漫反射、镜面反射两种材质,还可以引入外部,贴到物体表面,成为纹理贴图。
Threejs 的基本要素——灯光
Threejs 支持模拟不同光源,展现不同光照效果,有环境光、平行光、聚光灯、点光源等。
Threejs 的基本要素——渲染器
渲染器就是去渲染你场景中灯光、相机、网格等。
可视化地图——three.js实现场景的搭建
场景搭建包括照相机、渲染器,我用一个map类来表示。场景有了,灯光也有了,接下来我们看下样子。多级菜单源码
对场景黑乎乎的什么都没有,接下来我们随便加一个长方体并且调用renderer的render方法。
按照上面的步骤去做,你会发现页面还是明明都已经加了,为什么呢?「默认情况下,当我们调用scene.add()的时候,物体将会被添加到(0,0,0)坐标。但将使得摄像机和立方体彼此在一起。为了防止这种情况的发生,我们只需要将摄像机稍微向外移动一些即可」
所以只要将照相机的位置z轴属性调整一下就可以到了
这时候有同学就会问,嗯搞了半天不和canvas 2d 一样嘛,有什么区别? 看不出立体的感觉? OK 接下来我就让这个立方体动起来。其实就是不停的去调用我们render函数。我们用requestanimationframe。尽量还是不要用setInterval,有一个很简单的优化。
效果如下图所示:
是不是有那个立体的感觉了?我以最简单的立方体的旋转,带大家从头入门下three.js。 如果看到这里觉得对你有帮助的话,希望你能给我点个赞哦,感谢各位老铁了!下面正式地图需求分析。
地图数据的获得
获取地图数据,大家可以了解下openStreetMap,这是一个可供自由编辑的世界地图。有了点才能生成线,最后才能生成平面。这里涉及到一个知识点,「墨卡托投影转换」。墨卡托投影转换可以把我们经纬度坐标转换成我们对应平面的2d坐标。
我直接用可视化框架——「d3」 它里面有自带的墨卡托投影转换。由于中国有很多省,每个省都对应一个Object3d。
Object3d是three.js 所有的基类, 提供了一系列的属性和方法来对三维空间中的物体进行操纵。通过.add( object )方法来将对象进行组合,该方法将对象添加为子对象。我这里的整个中国是一个大的Object3d,每一个省是一个Object3d,省是挂在中国下的。很明显,在three.js 是一个很典型的树形数据结构。
我们看下代码:
整体框架定下来了,接下来我们进入核心环节。
生成地图几何体
这里用到了 Three.shape() 和 THREE.ExtrudeGeometry()。为什么会用到这个呢?首先每一个省份轮廓组成的下标是一个 2d坐标,但是我们要生成立方体,shape() 可以定义一个二维形状平面。它可以和ExtrudeGeometry一起使用,获取点,或者获取三角面。
我们看下代码:
代码如下:
遍历第一个点的的和canvas2d画图其实是一模一样的,移动起点,然后后面在划线,画出轮廓。然后我们在这里可以设置拉伸的深度,然后接下来就是设置材质了。lineGeometry 其实 对应的是轮廓的边线。我们看下吧:
相机辅助视图
为了方便调相机位置,我增加了辅助视图, cameraHelper。然后你回看下屏幕会出现一个十字架,然后我们就可以不断地调整相机的位置,让我们地地图处于画面的中央:
经过辅助的视图地不断调整:
是不是有那个立体感了?到这里我们的中国地图已经在画布的中央了就已经实现了。
增加交互控制器
现在地图是已经生成了,但是用户交互感比较差,这里我们引入three的OrbitControls 可以用鼠标在画面随意转动,就可以看到立方体的每一个部分了。
射线追踪
但是这个方法不在three 的包里面,得单独引入一个文件。我们可以对canvas监听的onmouseMove事件,然后 我们就可以知道当前移动的鼠标是选择的哪一个mesh。但是在这之前,我们先对每一个province这个对象上增加一个属性来表示他是哪一个省份的。
引入射线追踪,带入如下:
由于我们不停地在在画布移动,所以需要不停的的射线位置。现在有了射线,那我们需要场景的所有东西去比较了,rayCaster 也提供了方法:
这个intersects得到的交叉很多,但是呢我们只选择其中一个,那就是物体材质个数有两个的,因为我们上面就是用对mesh用两个材质。
过滤代码如下:
看下效果图:
增加tooltip
为了让交互更加完美,找到了同时在鼠标右下方显示个tooltip,那这个肯定是一个div默认是影藏的,然后根据鼠标的移动移动相应的位置。
第一步新建div
第二步设置样式 默认是影藏的
第三步更改div的位置:
最后一步设置tooltip的名字:
到这里,整个3d可视化地球项目已经完成了,我们一起来看下效果吧。
总结
如果觉得看完对你有帮助的话,希望你不要吝啬你手中的 ,点个 和关注是对我最大的支持,知识输出不容易,而我勿忘初心,持续分享可视化的好文章。如果你对可视化感兴趣,你可以关注下面我的可视化专栏,或者可以关注我的公众号:「前端图形」,持续分享计算机图形学知识。本篇文章的所有代码都在我的 github 上 欢迎star,最后文章有哪里写的不对的地方,欢迎指正交流。
canvaså°å¾äººç©ä½ç½®
æcanvas设置æåå±å¹ç宽çé«ï¼canvasçèµ·ç¹åæ ï¼0ï¼0ï¼è®¾ç½®å¨å·¦ä¸è§
å½ä½ 使ç¨ctx.fillRect(,,,)ï¼ctx.fill();ç»å¾æ¶ï¼å¯ä»¥å¾åç¡®çæè¿ä¸ªç©å½¢å®ä½å¨å±å¹ä¸çåæ ï¼ï¼ï¼ä¸ã
å½æ¶å½ä½ 使ç¨ctx.strokeRect(,,,)æ¶ï¼åä½ç½®åçå移ï¼å移å¤å°åç©å½¢è¾¹æ¡çç²ç»æå ³ã
跪求javascript大神帮助,跑酷类地图背景自动循环!!
<script>
var i=1;
setInterval("changeBag()",);
function changeBag(){
if(i=5) i=1;
document.getElementById("bb").src=i+".jpg";
i++;
}
</script>
å¦ä½å¨html5çcanvasç»å¶å°å¾
æè¿é认为大家é½ç¨å¾®äºè§£çè³çæcanvasçä¸äºAPIï¼å°±ä¸å ·ä½è¯´ï¼æ¯ä¸ä¸ªåæ°ä»£è¡¨ä»ä¹ææäºã
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8'>
<title>å¾çå 载平移æ¾å¤§ç¼©å°ç¤ºä¾</title>
<style>
html,body{
margin:0px;
padding:0px;
}
canvas{
border: 1px solid #;
}
</style>
</head>
<body>
<canvas id="canvas" width="" height=""></canvas>
<script type="text/javascript" src="main.js"></script>
</body>
</html>
var canvas,context;
function int(){
canvas=document.getElementById('canvas');
context=canvas.getContext('2d');
}
å¾çå è½½
å建ä¸ä¸ªå¾ç对象ä¹åï¼å¾çä¸è½é©¬ä¸ç»å¶å°canvasä¸é¢ï¼å 为å¾çè¿æ²¡æå è½½å®æãæ以æ们éè¦çå¬å¾ç对象å è½½å®äºä»¶ï¼ç¶ååå»ç»å¶ã
var img,//å¾ç对象
imgIsLoaded//å¾çæ¯å¦å è½½å®æ;
function loadImg(){
img=new Image();
img.onload=function(){
imgIsLoaded=true;
//draw image
}
img.src="map.jpg";
}
å¾çç»å¶
ç»å¶å¾åä¸ä¸ªå½æ°å°±å¯ä»¥æå®ï¼ä½æ¯éè¦è®°å½è¿ä¸ªå¾åçå·¦ä¸è§åæ 以å缩æ¾æ¯ä¾ã
var imgX,imgY,imgScale;
function drawImage(){
context.clearRect(0,0,canvas.width,canvas.height);
context.drawImage(img,0,0,img.width,img.height,imgX,imgY,img.width*imgScale,img.height*imgScale);
}
å¾ç平移
html5äºä»¶æå°ç»åº¦å¨DOMä¸ï¼æ以æ们æ æ³å¯¹canvasä¸çå¾ååçå¬ï¼åªè½å¯¹canvasçå¬ã
é¦å çå¬é¼ æ mousedownäºä»¶ï¼çäºä»¶åçä¹åï¼åçå¬é¼ æ mousemoveäºä»¶åmouseupäºä»¶
mousemoveäºä»¶åçä¹åï¼è·å¾é¼ æ 移å¨çä½ç§»ï¼ç¸åºçå¾ççä½ç½®æ¹åå¤å°
mouseupäºä»¶åçä¹åï¼åæ¶å¯¹mousemove以åmouseupäºä»¶çå¬
canvas.onmousedown=function(event){
var pos=windowToCanvas(canvas,event.clientX,event.clientY);
canvas.onmousemove=function(event){
canvas.style.cursor="move";
var pos1=windowToCanvas(canvas,event.clientX,event.clientY);
var x=pos1.x-pos.x;
var y=pos1.y-pos.y;
pos=pos1;
imgX+=x;
imgY+=y;
drawImage();
}
canvas.onmouseup=function(){
canvas.onmousemove=null;
canvas.onmouseup=null;
canvas.style.cursor="default";
}
}
function windowToCanvas(canvas,x,y){
var bbox = canvas.getBoundingClientRect();
return {
x:x - bbox.left - (bbox.width - canvas.width) / 2,
y:y - bbox.top - (bbox.height - canvas.height) / 2
};
}
å¾ç缩æ¾
å ¶å®ç¼©æ¾å¾ç®åï¼ç¨å¾®å¤æçæ¯ï¼å¦ä½è®©é¼ æ æ为æ¾å¤§æè 缩å°çä¸å¿ãå¦ææ°å¦å ä½ä¸å¥½ï¼è®¡ç®å ¬å¼å°±å¯è½çä¸æç½äºã
canvas.onmousewheel=canvas.onwheel=function(event){ //chrome firefoxæµè§å¨å ¼å®¹
var pos=windowToCanvas(canvas,event.clientX,event.clientY);
event.wheelDelta=event.wheelDelta?event.wheelDelta:(event.deltaY*(-));
if(event.wheelDelta>0){
imgScale*=2;
imgX=imgX*2-pos.x;
imgY=imgY*2-pos.y;
}else{
imgScale/=2;
imgX=imgX*0.5+pos.x*0.5;
imgY=imgY*0.5+pos.y*0.5;
}
drawImage();
}
è¿ä¸ªæ¶åï¼åºæ¬åè½å°±å®ç°äºï¼å è½½ä¸å¼ å¾çåå è½½å¤å¼ å¾çé½å·®ä¸å¤ï¼ç»´æ¤æ¯ä¸å¼ å¾ççä½ç½®å大å°ï¼ä¸é¢æ¥æ´çä¸ä¸ä»£ç å§ã
var canvas,context;
var img,//å¾ç对象
imgIsLoaded,//å¾çæ¯å¦å è½½å®æ;
imgX=0,
imgY=0,
imgScale=1;
(function int(){
canvas=document.getElementById('canvas');
context=canvas.getContext('2d');
loadImg();
})();
function loadImg(){
img=new Image();
img.onload=function(){
imgIsLoaded=true;
drawImage();
}
img.src="map.jpg";
}
function drawImage(){
context.clearRect(0,0,canvas.width,canvas.height);
context.drawImage(img,0,0,img.width,img.height,imgX,imgY,img.width*imgScale,img.height*imgScale);
}
canvas.onmousedown=function(event){
var pos=windowToCanvas(canvas,event.clientX,event.clientY);
canvas.onmousemove=function(event){
canvas.style.cursor="move";
var pos1=windowToCanvas(canvas,event.clientX,event.clientY);
var x=pos1.x-pos.x;
var y=pos1.y-pos.y;
pos=pos1;
imgX+=x;
imgY+=y;
drawImage();
}
canvas.onmouseup=function(){
canvas.onmousemove=null;
canvas.onmouseup=null;
canvas.style.cursor="default";
}
}
canvas.onmousewheel=canvas.onwheel=function(event){
var pos=windowToCanvas(canvas,event.clientX,event.clientY);
event.wheelDelta=event.wheelDelta?event.wheelDelta:(event.deltaY*(-));
if(event.wheelDelta>0){
imgScale*=2;
imgX=imgX*2-pos.x;
imgY=imgY*2-pos.y;
}else{
imgScale/=2;
imgX=imgX*0.5+pos.x*0.5;
imgY=imgY*0.5+pos.y*0.5;
}
drawImage();
}
function windowToCanvas(canvas,x,y){
var bbox = canvas.getBoundingClientRect();
return {
x:x - bbox.left - (bbox.width - canvas.width) / 2,
y:y - bbox.top - (bbox.height - canvas.height) / 2
};