作用力和小球

作用力和小球

测试地址

上面的这种神奇的效果是使用D3.js实现的,d3的是代码库和教程请参见这里。d3是一个极其强大的数据图表库,尤其擅长操作svg,虽然被设计用来展示数据,但是其丰富的svg操作方法还有物理引擎可以被我们用来制作页面的展示。例子中的展示页面就是目前我们公司主要的产品,采用此种展示方式其效果自然是不言而喻。

分析下效果,主要是一些节点与连线,再加上节点间相互的引力与斥力还有重力效果。节点与连接线好说,使用d3绘制出svg即可;相互作用力可以使用d3提供的force中相应的方法实现。关于d3详细教程请参见其官网,这里只介绍使用到的部分。

首先将节点数据储存到数据文件中data.json

{
  "nodes":[
    {"name":"买房邦","group":1,"class":"node","tourl":"http://www.iloushi.cn/m/maifangbang/","img":"...","imgx":"-54","imgy":"-54","w":"88","h":"88"},
    {"name":"楼盘码","group":2,"class":"node","tourl":"http://www.iloushi.cn/m/loupanma/","img":"...","imgx":"-28","imgy":"-28","w":"56","h":"56"},
    {"name":"i楼市","group":3,"class":"node","tourl":"http://www.iloushi.cn/m/iloushi/","img":"...","imgx":"-34","imgy":"-34","w":"68","h":"68"},
    {"name":"乐生活","group":4,"class":"node","tourl":"http://www.iloushi.cn/m/leshenghuo/","img":"...","imgx":"-48","imgy":"-48","w":"83","h":"83"},
    {"name":"卖房邦","group":5,"class":"node","tourl":"http://www.iloushi.cn/m/maifangbangv/","img":"...","imgx":"-35","imgy":"-37","w":"97","h":"97"},
    {"name":"多媒体楼书","group":6,"class":"node","tourl":"http://www.iloushi.cn/m/ebook/","img":"...","imgx":"-34","imgy":"-34","w":"63","h":"63"},
    {"name":"房产智能TV","group":7,"class":"node","tourl":"http://www.iloushi.cn/m/tv/","img":"...","imgx":"-36","imgy":"-36","w":"72","h":"72"},
    {"name":"微客智慧WIFI","group":7,"class":"node","tourl":"http://www.iloushi.cn/m/wifi/","img":"...","imgx":"-35","imgy":"-35","w":"71","h":"70"},
    {"group":1,"class":"fs drops","img":"...","imgx":"-7","imgy":"-7","w":"14","h":"14"},
    {"group":1,"class":"fs drops","img":"...","imgx":"-4","imgy":"-4","w":"9","h":"9"},
    {"group":1,"class":"fs drops","img":"...","imgx":"-5","imgy":"-5","w":"10","h":"10"},
    {"group":1,"class":"fs drops","img":"...","imgx":"-7","imgy":"-7","w":"14","h":"14"},
    {"group":1,"class":"fs drops","img":"...","imgy":"-3","w":"6","h":"6"}
  ],
  "links":[
    {"source":0,"target":2},
    {"source":1,"target":2},
    {"source":4,"target":2},
    {"source":6,"target":7},
    {"source":5,"target":7},
    {"source":3,"target":7},
    {"source":7,"target":1},
    {"source":8,"target":6},
    {"source":9,"target":3},
    {"source":4,"target":10},
    {"source":2,"target":11},
    {"source":3,"target":1}
  ]
}

这个json的结构是d3的规范,其中分为nodes和links。nodes即效果中的圆球,包括大的彩色球和小的蓝色球,links即圆球间的连线。

links数组中元素的属性比较简单,sourcetarget,source即连线开始与哪个节点(小球),target是结束与哪个个节点。其值表示nodes数组中节点的索引,即第几个元素。

nodes数组中元素属性比较复杂,下面解释下。

{
  "nodes":[
    {
        "name":"楼盘码",//自定义属性,节点名称
        "group":2,//分组,暂时没明白做什么用的
        "class":"node",//节点的class属性
        "tourl":"http://www.iloushi.cn/m/loupanma/",//自定义属性,节点点击后跳转的url
        "img":"...",//自定义属性,节点的图片地址,这里可以把图片base64加密后直接复制在这里
        "imgx":"-28",//自定义属性,节点的坐标x,这里取节点宽度一半的负值是为了让图片居中
        "imgy":"-28",//自定义属性,节点的坐标y,这里取节点高度一半的负值是为了让图片居中
        "w":"56",//节点的宽度
        "h":"56"//节点的高度
    },
  ]
}

数据文件中定义了有几个节点几条线,哪两个节点相连,节点的图片。至于连接线的长短是根据节点之间的作用力强弱自动算出来的,所以如果连线过于集中导致最终小球都挤在一起的话,可以调整小球之间的连线。下面就是用过d3读出数据文件,然后根据数据绘制页面了。

