JavaScript實(shí)現(xiàn)的拋物線運(yùn)動(dòng)效果 – WEB前端開發(fā)
最近做購物車功能,看到天貓上的購物車有元素拋物線運(yùn)動(dòng)效果,所以也想湊熱鬧實(shí)現(xiàn)一個(gè)。
網(wǎng)上搜索了一下,看了一下張?chǎng)涡竦摹? 小折騰:JavaScript與元素間的拋物線軌跡運(yùn)動(dòng) 》,原理張?chǎng)涡褚呀?jīng)講的很清楚了,多說了也沒什么意思,就是數(shù)學(xué)公式。不過看代碼個(gè)人覺得有點(diǎn)變扭,那不是我的習(xí)慣,所以自己重新寫了一個(gè)。

先看demo: http://www.css88.com/demo/parabola/index.html
如何使用:
運(yùn)動(dòng)位移的元素必須設(shè)置為
position: absolute
,通過絕對(duì)定位控制
left
,
top
來實(shí)現(xiàn)的;
首先你可以
new
一個(gè)對(duì)象:
var bool = new Parabola({ el: "#boll", offset: [500, 100], curvature: 0.005, duration: 3000, callback: function () { alert("完成后回調(diào)") }, 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" }); } });
參數(shù)說明:
參數(shù)名 | 數(shù)據(jù)類型 | 默認(rèn)值 | 描述 |
---|---|---|---|
el | jQuery||String(選擇器) | null | 必須填寫的參數(shù),要移動(dòng)的元素,可以是jQuery對(duì)象或選擇器 |
offset | Array | [0, 0] | 表示移動(dòng)元素在X,Y軸的偏移位置,設(shè)置了targetEl參數(shù)后,該參數(shù)將失效 |
targetEl | jQuery||String(選擇器) | null | 終點(diǎn)元素,這時(shí)就會(huì)自動(dòng)獲取該元素的left、top值,來表示移動(dòng)元素在X,Y軸的偏移位置;設(shè)置了這個(gè)參數(shù),offset將失效 |
duration | Number | 500 | 運(yùn)動(dòng)的時(shí)間,默認(rèn)500毫秒 |
curvature | Number | 0.001 | 拋物線曲率,就是彎曲的程度,越接近于0越像直線,默認(rèn)0.001 |
callback | Function | null | 運(yùn)動(dòng)后執(zhí)行的回調(diào)函數(shù),this指向該對(duì)象 |
stepCallback | Function | null | 運(yùn)動(dòng)過程中執(zhí)行的回調(diào)函數(shù),this指向該對(duì)象,接受x,y參數(shù),分別表示X,Y軸的偏移位置。 |
autostart | Boolean | false | 是否自動(dòng)開始運(yùn)動(dòng),默認(rèn)為false |
方法:
.reset()
重置元素的位置
.start()
開始執(zhí)行動(dòng)畫
.stop()
停止動(dòng)畫
.setOptions(options)
重置options參數(shù)
先看demo: http://www.css88.com/demo/parabola/index.html
JS代碼:
; (function () { var _$ = function (_this) { return _this.constructor == jQuery ? _this : $(_this); }; // 獲取當(dāng)前時(shí)間 function now() { return +new Date(); } // 轉(zhuǎn)化為整數(shù) 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; // 根據(jù)兩點(diǎn)坐標(biāo)以及曲率確定運(yùn)動(dòng)曲線函數(shù)(也就是確定a, b的值) //a=this.curvature /* 公式: y = a*x*x + b*x + c; */ /* * 因?yàn)榻?jīng)過(0, 0), 因此c = 0 * 于是: * y = a * x*x + b*x; * y1 = a * x1*x1 + b*x1; * y2 = a * x2*x2 + b*x2; * 利用第二個(gè)坐標(biāo): * b = (y2+ a*x2*x2) / x2 */ // 于是 this.b = ( this.driftY - this.curvature * this.driftX * this.driftX ) / this.driftX; //自動(dòng)開始 if (ops.autostart) { this.start(); } }, /** * 初始化 配置參數(shù) 返回參數(shù)MAP * @param {Object} options 插件配置 . * @return {Object} 配置參數(shù) */ getOptions: function (options) { if (typeof options !== "object") { options = {}; } options = $.extend({}, defaultSetting, _$(options.el).data(), (this.options || {}), options); return options; }, /** * 定位 * @param {Number} x x坐標(biāo) . * @param {Object} y y坐標(biāo) . * @return {Object} this */ domove: function (x, y) { this.$el.css({ position: "absolute", left: this.elOriginalLeft + x, top: this.elOriginalTop + y }); return this; }, /** * 每一步執(zhí)行 * @param {Data} now 當(dāng)前時(shí)間 . * @return {Object} this */ step: function (now) { var ops = this.options; var x, y; if (now > this.end) { // 運(yùn)行結(jié)束 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; }, /** * 設(shè)置options * @param {Object} options 當(dāng)前時(shí)間 . */ 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; // 設(shè)置起止時(shí)間 this.begin = now(); this.end = this.begin + this.duration; if (this.driftX === 0 && this.driftY === 0) { // 原地踏步就別浪費(fèi)性能了 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], //終點(diǎn)元素,這時(shí)就會(huì)自動(dòng)獲取該元素的left、top,設(shè)置了這個(gè)參數(shù),offset將失效 targetEl: null, //運(yùn)動(dòng)的時(shí)間,默認(rèn)500毫秒 duration: 500, //拋物線曲率,就是彎曲的程度,越接近于0越像直線,默認(rèn)0.001 curvature: 0.001, //運(yùn)動(dòng)后執(zhí)行的回調(diào)函數(shù) callback: null, // 是否自動(dòng)開始,默認(rèn)為false autostart: false, //運(yùn)動(dòng)過程中執(zhí)行的回調(diào)函數(shù) stepCallback: null }; window.Parabola = Parabola; })();