• [Web Chart系列之七] 物理动画效果(如撕扯效果)


    物理动画

    这里物理动画说的是,图形的动画效果和实际生活中的物理效果类似,比如说重力影响的效果。

    其实动画的实现就是把一个动作拆分成间隔连续的去完成, 视觉上就感觉是在动的了。

    比如把一个图从左边移到右边, 设总距离100的话, 每隔 1/40秒 移到 1 的话,效果就会有了。

    这个时间间隔和距离间隔设置多少为最佳,美学上是有一些定义的,这里就不探讨了。


    一个拽动和撕扯窗布的例子

    三个文件
      tearCloth.html ;  tearCloth.css ; tearCloth.js

    tearCloth.html
    <!--Add by oscar999-->
    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
    <HTML>
    <HEAD>
    <TITLE> New Document </TITLE>
    <META NAME="Author" CONTENT="oscar999">
    <link rel="stylesheet" type="text/css" href="tearCloth.css" />
    </HEAD>
    
    <BODY>
    <canvas id = "c" > </canvas>  
    <div id="info">
      <div id="top">
      <a id="close" href="">close</a>
      </div>
      <p>
        <br>
        - Tear the cloth with your mouse.<br><br>
        - Right click and drag to cut the cloth<br><br>
        - Reduce physics_accuracy if it's laggy.<br><br>
      </p>
    </div>
    
    <script src="tearCloth.js"></script>
    </BODY>
    </HTML>
    
    
    
    
      
      

    tearCloth.css
    * {
    	 margin: 0;
      overflow:hidden;
      -webkit-user-select: none;
      -moz-user-select: none;
      -ms-user-select: none;
      -o-user-select: none;
      user-select: none;  
    }
    
    body {
      background:#333;
    }
    
    canvas {
      background:#333;
      100%;
      height:376px;
      margin:0 auto;
      display:block;
    }
    
    #info {
      position:absolute;
      left:-1px;
      top:-1px;
      auto;
      max-380px;
      height:auto;
      background:#f2f2f2;
      border-bottom-right-radius:10px;
    }
    
    #top {
      background:#fff;
      100%;
      height:auto;
      position:relative;
      border-bottom:1px solid #eee;
    }
    
    p {
      font-family:Arial, sans-serif;
      color:#666;
      text-align:justify;
      font-size: 16px;
      margin:10px;
    }
    
    a {
      font-family:sans-serif;
      color:#444;
      text-decoration:none;
      font-size: 20px;
    }
    
    #site {
      float:left;
      margin: 10px;
      color: #38a;
      border-bottom:1px dashed #888;
    }
    
    #site:hover {
      color: #7af;
    }
    
    #close {
      float:right;
      margin: 10px;
    }
    
    #p {
      font-family: Verdana, sans-serif;
      position:absolute;
      right:10px;
      bottom:10px;
      color:#adf;
      border: 1px dashed #555;
      padding:4px 8px;
    }

    tearCloth.js
    document.getElementById('close').onmousedown = function(e) {
      e.preventDefault();
      document.getElementById('info').style.display = 'none';
      return false;
    };
    
    // settings
    
    var physics_accuracy = 6,
    mouse_influence      = 20, 
    mouse_cut            = 5,
    gravity              = 900, 
    cloth_height         = 30,
    cloth_width          = 50,
    start_y              = 20,
    spacing              = 7,
    tear_distance        = 60;
    
    
    window.requestAnimFrame =
    window.requestAnimationFrame       ||
    window.webkitRequestAnimationFrame ||
    window.mozRequestAnimationFrame    ||
    window.oRequestAnimationFrame      ||
    window.msRequestAnimationFrame     ||
    function(callback) {
        window.setTimeout(callback, 1000 / 60);
    };
    
    var canvas,
    	ctx,
    	cloth,
    	boundsx,
    	boundsy,
    	mouse = {
    		down: false,
    		button: 1,
    		x: 0,
    		y: 0,
    		px: 0,
    		py: 0
    	};
    
    window.onload = function() {
    
    	canvas = document.getElementById('c');
    	ctx    = canvas.getContext('2d');
    
    	canvas.width = canvas.clientWidth;
    	canvas.height = 376;
    
    	canvas.onmousedown = function(e) {
    		mouse.button = e.which;
    		mouse.px = mouse.x;
    		mouse.py = mouse.y;
      var rect = canvas.getBoundingClientRect();
      mouse.x = e.clientX - rect.left,
      mouse.y = e.clientY - rect.top,
    		mouse.down = true;
    		e.preventDefault();
    	};
    
    	canvas.onmouseup = function(e) {
    		mouse.down = false;
    		e.preventDefault();
    	};
    
    	canvas.onmousemove = function(e) {
    		mouse.px = mouse.x;
    		mouse.py = mouse.y;
    		var rect = canvas.getBoundingClientRect();
      mouse.x = e.clientX - rect.left,
      mouse.y = e.clientY - rect.top,
    		e.preventDefault();
    	};
    
    	canvas.oncontextmenu = function(e) {
    		e.preventDefault(); 
    	};
    
    	boundsx = canvas.width - 1;
    	boundsy = canvas.height - 1;
    
    	ctx.strokeStyle = 'rgba(222,222,222,0.6)';
    	cloth = new Cloth();
    	update();
    };
    
    var Point = function(x, y) {
    
    	this.x = x;
    	this.y = y;
    	this.px = x;
    	this.py = y;
    	this.vx = 0;
    	this.vy = 0;
    	this.pin_x = null;
    	this.pin_y = null;
    	this.constraints = [];
    };
    
    Point.prototype.update = function(delta) {
    
    	if (mouse.down) {
    
    		var diff_x = this.x - mouse.x,
    			diff_y = this.y - mouse.y,
    			dist   = Math.sqrt(diff_x * diff_x + diff_y * diff_y);
    
    		if (mouse.button == 1) {
    
    			if(dist < mouse_influence) {
    				this.px = this.x - (mouse.x - mouse.px) * 1.8;
    				this.py = this.y - (mouse.y - mouse.py) * 1.8;
    			}
    
    		} else if (dist < mouse_cut) this.constraints = [];
    	}
    
    	this.add_force(0, gravity);
    
    	delta *= delta;
    	nx = this.x + ((this.x - this.px) * .99) + ((this.vx / 2) * delta);
    	ny = this.y + ((this.y - this.py) * .99) + ((this.vy / 2) * delta);
    
    	this.px = this.x;
    	this.py = this.y;
    
    	this.x = nx;
    	this.y = ny;
    
    	this.vy = this.vx = 0
    };
    
    Point.prototype.draw = function() {
    
    	if (this.constraints.length <= 0) return;
    	
    	var i = this.constraints.length;
    	while(i--) this.constraints[i].draw();
    };
    
    Point.prototype.resolve_constraints = function() {
    
    	if (this.pin_x != null && this.pin_y != null) {
    	
    		this.x = this.pin_x;
    		this.y = this.pin_y;
    		return;
    	}
    
    	var i = this.constraints.length;
    	while(i--) this.constraints[i].resolve();
    
    	this.x > boundsx ? this.x = 2 * boundsx - this.x : 1 > this.x && (this.x = 2 - this.x);
    	this.y < 1 ? this.y = 2 - this.y : this.y > boundsy && (this.y = 2 * boundsy - this.y);
    };
    
    Point.prototype.attach = function(point) {
    
    	this.constraints.push(
    		new Constraint(this, point)
    	);
    };
    
    Point.prototype.remove_constraint = function(lnk) {
    
    	var i = this.constraints.length;
    	while(i--) if(this.constraints[i] == lnk) this.constraints.splice(i, 1);
    };
    
    Point.prototype.add_force = function(x, y )  {
    
    	this.vx += x;
    	this.vy += y;
    };
    
    Point.prototype.pin = function(pinx, piny) {
    	this.pin_x = pinx;
    	this.pin_y = piny;
    };
    
    var Constraint = function(p1, p2) {
    
    	this.p1 = p1;
    	this.p2 = p2;
    	this.length = spacing;
    };
    
    Constraint.prototype.resolve = function() {
    
    	var diff_x = this.p1.x - this.p2.x,
    		diff_y = this.p1.y - this.p2.y,
    		dist = Math.sqrt(diff_x * diff_x + diff_y * diff_y),
    		diff = (this.length - dist) / dist;
    
    	if (dist > tear_distance) this.p1.remove_constraint(this);
    
    	var px = diff_x * diff * 0.5;
    	var py = diff_y * diff * 0.5;
    
    	this.p1.x += px;
    	this.p1.y += py;
    	this.p2.x -= px;
    	this.p2.y -= py;
    };
    
    Constraint.prototype.draw = function() {
    
    	ctx.moveTo(this.p1.x, this.p1.y);
    	ctx.lineTo(this.p2.x, this.p2.y);
    };
    
    var Cloth = function() {
    
    	this.points = [];
    
    	var start_x = canvas.width / 2 - cloth_width * spacing / 2;
    
    	for(var y = 0; y <= cloth_height; y++) {
    
    		for(var x = 0; x <= cloth_width; x++) {
    
    			var p = new Point(start_x + x * spacing, start_y + y * spacing);
    
       x != 0 && p.attach(this.points[this.points.length - 1]);
    			y == 0 && p.pin(p.x, p.y);
    			y != 0 && p.attach(this.points[x + (y - 1) * (cloth_width + 1)])
    
    			this.points.push(p);
    		}
    	}
    };
    
    Cloth.prototype.update = function() {
    
    	var i = physics_accuracy;
    
    	while(i--) {
    		var p = this.points.length;
    		while(p--) this.points[p].resolve_constraints();
    	}
    
    	i = this.points.length;
    	while(i--) this.points[i].update(.016);
    };
    
    Cloth.prototype.draw = function() {
    
    	ctx.beginPath();
    
    	var i = cloth.points.length;
    	while(i--) cloth.points[i].draw();
    
    	ctx.stroke();
    };
    
    function update() {
    
    	ctx.clearRect(0, 0, canvas.width, canvas.height);
    
    	cloth.update();
    	cloth.draw();
    
    	requestAnimFrame(update);
    }

    打开html 文件就可以看到效果了,最好是在Chrome和Firefox 下查看效果。
    也可以通过以下link 在线看效果:
    http://lonely-pixel.com/lab/cloth/


    其他的例子


    除了以上撕扯窗布的效果,还可以看一些更多的类似效果

    http://lonely-pixel.com/


    框架

    在这个JS框架横行的年代,你有可能会问,有没有一个JS框架,让让我很容易的实现这些效果。

    答案是肯定的: 有,

    verlet-js  就是比较好一个。

    verlet-js的定位就是一个简单的使用javascript实现的物理引擎。

    官方的网址是:

    http://subprotocol.com/verlet-js/


    可以看的例子有。

    Examples

    1. Shapes (Hello world)
    2. Fractal Trees
    3. Cloth
    4. Spiderweb

    那个蜘蛛在网上爬行的例子很炫。。



  • 相关阅读:
    前端开发资料、实用小工具
    冒泡排序法,二分查找法
    数组练习
    一维数组练习题
    for循环输出菱形
    练习题
    课堂练习
    gO语言的安装和环境变量的配置
    PO BO VO DTO POJO DAO 概念及其作用
    BaseServlet
  • 原文地址:https://www.cnblogs.com/javawebsoa/p/3041087.html
Copyright © 2020-2023  润新知