再读数据之前先初始化作用力,利用d3.layout.force中的方法设置作用力。

var wWidth = window.innerWidth,
wHeight = window.innerHeight;
//在body下新建svg元素并设置宽高为屏幕宽高
var svg = d3.select("body").append("svg").attr("width", wWidth).attr("height", wHeight).attr("style", "margin: 0 auto;display: block;");
//初始化作用力并设置参数
var force = d3.layout.force()
              .gravity(.05)//引力强度
              .distance(100)
              .charge( - 250)//节点的电荷数.(电荷数决定结点是互相排斥还是吸引)
              .theta(.01)//电荷间互相作用的强度
              .size([wWidth, wHeight]);//作用力的布局宽高

设置好引力、引力和斥力后就要读取数据了。

//从data.json中读取节点数据
d3.json("data.json",function(error,data) {
    //绘制节点间连线
    var links = svg.selectAll(".link")
                   .data(data.links)//将data.json中的连线数据传入svg中
                   .enter()//进入每条连接线
                   .append("line")//将连接线绘制成line元素
                   .attr("class", "link");
    //绘制节点
    var nodes = svg.selectAll(".node")
                   .data(data.nodes)//将data.json中的节点数据传入svg中
                   .enter()//进入每个节点
                   .append("g")//将节点绘制成g(分组)元素
                   .attr("class", "node")
                   .call(force.drag)//让节点相应作用力中的拖拽
                   .append("image")//在每个g元素下新建image元素
                   .attr("xlink:href",function(e) {return e.img})//将image元素的href属性设置为data.json中的img
                   .attr("tourl",function(e) {return e.tourl})//将image元素的tourl属性设置为data.json中的tourl
                   .attr("x",function(e) {return e.imgx})//将image元素的x属性设置为data.json中的imgx
                   .attr("y",function(e) {  return e.imgy})//将image元素的y属性设置为data.json中的imgy
                   .attr("width",function(e) {return e.w})//将image元素的width属性设置为data.json中的w
                   .attr("height",  function(e) {return e.h });//将image元素的height属性设置为data.json中的h
});

运行代码:

由于还没有将数据填入force(作用力),所有的球都挤在了一起。我们用d3.json从文件中读取数据,回调中的data即为文件中的数据。svg为我们新建的svg画布,d3的链式调用很神奇,比如在还没有将node添加进去之前就可以使用svg.selectAll(".node")选择节点了,看起来是先选择节点再建立节点,根据官方的api应该是调用data()后再调用enter()可以选择按照data新建元素的占位符,也就是说只是占位符还没有真的建立元素。可能运行时不一定是按顺序执行,其内部采用回调的方式执行,如果熟悉d3的朋友可以给我留言这是什么原理,十分感谢。还有一个神奇的地方是attr()方法,第一个参数是属性名称,第二个参数是回调函数,在回调中调用函数的参数e就可以获得这个节点的信息,即nodes数组中单个元素,这样就省去了遍历数组。所以整个代码没有循环也把数据中的数组一一添加到了svg画布中,整段代码显得十分整洁。在链式调用中可以使用append()添加svg标签,然后使用attr()设置属性,这样就可以在节点中任意添加元素了。

上面只是把节点添加到svg中绘制出来,为了让节点可以移动我们还要把节点添加到force里面去,这样物理引擎与渲染引擎一一对应就可以联动起来了。

//将节点传入作用力
force.nodes(data.nodes)
     .links(data.links)
     .start();
//让节点在每一帧都根据作用力的变化重新绘制
force.on("tick",function() {
    links.attr("x1",function(e) {return e.source.x})//将x1设置为前节点的x
         .attr("y1",    function(e) {return e.source.y})//将y1设置为前节点的y
         .attr("x2",    function(e) {return e.target.x})//将x2设置为后节点的x
         .attr("y2",    function(e) {return e.target.y});//将y2设置为后节点的y
    nodes.attr("transform",function(e) {return "translate(" + e.x + "," + e.y + ")"});//更新节点的x,y
});

其中的tick事件表示每一帧类似于requestAnimationFrame。这样整个页面就根据作用力的约束动起来了。

时间仓促未作详细测试,如有任何bug请在Issues中提出。

项目地址github

如有问题或者建议请微博@UED天机。我会及时回复

更多教程请关注ued.sexy

时间: 2024-11-15 14:44:03

作用力和小球的相关文章

小tips:用java模拟小球做抛物线运动

这几天刚刚学习了java线程,然后跟着书做了几个关于线程的练习,其中有一个练习题是小球动起来.这个相信很简单,只要运用线程就轻松能够实现.然后看到了它的一个课后思考题,怎样让小球做个抛物线运动,这点我调试了很久,上网也找了很多资料,但是没有那种适合我们小白的教程,于是我简单讲讲我的思路.我将从如何画一个小球开始到一步步实现小球进行抛物线运动.嘿嘿!! 一.任务分解 现在我们来做抛物线运动的小球,先不急写代码,先来想象一下做好的结果是什么样子的.我将这个任务分成三步,第一步,画一个小球:第二步,该

