原文:http://www.css88.com/archives/5355
作者的主要实现代码:
; (function () { var _$ = function (_this) { return _this.constructor == jQuery ? _this : $(_this); }; // 获取当前时间 function now() { return +new Date(); } // 转化为整数 function toInteger(text) { text = parseInt(text); return isFinite(text) ? text : 0; } var Parabola = function (options) { this.initialize(options); }; Parabola.prototype = { constructor: Parabola, /** * 初始化 * @classDescription 初始化 * @param {Object} options 插件配置 . */ initialize: function (options) { this.options = this.options || this.getOptions(options); var ops = this.options; if (!this.options.el) { return; } this.$el = _$(ops.el); this.timerId = null; this.elOriginalLeft = toInteger(this.$el.css("left")); this.elOriginalTop = toInteger(this.$el.css("top")); // this.driftX X轴的偏移总量 //this.driftY Y轴的偏移总量 if (ops.targetEl) { this.driftX = toInteger(_$(ops.targetEl).css("left")) - this.elOriginalLeft; this.driftY = toInteger(_$(ops.targetEl).css("top")) - this.elOriginalTop; } else { this.driftX = ops.offset[0]; this.driftY = ops.offset[1]; } this.duration = ops.duration; // 处理公式常量 this.curvature = ops.curvature; // 根据两点坐标以及曲率确定运动曲线函数(也就是确定a, b的值) //a=this.curvature /* 公式: y = a*x*x + b*x + c; */ /* * 因为经过(0, 0), 因此c = 0 * 于是: * y = a * x*x + b*x; * y1 = a * x1*x1 + b*x1; * y2 = a * x2*x2 + b*x2; * 利用第二个坐标: * b = (y2+ a*x2*x2) / x2 */ // 于是 this.b = ( this.driftY - this.curvature * this.driftX * this.driftX ) / this.driftX; //自动开始 if (ops.autostart) { this.start(); } }, /** * 初始化 配置参数 返回参数MAP * @param {Object} options 插件配置 . * @return {Object} 配置参数 */ getOptions: function (options) { if (typeof options !== "object") { options = {}; } options = $.extend({}, defaultSetting, _$(options.el).data(), (this.options || {}), options); return options; }, /** * 定位 * @param {Number} x x坐标 . * @param {Object} y y坐标 . * @return {Object} this */ domove: function (x, y) { this.$el.css({ position: "absolute", left: this.elOriginalLeft + x, top: this.elOriginalTop + y }); return this; }, /** * 每一步执行 * @param {Data} now 当前时间 . * @return {Object} this */ step: function (now) { var ops = this.options; var x, y; if (now > this.end) { // 运行结束 x = this.driftX; y = this.driftY; this.domove(x, y); this.stop(); if (typeof ops.callback === 'function') { ops.callback.call(this); } } else { //x 每一步的X轴的位置 x = this.driftX * ((now - this.begin) / this.duration); //每一步的Y轴的位置y = a*x*x + b*x + c; c==0; y = this.curvature * x * x + this.b * x; this.domove(x, y); if (typeof ops.stepCallback === 'function') { ops.stepCallback.call(this); } } return this; }, /** * 设置options * @param {Object} options 当前时间 . */ setOptions: function (options) { this.reset(); if (typeof options !== "object") { options = {}; } this.options = this.getOptions(options); this.initialize('parabola', this.options); return this; }, /** * 开始 */ start: function () { var self = this; // 设置起止时间 this.begin = now(); this.end = this.begin + this.duration; if (this.driftX === 0 && this.driftY === 0) { // 原地踏步就别浪费性能了 return; } /*timers.push(this); Timer.start();*/ if (!!this.timerId) { clearInterval(this.timerId); this.stop(); } this.timerId = setInterval(function () { var t = now(); self.step(t); }, 13); return this; }, /** * 重置 */ reset: function (x, y) { this.stop(); x = x ? x : 0; y = y ? y : 0; this.domove(x, y); return this; }, /** * 停止 */ stop: function () { if (!!this.timerId) { clearInterval(this.timerId); } return this; } }; var defaultSetting = { el: null, //偏移位置 offset: [0, 0], //终点元素,这时就会自动获取该元素的left、top,设置了这个参数,offset将失效 targetEl: null, //运动的时间,默认500毫秒 duration: 500, //抛物线曲率,就是弯曲的程度,越接近于0越像直线,默认0.001 curvature: 0.001, //运动后执行的回调函数 callback: null, // 是否自动开始,默认为false autostart: false, //运动过程中执行的回调函数 stepCallback: null }; window.Parabola = Parabola; })();使用:
var bool = new Parabola({ el: "#boll", offset: [500, 100], curvature: 0.005, duration: 3000, callback: function () { alert("完成后回调") }, stepCallback: function (x, y) { console.log(x, y); $("<div>").appendTo("body").css({ "position": "absolute", "top": this.elOriginalTop + y, "left": this.elOriginalLeft + x, "background-color": "#CDCDCD", "width": "5px", "height": "5px", "border-radius": "5px" }); } });demo地址: http://www.css88.com/demo/parabola/index.html
<!DOCTYPE HTML> <html> <head> <meta charset="utf-8"> <title>抛物线运动效果</title> <style type="text/css"> .boll { width: 50px; height: 50px; background-color: #ff3333; position: absolute; top: 380px; left: 100px; -moz-border-radius: 50px; -webkit-border-radius: 50px; border-radius: 50px; } .target { width: 50px; height: 50px; background-color: #CDCDCD; position: absolute; top: 180px; left: 600px; -moz-border-radius: 50px; -webkit-border-radius: 50px; border-radius: 50px; } </style> <script type="text/javascript" src="http://www.css88.com/jqapi-1.9/js/jquery.min.js"></script> <script type="text/javascript" src="js/parabola.js"></script> </head> <body> <div class="btns" style="margin-top:20px"> <a href="http://www.css88.com/archives/5355" target="_blank">回到JavaScript实现的抛物线运动效果</a> </div> <div class="btns" style="margin-top:20px"> <a href="#" class="btnA btn-danger" id="reset" rel="popover" title="A Title" style="">reset</a> <a href="#" class="btnA btn-danger" id="run" rel="popover" title="A Title" style="">run</a> <a href="#" class="btnA btn-danger" id="stop" rel="popover" title="A Title" style="">stop</a> <a href="#" class="btnA btn-danger" id="setOptions" rel="popover" title="A Title" style="">setOptions</a> </div> <div id="boll" class="boll"></div> <div id="target" class="target"></div> <script type="text/javascript"> var bool = new Parabola({ el: "#boll", offset: [500, 100], curvature: 0.005, duration: 3000, callback:function(){ alert("完成后回调") }, stepCallback:function(x,y){ console.log(x,y); $("<div>").appendTo("body").css({ "position": "absolute", "top": this.elOriginalTop + y, "left":this.elOriginalLeft + x, "background-color":"#CDCDCD", "width":"5px", "height":"5px", "border-radius": "5px" }); } }); $("#reset").click(function (event) { event.preventDefault(); bool.reset() }); $("#run").click(function (event) { event.preventDefault(); bool.start(); }); $("#stop").click(function (event) { event.preventDefault(); bool.stop(); }); $("#setOptions").click(function (event) { event.preventDefault(); bool.setOptions({ targetEl: $("#target"), curvature: 0.001, duration: 1000 }); }); </script> </body> </html>
<script src="jquery.js"></script> <script src="dist/jquery.fly.min.js"></script> <script> jQuery(function($) { $('#fly').fly({ start:{ left: 11, //开始位置(必填)#fly元素会被设置成position: fixed top: 600, //开始位置(必填) }, end:{ left: 500, //结束位置(必填) top: 130, //结束位置(必填) width: 100, //结束时高度 height: 100, //结束时高度 }, autoPlay: false, //是否直接运动,默认true speed: 1.1, //越大越快,默认1.2 vertex_Rtop:100, //运动轨迹最高点top值,默认20 onEnd: function(){} //结束回调 }); $('#fly').play(); //autoPlay: false后,手动调用运动 $('#fly').destory(); //移除dom }); </script>IE10以下,引入src/requestAnimationFrame.js
(function () { var lastTime = 0; var vendors = ['webkit', 'moz']; for (var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) { window.requestAnimationFrame = window[vendors[x] + 'RequestAnimationFrame']; window.cancelAnimationFrame = window[vendors[x] + 'CancelAnimationFrame'] || window[vendors[x] + 'CancelRequestAnimationFrame']; } if (!window.requestAnimationFrame){ window.requestAnimationFrame = function (callback, element) { var currTime = new Date().getTime(); var timeToCall = Math.max(0, 16 - (currTime - lastTime)); var id = window.setTimeout(function () { callback(currTime + timeToCall); }, timeToCall); lastTime = currTime + timeToCall; return id; }; } if (!window.cancelAnimationFrame){ window.cancelAnimationFrame = function (id) { clearTimeout(id); }; } }());