javascript仿天貓加入購物車動畫效果
注意:首先需要宣告的是:程式碼原思路不是我寫的,是在網上找的這種效果,自己使用程式碼封裝了下而已;程式碼中都有註釋,我們最主要的是理解拋物線的思路及在工作中完成這樣的任務,最近需要做類似於天貓加入購物車動畫效果,所以就在網上搜尋了下,就看到類似的效果,就把程式碼截下來自己封裝了下~~
如果想要了解拋物線的細節,我建議大家先 看下 張鑫旭 講解的拋物線的文章,再來看如下JS程式碼,可能理解更深點~~
首先我們來理解的是: 既然是拋物運動,那麼 運動的元素肯定需要 "絕對定位";配置的引數有如下:(依賴於Jquery或者zepto.js)
配置項 | 含義 |
el | 需要運動的元素 {object | string} 預設為null |
offset | 運動的元素在 X軸,Y軸的偏移位置 |
targetEl | 終點目標元素 這時就會自動獲取該元素的left、top值,來表示移動元素在X,Y軸的偏移位置;設定了這個引數,offset將失效 |
duration | 運動時間,預設為500毫秒 |
curvature | 拋物線曲率,就是彎曲的程度,越接近於0越像直線,預設0.001 |
callback | 運動完成後執行的回撥函式 |
autostart | 是否自動開始運動,預設為false |
stepCallback | 運動過程中執行的回撥函式,this指向該物件,接受x,y引數,分別表示X,Y軸的偏移位置。 |
JSFiddler效果如下:
下面是所有的JS程式碼:
/* * 實現拋物線函式 Parabola * 切記既然是拋物線運動,那麼運動的元素需要絕對定位 * 具體瞭解拋物線 可以看張鑫旭的文章 * http://www.zhangxinxu.com/wordpress/2013/12/javascript-js-%E5%85%83%E7%B4%A0-%E6%8A%9B%E7%89%A9%E7%BA%BF-%E8%BF%90%E5%8A%A8-%E5%8A%A8%E7%94%BB/ */ var Parabola = function(opts){ this.init(opts); }; Parabola.prototype = { constructor: Parabola, /* * @fileoverview 頁面初始化 * @param opts {Object} 配置引數 */ init: function(opts){ this.opts = $.extend(defaultConfig, opts || {}); // 如果沒有運動的元素 直接return if(!this.opts.el) { return; } // 取元素 及 left top this.$el = $(this.opts.el); this.$elLeft = this._toInteger(this.$el.css("left")); this.$elTop = this._toInteger(this.$el.css("top")); // 計算x軸,y軸的偏移量 if(this.opts.targetEl) { this.diffX = this._toInteger($(this.opts.targetEl).css("left")) - this.$elLeft; this.diffY = this._toInteger($(this.opts.targetEl).css("top")) - this.$elTop; }else { this.diffX = this.opts.offset[0]; this.diffY = this.opts.offset[1]; } // 運動時間 this.duration = this.opts.duration; // 拋物線曲率 this.curvature = this.opts.curvature; // 計時器 this.timerId = null; /* * 根據兩點座標以及曲率確定運動曲線函式(也就是確定a, b的值) * 公式: 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.diffY - this.curvature * this.diffX * this.diffX) / this.diffX; // 是否自動運動 if(this.opts.autostart) { this.start(); } }, /* * @fileoverview 開始 */ start: function(){ // 開始運動 var self = this; // 設定起始時間 和 結束時間 this.begin = (new Date()).getTime(); this.end = this.begin + this.duration; // 如果目標的距離為0的話 就什麼不做 if(this.diffX === 0 && this.diffY === 0) { return; } if(!!this.timerId) { clearInterval(this.timerId); this.stop(); } // 每幀(對於大部分螢幕)大約16~17毫秒。預設大小是166.67。也就是預設10px/ms this.timerId = setInterval(function(){ var t = (new Date()).getTime(); self.step(t); },16); return this; }, /* * @fileoverview 執行每一步 * @param {string} t 時間 */ step: function(t){ var opts = this.opts; var x, y; // 如果當前執行的時間大於結束的時間 if(t > this.end) { // 執行結束 x = this.diffX; y = this.diffY; this.move(x,y); this.stop(); // 結束後 回撥 if(typeof opts.callback === 'function') { opts.callback.call(this); } }else { // 每一步x軸的位置 x = this.diffX * ((t - this.begin) / this.duration); // 每一步y軸的位置 y = a * x *x + b*x + c; c = 0 y = this.curvature * x * x + this.b * x; // 移動 this.move(x,y); if(typeof opts.stepCallback === 'function') { opts.stepCallback.call(this,x,y); } } return this; }, /* * @fileoverview 給元素定位 * @param {x,y} x,y座標 * @return this */ move: function(x,y) { this.$el.css({ "position":'absolute', "left": this.$elLeft + x + 'px', "top": this.$elTop + y + 'px' }); return this; }, /* * 獲取配置項 * @param {object} options配置引數 * @return {object} 返回配置引數項 */ getOptions: function(options){ if(typeof options !== "object") { options = {}; } options = $.extend(defaultConfig, options || {}); return options; }, /* * 設定options * @param options */ setOptions: function(options) { this.reset(); if(typeof options !== 'object') { options = {}; } options = $.extend(this.opts,options); this.init(options); return this; }, /* * 重置 */ reset: function(x,y) { this.stop(); x = x ? x : 0; y = y ? y : 0; this.move(x,y); return this; }, /* * 停止 */ stop: function(){ if(!!this.timerId){ clearInterval(this.timerId); } return this; }, /* * 變成整數 * isFinite() 函式用於檢查其引數是否是無窮大。 */ _toInteger: function(text){ text = parseInt(text); return isFinite(text) ? text : 0; } }; var defaultConfig = { //需要運動的元素 {object | string} el: null, // 運動的元素在 X軸,Y軸的偏移位置 offset: [0,0], // 終點元素 targetEl: null, // 運動時間,預設為500毫秒 duration: 500, // 拋物線曲率,就是彎曲的程度,越接近於0越像直線,預設0.001 curvature: 0.01, // 運動後執行的回撥函式 callback: null, // 是否自動開始運動,預設為false autostart: false, // 運動過程中執行的回撥函式,this指向該物件,接受x,y引數,分別表示X,Y軸的偏移位置。 stepCallback: null };
HTML程式碼如下:
<div class="btns" style="margin-top:20px"> <a href="#" id="reset">重置</a> <a href="#" id="run">開始運動</a> <a href="#" id="stop">停止運動</a> <a href="#" id="setOptions">設定配置引數</a> </div> <div id="boll" class="boll"></div> <div id="target" class="target"></div>
JS初始化如下方式:
var bool = new Parabola({ el: "#boll", offset: [500, 100], duration: 500, curvature: 0.005, callback:function(){ alert("完成後回撥") }, stepCallback:function(x,y){ $("<div>").appendTo("body").css({ "position": "absolute", "top": this.$elTop + y + 'px', "left":this.$elLeft + x + 'px', "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 }); });