完美世界笔试题---小球弹性碰撞

题目描述: 时间限制:c/c++语言1000MS: 其它语言3000MS 内存限制:c/c++语言65536KB:其他语言589824KB 如下图一个类似手机屏幕的矩形区域,宽度为w,高度为h,一个小球(视为质点,忽略其体积大小)初始位于底边距离左侧x的位置,向右上角45度发射.当小球碰到边界时,按完全弹性碰撞理想反弹,如果小球恰好碰到角落,则反向返回.如此无限循环. 请编写程序,输出前n次小球回到底边时的横坐标(首次发射时的不算). 输入: 每个输入是一行依次表示为w,h,x,n的4个正整数,

[bzoj1867][Noi1999][钉子和小球] (动态规划)

Description Input 第1行为整数n(2<=n<=50)和m(0<=m<=n).以下n行依次为木板上从上至下n行钉子的信息,每行中‘*’表示钉子还在,‘.’表示钉子被拔去,注意在这n行中空格符可能出现在任何位置. Output 仅一行,是一个既约分数(0写成0/1),为小球落在编号为m的格子中的概pm.既约分数的定义:A/B是既约分数,当且仅当A.B为正整数且A和B没有大于1的公因子. Sample Input 5 2 Sample Output 7/16 Solut

UVa 679小球下落(二叉树的编号)

题意:二叉树按层次遍历从1开始标号,所有叶子结点深度相同,每个结点开关初始状态皆为关闭,小球从根结点开始下落(小球落在结点开关上会使结点开关状态改变),若结点开关关闭,则小球往左走,否则往右走,给定二叉树深度D,求第I个小球所落到的叶子结点编号. 分析:对于每一个结点,若小球当前编号为k, k为奇数,则这个小球是第(k+1)/2个落在此处的,则往左走; k为偶数,则这个小球是第k/2个落在此处的,则往右走; 从根结点到叶子结点需判断D-1次. 比如,假如I为15,则这是第15个落在根结点的小球,

canvas 模拟小球上抛运动的物理效果

最近一直想用学的canvas做一个漂亮的小应用,但是,发现事情并不是想的那么简单.比如,游戏的逼真效果,需要自己来coding…… 所以,自己又先做了一个小demo,算是体验一下亲手打造物理引擎的感觉吧.*_* 代码效果预览地址:http://code.w3ctech.com/detail/2524 html: 1 <div class="container"> 2 <canvas id="canvas" style="border:1p

Windows Store App JavaScript 开发:小球运动示例

通过前面内容的学习,相信读者已经对开发基于JavaScript的Windows应用商店应用有了一定的了解,本小节通过一个小球运动的示例来介绍如何新建一个JavaScript的Windows应用商店项目,以及怎样向项目中添加功能实现代码. 首先启动Visual Studio 2012集成开发工具,打开"文件"菜单,选择其中的"新建项目"菜单项后会弹出"新建项目"窗口.在"新建项目"窗口左侧的JavaScript模板分类中选择&q

coco2d-js demo程序之滚动的小球

最近有一个游戏叫围住神经猫,报道说是使用html5技术来做的. html5的跨平台的优良特性很不错,对于人手不足,技术不足,选用html5技术实现跨平台的梦想真是不错. 最近在看coco2d-js这个跨平台游戏开发框架,很不错,写了一个demo程序供大家参考. /** * Created by caicai on 14-7-27. */ var Ball = cc.Sprite.extend({ velocity:null, ctor:function () { this._super(res.

vue.js加入购物车小球动画

http://www.cnblogs.com/yuxingyoucan/p/7063881.html 生成一个动画小球的div,并且生成五个小球,五个是为了生成一定数量的小球来作为操作使用,按照小球动画的速度,一般来说五个也可以保证有足够的小球数量来运行动画 动画的内容分别是外层和内层,外层控制动画小球的轨道和方向,内层控制动画小球的运行状态 动画使用vue的js钩子实现 因为小球动画只有一个方向(只执行单方向从上到下滚落),所以只用了before-enter,enter,after-enter

如何用面对对象来做一个躁动的小球?

今天来看看怎样用面对对象来做一个躁动的小球. 首先我们先创建一个对象,他的属性包含小球的随机水平.纵向坐标,随机宽.高,随机颜色,以及创建小球的方法. html: <div id="wrap"></div> js: function Boll(x,y,w,h,color){ // 随机宽高 var wh = randFn(5, 40); // 随机颜色 var c = 'rgb('+randFn(0, 255)+',' + randFn(0,255)+','+r