JavaScript实现的抛物线运动效果

10年服务1亿前端开发工程师

最近做购物车功能,看到天猫上的购物车有元素抛物线运动效果,所以也想凑热闹实现一个。

网上搜索了一下,看了一下张鑫旭的《小折腾:JavaScript与元素间的抛物线轨迹运动》,原理张鑫旭已经讲的很清楚了,多说了也没什么意思,就是数学公式。不过看代码个人觉得有点变扭,那不是我的习惯,所以自己重新写了一个。

先看demo:https://www.css88.com/demo/parabola/index.html

如何使用:

运动位移的元素必须设置为position: absolute,通过绝对定位控制lefttop来实现的;

首先你可以new一个对象:

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"
            });
        }
    });

参数说明:

参数名 数据类型 默认值 描述
el jQuery||String(选择器) null 必须填写的参数,要移动的元素,可以是jQuery对象或选择器
offset Array [0, 0] 表示移动元素在X,Y轴的偏移位置,设置了targetEl参数后,该参数将失效
targetEl jQuery||String(选择器) null 终点元素,这时就会自动获取该元素的left、top值,来表示移动元素在X,Y轴的偏移位置;设置了这个参数,offset将失效
duration Number 500 运动的时间,默认500毫秒
curvature Number 0.001 抛物线曲率,就是弯曲的程度,越接近于0越像直线,默认0.001
callback Function null 运动后执行的回调函数,this指向该对象
stepCallback Function null 运动过程中执行的回调函数,this指向该对象,接受x,y参数,分别表示X,Y轴的偏移位置。
autostart Boolean false 是否自动开始运动,默认为false

方法:

.reset()

重置元素的位置

.start()

开始执行动画

.stop()

停止动画

.setOptions(options)

重置options参数

先看demo:https://www.css88.com/demo/parabola/index.html

JS代码:

;
(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;
})();
赞(4) 打赏
未经允许不得转载:WEB前端开发 » JavaScript实现的抛物线运动效果

评论 4

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址
  1. #-49

    醉了,

    charkali4年前 (2014-12-10)回复
  2. #-48

    其实抛物线运动不难实现,天猫的效果实现难度在于当起始位子和终点位置不同时(包含浏览器窗体大小改变),每次运动的curvature是计算得到的。我前端时间也做了一个,可以一起交流。

    马铃薯4年前 (2014-12-12)回复
  3. #-47

    参考的是天猫的实现思路。喜欢就star一个。
    代码在:https://github.com/amibug/fly

    马铃薯4年前 (2014-12-20)回复
  4. #-46

    注释了写错了,是减号不是加号

    利用第二个坐标:
    b = (y2+ a*x2*x2) / x2

    t2年前 (2016-11-13)回复

前端开发相关广告投放 更专业 更精准

联系我们

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