js 抛物线 笔记备份

原理公式 y=ax^2+bx+c

线性代数中的行列式来计算

//y=ax^2+bx (过原点的抛物线)
// 利用行列式计算,先计算D ,在计算参数 a, b
//D=X0^2*x1-x1^2*x0
//a=(y0*x1-x0*y1)/D
//b=(x0^2*y1-x1^2*y0)/D

 

 解方程组 

  传入3点坐标(x1, y1)(x2, y2)(x3, y3)求解出参数 a, b, c

a: (y3 - y1 + (y2 - y1) * (x1 - x3) / (x2 - x1)) / (x3 * x3 - x1 * x1 - (x3 - x1) * (x1 + x2))

b: (y2 - y1 + a * x1 * x1 - a * x2 * x2) / (x2 - x1)

c: y1 - a * x1 * x1 - b * x1

 

线性代数示范

//y=ax^2+bx (过原点的抛物线)
            // 利用行列式计算,先计算D ,在计算参数 a, b
            //D=X0^2*x1-x1^2*x0
            //a=(y0*x1-x0*y1)/D
            //b=(x0^2*y1-x1^2*y0)/D
            const D = pow(topPoint.x) * endPoint.x - pow(endPoint.x) * topPoint.x
            console.log('D:' + D);
            const a = (topPoint.y * endPoint.x - topPoint.x * endPoint.y) / D
            const b = (pow(topPoint.x) * endPoint.y - pow(endPoint.x) * topPoint.y) / D
            console.log(a, b);

 

 解方程示范

 const a = (y3 - y1 + (y2 - y1) * (x1 - x3) / (x2 - x1)) / (x3 * x3 - x1 * x1 - (x3 - x1) * (x1 + x2))

  const b = (y2 - y1 + a * x1 * x1 - a * x2 * x2) / (x2 - x1)

  const c = y1 - a * x1 * x1 - b * x1

  const timer = setInterval(() => {

    times += 1

    const x = x1 + speedX * times

    const y = a * x * x + b * x + c

    if (times * time > animationTime) {

      clearInterval(timer)

      document.body.removeChild(animationDoms)

      callback && callback()

    } else {

      animationDoms.style.left = `${-x}px`

      animationDoms.style.top = `${-y}px`

    }

  }, time)

}

  

 

 

    var funParabola = function(element, target, options) {
            /*
             * 网页模拟现实需要一个比例尺
             * 如果按照1像素就是1米来算,显然不合适,因为页面动不动就几百像素
             * 页面上,我们放两个物体,200~800像素之间,我们可以映射为现实世界的2米到8米,也就是100:1
             * 不过,本方法没有对此有所体现,因此不必在意
            */
            
            var defaults = {
                speed: 166.67, // 每帧移动的像素大小,每帧(对于大部分显示屏)大约16~17毫秒
                curvature: 0.001,  // 实际指焦点到准线的距离,你可以抽象成曲率,这里模拟扔物体的抛物线,因此是开口向下的
                progress: function() {},
                complete: function() {}
            };
            
            var params = {}; options = options || {};
            
            for (var key in defaults) {
                params[key] = options[key] || defaults[key];
            }
            
            var exports = {
                mark: function() { return this; },
                position: function() { return this; },
                move: function() { return this; },
                init: function() { return this; }
            };
            
            /* 确定移动的方式 
             * IE6-IE8 是margin位移
             * IE9+使用transform
            */
            var moveStyle = "margin", testDiv = document.createElement("div");
            if ("oninput" in testDiv) {
                ["", "ms", "webkit"].forEach(function(prefix) {
                    var transform = prefix + (prefix? "T": "t") + "ransform";
                    if (transform in testDiv.style) {
                        moveStyle = transform;
                    }
                });        
            }
            
            // 根据两点坐标以及曲率确定运动曲线函数(也就是确定a, b的值)
            /* 公式: y = a*x*x + b*x + c;
            */
            var a = params.curvature, b = 0, c = 0;
            
            // 是否执行运动的标志量
            var flagMove = true;
            
            if (element && target && element.nodeType == 1 && target.nodeType == 1) {
                var rectElement = {}, rectTarget = {};
                
                // 移动元素的中心点位置,目标元素的中心点位置
                var centerElement = {}, centerTarget = {};
                
                // 目标元素的坐标位置
                var coordElement = {}, coordTarget = {};
                
                // 标注当前元素的坐标
                exports.mark = function() {
                    if (flagMove === false) return this;
                    if (typeof coordElement.x == "undefined") this.position();
                    element.setAttribute("data-center", [coordElement.x, coordElement.y].join());
                    target.setAttribute("data-center", [coordTarget.x, coordTarget.y].join());
                    return this;
                }
                
                exports.position = function() {
                    if (flagMove === false) return this;
                    
                    var scrollLeft = document.documentElement.scrollLeft || document.body.scrollLeft,
                        scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
                    
                    // 初始位置
                    if (moveStyle == "margin") {
                        element.style.marginLeft = element.style.marginTop = "0px";
                    } else {
                        element.style[moveStyle] = "translate(0, 0)";
                    }
                    
                    // 四边缘的坐标
                    rectElement = element.getBoundingClientRect();
                    rectTarget = target.getBoundingClientRect();
                    
                    // 移动元素的中心点坐标
                    centerElement = {
                        x: rectElement.left + (rectElement.right - rectElement.left) / 2 + scrollLeft,
                        y: rectElement.top + (rectElement.bottom - rectElement.top) / 2    + scrollTop
                    };
                    
                    // 目标元素的中心点位置
                    centerTarget = {
                        x: rectTarget.left + (rectTarget.right - rectTarget.left) / 2 + scrollLeft,
                        y: rectTarget.top + (rectTarget.bottom - rectTarget.top) / 2 + scrollTop        
                    };
                    
                    // 转换成相对坐标位置
                    coordElement = {
                        x: 0,
                        y: 0    
                    };
                    coordTarget = {
                        x: -1 * (centerElement.x - centerTarget.x),
                        y:  -1 * (centerElement.y - centerTarget.y)    
                    };
                    
                    /*
                     * 因为经过(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
                    */
                    // 于是
                    b = (coordTarget.y - a * coordTarget.x * coordTarget.x) / coordTarget.x;    
                    
                    return this;
                };        
                
                // 按照这个曲线运动
                exports.move = function() {
                    // 如果曲线运动还没有结束,不再执行新的运动
                    if (flagMove === false) return this;
                    
                    var startx = 0, rate = coordTarget.x > 0? 1: -1;

                    var step = function() {
                        // 切线 y'=2ax+b
                        var tangent = 2 * a * startx + b; // = y / x
                        // y*y + x*x = speed
                        // (tangent * x)^2 + x*x = speed
                        // x = Math.sqr(speed / (tangent * tangent + 1));
                        startx = startx + rate * Math.sqrt(params.speed / (tangent * tangent + 1));
                        
                        // 防止过界
                        if ((rate == 1 && startx > coordTarget.x) || (rate == -1 && startx < coordTarget.x)) {
                            startx = coordTarget.x;
                        }
                        var x = startx, y = a * x * x + b * x;
                        
                        // 标记当前位置,这里有测试使用的嫌疑,实际使用可以将这一行注释
                        element.setAttribute("data-center", [Math.round(x), Math.round(y)].join());
                        
                        // x, y目前是坐标,需要转换成定位的像素值
                        if (moveStyle == "margin") {
                            element.style.marginLeft = x + "px";
                            element.style.marginTop = y + "px";
                        } else {
                            element.style[moveStyle] = "translate("+ [x + "px", y + "px"].join() +")";
                        }
                        
                        if (startx !== coordTarget.x) {
                            params.progress(x, y);
                            window.requestAnimationFrame(step);    
                        } else {
                            // 运动结束,回调执行
                            params.complete();
                            flagMove = true;    
                        }
                    };
                    window.requestAnimationFrame(step);
                    flagMove = false;
                    
                    return this;
                };
                
                // 初始化方法
                exports.init = function() {
                    this.position().mark().move();
                };
            }
            
            return exports;
        }



/* 元素 */
 var element = document.getElementById("element"), target = document.getElementById("target");

// 抛物线元素的的位置标记
 var parabola = funParabola(element, target).mark();

// 抛物线运动的触发
 document.body.onclick = function() {
     element.style.marginLeft = "0px";
     element.style.marginTop = "0px";
     parabola.init();
 };

 

 

 

/* 元素 */
var element = document.getElementById(  "element" ), 
  target = document.getElementById(  "target" );
// 抛物线元素的的位置标记
var parabola = funParabola(element, target).mark();
// 抛物线运动的触发
document.body.onclick =   function () {
  element.style.marginLeft =   "0px" ;
  element.style.marginTop =   "0px" ;
  parabola.init();
};
加入购物车实战:
/* 本demo演示脚本基于ieBetter.js, 项目地址:https://github.com/zhangxinxu/ieBetter.js */

// 元素以及其他一些变量
var eleFlyElement = document.querySelector(  "#flyItem" ), eleShopCart = document.querySelector(  "#shopCart" );
var numberItem = 0;
// 抛物线运动
var myParabola = funParabola(eleFlyElement, eleShopCart, {
  speed: 400,
  curvature: 0.002,  
  complete:   function () {
    eleFlyElement.style.visibility =   "hidden" ;
    eleShopCart.querySelector(  "span" ).innerHTML = ++numberItem;
  }
});
// 绑定点击事件
if (eleFlyElement && eleShopCart) {
  [].slice.call(document.getElementsByClassName(  "btnCart" )).forEach(  function (button) {
    button.addEventListener(  "click" ,   function () {
      // 滚动大小
      var scrollLeft = document.documentElement.scrollLeft || document.body.scrollLeft || 0,
        scrollTop = document.documentElement.scrollTop || document.body.scrollTop || 0;

      eleFlyElement.style.left = event.clientX + scrollLeft +   "px" ;
      eleFlyElement.style.top = event.clientY + scrollTop +   "px" ;
      eleFlyElement.style.visibility =   "visible" ;
    
      // 需要重定位
      myParabola.position().move(); 
    });
  });
}

  

学习版本

<!DOCTYPE html>
<html lang="en" >

<head>

  <meta charset="UTF-8">
  <link rel="shortcut icon" type="image/x-icon" href="https://static.codepen.io/assets/favicon/favicon-8ea04875e70c4b0bb41da869e81236e54394d63638a1ef12fa558a4a835f1164.ico" />
  <link rel="mask-icon" type="" href="https://static.codepen.io/assets/favicon/logo-pin-f2d2b6d2c61838f7e76325261b7195c27224080bc099486ddd6dccb469b8e8e6.svg" color="#111" />
  <title>CodePen - gwcpwx</title>
  
  
  
  
      <style>
      #goods {  
  cursor: pointer;
  width: 100px;
  height: 40px;
  line-height: 40px;
  border: 1px solid deeppink;
  text-align: center;
  color: deeppink;  
}  
#goods:hover {  
  color: #fff;  
  background-color: deeppink;  
}
#cart {
  position: fixed;
  right: 0;
  bottom: 100px;
  color: deeppink;
  border: 1px solid deeppink;  
}  
    </style>

  <script>

</script>

 

</head>

<body translate="no" >
y = ax² + bx + c
  <div id="goods"> 
商品
</div> 


<br><br><br><br>
<br><br><br><br><br> 
<br><br><br><br><br>
<div id="cart"> 
购物车  
</div>

 

    <script >
          var goodsDom = document.querySelector("#goods");
    var cartDom = document.querySelector("#cart");
    goodsDom.onclick = function () {
    var goodsXLeft = goodsDom.offsetLeft;
    var goodsYTop = goodsDom.offsetTop;
    var startX = goodsXLeft + 100;
    var startY = goodsYTop - document.body.scrollTop + 12;
    var endX = cartDom.offsetLeft;
    var endY = cartDom.offsetTop;
    var diffX = endX - startX;
    var diffY = endY - startY;
    //  假设中点(0, 0),也就是方程中的c为0
    var c = 0;
    var a = (endY/endX-startY/startX)/(endX - startX);
    var b = endY/endX - a*endX;
    // 创建一个移动的dom
    var movingDom = document.createElement("div");
    movingDom.style.position = 'fixed';
    movingDom.style.left = startX + 'px';
    movingDom.style.top = startY + 'px';
    movingDom.style.height = '16px';
    movingDom.style.width = '16px';
    movingDom.style.borderRadius = '8px';
    movingDom.style.background = 'red';
    document.body.appendChild(movingDom)
    // 定义移动的dom的x, y
    var x = startX;
    var y = startY;
    var ax2 = 0;
    var bx = 0;
    var time = setInterval(function(){
      if(x < endX) {
        x = x + 2;
        ax2 = a*x*x;
        bx = b*x;
        y = ax2 + bx;
        console.log(ax2, bx, y)
        movingDom.style.left = x + 'px';
        movingDom.style.top = y + 'px';
      } else {
        movingDom.parentNode.removeChild(movingDom)
        clearInterval(time);
      }
     },10)
    }
      //# sourceURL=pen.js
    </script>



  
  

</body>

</html>
 

 

 

 

 

 vue实现抛物线

 

<template>
  
      <div>   
          <ul class="lists">
              <li>商品1商品1<i @click="ball_fly($event)">+</i></li>
              <li>商品12商品1<i @click="ball_fly($event)">+</i></li>
              <li>商品13商品1<i @click="ball_fly($event)">+</i></li>
              
              <li>商品1商品1<i @click="ball_fly($event)">+</i></li>
              <li>商品12商品1<i @click="ball_fly($event)">+</i></li>
              <li>商品13商品1<i @click="ball_fly($event)">+</i></li>
              
              <li>商品1商品1<i @click="ball_fly($event)">+</i></li>
              <li>商品12商品1<i @click="ball_fly($event)">+</i></li>
              <li>商品13商品1<i @click="ball_fly($event)">+</i></li>
              
              <li>商品1商品1<i @click="ball_fly($event)">+</i></li>
              <li>商品12商品1<i @click="ball_fly($event)">+</i></li>
              <li>商品13商品1<i @click="ball_fly($event)">+</i></li>
          </ul>
          
              <div class="targetbox"><div class="target car_icon"  ref="carIcon">购物车</div>  </div> 

      </div>
   
     
    </template>
<script>

export default {
  name: 'business',
  data () {
    return {
      showMe: false,
      // 计算商品区域高度
      computedContentHeight: window.innerHeight - (window.innerWidth / 10 * 4.2),
     
    };
  },
  
   
  
  methods: {
    // 初始化
    init () {
     
      // 给购物车添加animationend事件,动画结束后去掉有animation的class
      this.$refs.carIcon.addEventListener('animationend', () => {
        this.$refs.carIcon.classList.remove('tantantan');
      }, false);
    },
 
   


    // 修改版抛球效果,使用css3中的贝塞尔曲线实现
    ball_fly (e) {
      // 被点元素位置
      var bound = e.target.getBoundingClientRect();
      var boundTop = bound.top;// 点击top值
      var boundLeft = bound.left;// 点击left值
      // 目标元素位置
      var target = this.$refs.carIcon;
      var targetData = target.getBoundingClientRect();
      var targetTop = targetData.top;// 目标top值
      var targetLeft = targetData.left;// 目标left值
      // 创建父球(父球横向运动)
      var father = document.createElement('div');
      father.className = 'father flyball';
      // 创建子球(子球垂直css3贝塞尔曲线运动,先上后下,得到抛球效果)
      var child = document.createElement('div');
      child.className = 'child inner';
      father.appendChild(child);
      // 设置父盒子生成的位置
      // father.style.cssText = 'top:' + boundTop + 'px;left:' + boundLeft + 'px;';
      father.style.top = boundTop + 'px';
      father.style.left = boundLeft + 'px';
      // append小球到页面中
      document.body.appendChild(father);
      setTimeout(() => {
        // 目标left - 所点元素left + 目标元素宽度的一半(修正落点)
        father.style.transform = 'translate3d(' + (targetLeft - boundLeft + targetData.width / 2) + 'px, 0px, 0px)';
        child.style.cssText = 'transform: translate3d(0px, ' + (targetTop - boundTop) + 'px, 0px);';
        // 运动结束后删掉小球
        setTimeout(() => {
          // 移除小球
          father.parentNode.removeChild(father);
          // 购物车添加弹弹弹的css
          this.$refs.carIcon.classList.add('tantantan');
           // 给购物车添加animationend事件,动画结束后去掉有animation的class
      this.$refs.carIcon.addEventListener('animationend', () => {
        this.$refs.carIcon.classList.remove('tantantan');
      }, false);
          
        }, 500);
      }, 10);
    }
    // 生成小球抛出 计算left top 生成动画 不流畅 (css3的没想好)
    /* ball_fly (e) {
      // 被点元素宽高
      var bound = e.target.getBoundingClientRect(); // 被点元素位置
      // 创造元素
      var qiu = document.createElement('div');
      qiu.className = 'qiu';
      qiu.style.top = bound.top + 'px';
      qiu.style.left = bound.left + 'px';
      document.body.appendChild(qiu);
      // 目标元素位置
      var dsa = this.$refs.carIcon;
      var mubiao = dsa.getBoundingClientRect();
      var mubiaoT = mubiao.top;
      var mubiaoL = mubiao.left;
      var timer = null;
      // top差值 left差值
      var chaTop = mubiaoT - bound.top;
      // 要减掉目标宽度一半 让落点对准目标中心
      var chaLeft = bound.left - mubiaoL - dsa.offsetWidth / 2;
      // 规定上抛初速度为 top 差值的55分之1
      var g = chaTop / 55;
      // 规定上抛初速度为 top 差值的15分之1
      var vTop = chaTop / 15;
      timer = setInterval(() => {
        qiu.style.top = (qiu.getBoundingClientRect().top + (-vTop + g)) + 'px';
        qiu.style.left = (qiu.getBoundingClientRect().left + (-chaLeft / 14)) + 'px';
        // 每次 g 对速度的影响
        vTop -= g;
        if (qiu.getBoundingClientRect().top >= mubiaoT) {
          clearInterval(timer);
          qiu.parentNode.removeChild(qiu);
          this.$refs.carIcon.classList.add('tantantan');
        }
      }, 1000 / 25);
    } */
  }
};
</script>

<style lang="less">
.business_box{
  width:100%;
  height:100%;
  
  } 
 @keyframes mymove {
0% {
    transform: scale(1);
}
25% {
    transform: scale(0.8);
}
50% {
    transform: scale(1.1);
}
75% {
    transform: scale(0.9);
}
100% {
    transform: scale(1);
}
}
/* 购物车弹弹弹 */
.tantantan {
  animation: mymove 1s;
}
/* 修正版抛球效果所需CSS */
.flyball {
    position:fixed;
    top:0;
    left:0;
    -webkit-transition:-webkit-transform .5s linear;
    transition:-webkit-transform .5s linear;
    transition:transform .5s linear;
    transition:transform .5s linear, -webkit-transform .5s linear
}
.flyball .inner {
    position:absolute;
    top:0;
    left:0;
    background-color:#3190e8;
    border-radius:50%
}
.flyball, .flyball .inner {
    will-change:transform;/* css3自带的开启GPU加速 */
    -webkit-transform:translateZ(0);
    transform:translateZ(0)
}
.flyball .inner {
    -webkit-transition:-webkit-transform .5s cubic-bezier(.3, -.2, 1, 0);
    transition:-webkit-transform .5s cubic-bezier(.3, -.2, 1, 0);
    transition:transform .5s cubic-bezier(.3, -.2, 1, 0);
    transition:transform .5s cubic-bezier(.3, -.2, 1, 0), -webkit-transform .5s cubic-bezier(.3, -.2, 1, 0)
}

/* 父盒子的样式 */
.father{
  width: 20px;;
  height:20px;
  position: fixed;
  z-index: 999;
}
/* 子盒子(小球)的样式 */
.child{
  width: 20px;;
  height:20px;
  background: #3190e8;
  position: absolute;
  top: 0;
  left: 0;
}

.lists{ width: 100%;  height: auto;; overflow: hidden;}

.lists li{ height: 44px; line-height: 40px; background: #f1f1f1; margin-bottom: 20px; position: relative; padding: 0 10px;}
.lists li i{ width: 20px;  height: 20px; text-align: center;; line-height: 19px;; display: block; border-radius: 15px; overflow: hidden; position: absolute; right: 20px; top:10px;; background: #3190e8; color: #fff; ;}
.targetbox{ position: fixed; bottom: 54px; left: 0; width: 100%; height: 54px; background: #666;}
.target{  width: 50px; height: 50px; color: #fff; line-height: 50px; text-align: center;}
.car_icon {
  
   
   
    background: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zd…48Y2lyY2xlIGN4PSIxMiIgY3k9IjUxIiByPSI0IiBmaWxsPSIjRkZGIi8+PC9nPjwvc3ZnPg==) #3190e8 center no-repeat;
   
    border-radius: 50%;
    background-size: 60% auto;
}
</style>

 

 

 css 实现抛物线


<  html lang="en" style="width:100%;height:100%;">
<  head >
    <  meta charset="UTF-8">
    <  meta name="viewport" content="width=device-width">
    <  style >
        * {
            padding: 0;
            margin: 0;
        }
        #ball {
            width:12px;
            height:12px;
            background: #5EA345;
            border-radius: 50%;
            position: fixed;
            transition: left 1s linear, top 1s ease-in;
        }
    
    <  title >CSS3 水平抛物线动画 

<  body style="width:100%;height:100%;">
    <  div id="ball"> 

<  script >
    var $ball = document.getElementById('ball');
    document.body.onclick = function (evt) {
        console.log(evt.pageX,evt.pageY)
        $ball.style.top = evt.pageY+'px';
        $ball.style.left = evt.pageX+'px';
        $ball.style.transition = 'left 0s, top 0s';
        setTimeout(()=>{
            $ball.style.top = window.innerHeight+'px';
            $ball.style.left = '0px';
            $ball.style.transition = 'left 1s linear, top 1s ease-in';
        }, 20)
    }

  

小程序

cartAnimation(x, y) { // x y 为手指点击的坐标,即球的起始坐标
    let self = this,
        cartY = app.globalData.winHeight - 50, // 结束位置(购物车图片)纵坐标
        cartX = 50, // 结束位置(购物车图片)的横坐标
        animationX = flyX(cartX, x), // 创建球的横向动画
        animationY = flyY(cartY, y) // 创建球的纵向动画
    this.setData({
          ballX: x,
          ballY: y,
          showBall: true
    })
    setTimeoutES6(100).then(() => { // 100 ms 延时,确保球已经到位并显示
        self.setData({
            animationX: animationX.export(),
            animationY: animationY.export(),
        })
        return setTimeoutES6(400) // 400 ms 是球的抛物线动画时长
    }).then(() => { // 400 ms 延时后隐藏球
        this.setData({
            showBall: false,
        })
    })
}

function setTimeoutES6(sec) { // Promise 化 setTimeout
    return new Promise((resolve, reject) => {
        setTimeout(() => {resolve()}, sec)
    })
}

function flyX(cartX, oriX) { // 水平动画
    let animation = wx.createAnimation({
        duration: 400,
        timingFunction: 'linear',
    })
    animation.left(cartX).step()
    return animation
}

function flyY(cartY, oriY) { // 垂直动画
    let animation = wx.createAnimation({
        duration: 400,
        timingFunction: 'ease-in',
    })
    animation.top(cartY).step()
    return animation
}

  

 

222

    var funParabola = function(element, target, options) {
            /*
             * 网页模拟现实需要一个比例尺
             * 如果按照1像素就是1米来算,显然不合适,因为页面动不动就几百像素
             * 页面上,我们放两个物体,200~800像素之间,我们可以映射为现实世界的2米到8米,也就是100:1
             * 不过,本方法没有对此有所体现,因此不必在意
            */
            
            var defaults = {
                speed: 166.67, // 每帧移动的像素大小,每帧(对于大部分显示屏)大约16~17毫秒
                curvature: 0.001,  // 实际指焦点到准线的距离,你可以抽象成曲率,这里模拟扔物体的抛物线,因此是开口向下的
                progress: function() {},
                complete: function() {}
            };
            
            var params = {}; options = options || {};
            
            for (var key in defaults) {
                params[key] = options[key] || defaults[key];
            }
            
            var exports = {
                mark: function() { return this; },
                position: function() { return this; },
                move: function() { return this; },
                init: function() { return this; }
            };
            
            /* 确定移动的方式 
             * IE6-IE8 是margin位移
             * IE9+使用transform
            */
            var moveStyle = "margin", testDiv = document.createElement("div");
            if ("oninput" in testDiv) {
                ["", "ms", "webkit"].forEach(function(prefix) {
                    var transform = prefix + (prefix? "T": "t") + "ransform";
                    if (transform in testDiv.style) {
                        moveStyle = transform;
                    }
                });        
            }
            
            // 根据两点坐标以及曲率确定运动曲线函数(也就是确定a, b的值)
            /* 公式: y = a*x*x + b*x + c;
            */
            var a = params.curvature, b = 0, c = 0;
            
            // 是否执行运动的标志量
            var flagMove = true;
            
            if (element && target && element.nodeType == 1 && target.nodeType == 1) {
                var rectElement = {}, rectTarget = {};
                
                // 移动元素的中心点位置,目标元素的中心点位置
                var centerElement = {}, centerTarget = {};
                
                // 目标元素的坐标位置
                var coordElement = {}, coordTarget = {};
                
                // 标注当前元素的坐标
                exports.mark = function() {
                    if (flagMove === false) return this;
                    if (typeof coordElement.x == "undefined") this.position();
                    element.setAttribute("data-center", [coordElement.x, coordElement.y].join());
                    target.setAttribute("data-center", [coordTarget.x, coordTarget.y].join());
                    return this;
                }
                
                exports.position = function() {
                    if (flagMove === false) return this;
                    
                    var scrollLeft = document.documentElement.scrollLeft || document.body.scrollLeft,
                        scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
                    
                    // 初始位置
                    if (moveStyle == "margin") {
                        element.style.marginLeft = element.style.marginTop = "0px";
                    } else {
                        element.style[moveStyle] = "translate(0, 0)";
                    }
                    
                    // 四边缘的坐标
                    rectElement = element.getBoundingClientRect();
                    rectTarget = target.getBoundingClientRect();
                    
                    // 移动元素的中心点坐标
                    centerElement = {
                        x: rectElement.left + (rectElement.right - rectElement.left) / 2 + scrollLeft,
                        y: rectElement.top + (rectElement.bottom - rectElement.top) / 2    + scrollTop
                    };
                    
                    // 目标元素的中心点位置
                    centerTarget = {
                        x: rectTarget.left + (rectTarget.right - rectTarget.left) / 2 + scrollLeft,
                        y: rectTarget.top + (rectTarget.bottom - rectTarget.top) / 2 + scrollTop        
                    };
                    
                    // 转换成相对坐标位置
                    coordElement = {
                        x: 0,
                        y: 0    
                    };
                    coordTarget = {
                        x: -1 * (centerElement.x - centerTarget.x),
                        y:  -1 * (centerElement.y - centerTarget.y)    
                    };
                    
                    /*
                     * 因为经过(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
                    */
                    // 于是
                    b = (coordTarget.y - a * coordTarget.x * coordTarget.x) / coordTarget.x;    
                    
                    return this;
                };        
                
                // 按照这个曲线运动
                exports.move = function() {
                    // 如果曲线运动还没有结束,不再执行新的运动
                    if (flagMove === false) return this;
                    
                    var startx = 0, rate = coordTarget.x > 0? 1: -1;

                    var step = function() {
                        // 切线 y'=2ax+b
                        var tangent = 2 * a * startx + b; // = y / x
                        // y*y + x*x = speed
                        // (tangent * x)^2 + x*x = speed
                        // x = Math.sqr(speed / (tangent * tangent + 1));
                        startx = startx + rate * Math.sqrt(params.speed / (tangent * tangent + 1));
                        
                        // 防止过界
                        if ((rate == 1 && startx > coordTarget.x) || (rate == -1 && startx < coordTarget.x)) {
                            startx = coordTarget.x;
                        }
                        var x = startx, y = a * x * x + b * x;
                        
                        // 标记当前位置,这里有测试使用的嫌疑,实际使用可以将这一行注释
                        element.setAttribute("data-center", [Math.round(x), Math.round(y)].join());
                        
                        // x, y目前是坐标,需要转换成定位的像素值
                        if (moveStyle == "margin") {
                            element.style.marginLeft = x + "px";
                            element.style.marginTop = y + "px";
                        } else {
                            element.style[moveStyle] = "translate("+ [x + "px", y + "px"].join() +")";
                        }
                        
                        if (startx !== coordTarget.x) {
                            params.progress(x, y);
                            window.requestAnimationFrame(step);    
                        } else {
                            // 运动结束,回调执行
                            params.complete();
                            flagMove = true;    
                        }
                    };
                    window.requestAnimationFrame(step);
                    flagMove = false;
                    
                    return this;
                };
                
                // 初始化方法
                exports.init = function() {
                    this.position().mark().move();
                };
            }
            
            return exports;
        }



/* 元素 */
 var element = document.getElementById("element"), target = document.getElementById("target");

// 抛物线元素的的位置标记
 var parabola = funParabola(element, target).mark();

// 抛物线运动的触发
 document.body.onclick = function() {
     element.style.marginLeft = "0px";
     element.style.marginTop = "0px";
     parabola.init();
 };

333

  

 

 

/*!
 * parabola trajectory v1.0
 *
 * Contact: https://github.com/xiaolin3303
 * 2016-09-30
 *
 * Designed and built with all the love of Web
 */
;(  function (window, Math) {
    /*
     * @params Object opts
     */
    function Parabola (opts) {
        opts = opts || {};
        // required `startPos`, `endPos` params in opts
        if (!opts.startPos) {
            throw new Error(  '`startPos` is required in init options' );
        }

        if (!opts.endPos) {
            throw new Error(  '`endPos` is required in init options' );
        }
        // opts.curvature = opts.curvature || 0.003;
        opts.duration = opts.duration || 2000;

        this .opts = opts;

        this .calCurvature();
    }

    Parabola.prototype.calCurvature =   function () {

        this .opts.driftX =   this .opts.endPos.left -   this .opts.startPos.left;
        this .opts.driftY =   this .opts.endPos.top -   this .opts.startPos.top;

        // 在不超出屏幕范围的前提下,尽量抛得更高,计算合适的曲率 (a)
        var yMin = -1 *   this .opts.startPos.top;

        var a =   this .power(  this .opts.driftX, 4);
        var b = (4 * yMin - 2 *   this .opts.driftY) *   this .power(  this .opts.driftX, 2);
        var c =   this .power(  this .opts.driftY, 2);

        this .opts.curvature = (-1 * b + Math.sqrt((  this .power(b, 2) - 4 * a * c))) / (2 * a);

        this .opts.b = (  this .opts.driftY -   this .opts.curvature *   this .opts.driftX *   this .opts.driftX) /   this .opts.driftX;
    }

    Parabola.prototype.power =   function (v, n) {
        if (n === 1) {
            return v;
        }   else {
            return v * arguments.callee(v, (n - 1));
        }
    }

    Parabola.prototype.calPosition =   function (progress) {
        // 当前进度下的X轴的位置
        x =   this .opts.driftX * progress;
        // 当前进度下的Y轴的位置
        // y = a*x*x + b*x + c,  c = 0
        y =   this .opts.curvature * x * x +   this .opts.b * x;

        return {
            left: Math.round(x +   this .opts.startPos.left),
            top: Math.round(y +   this .opts.startPos.top)
        }
    }

    Parabola.prototype.start =   function () {
        var opts =   this .opts;
        var me =   this ;
        var startTimeStamp = +  new Date();
        var animationFrame = window.requestAnimationFrame  ||
            window.webkitRequestAnimationFrame  ||
            window.mozRequestAnimationFrame     ||
            window.oRequestAnimationFrame       ||
            window.msRequestAnimationFrame      ||
            function (callback) { window.setTimeout(callback, 1000 / 60); };

        function step () {
            var currentTimeStamp = +  new Date();

            var progress = Math.min((currentTimeStamp - startTimeStamp) / opts.duration, 1);
            if (progress === 1) {
                // 动画结束
                return false ;
            }   else {
                var position = me.calPosition(progress);
                opts.onStep && opts.onStep(position);

                return true ;
            }
        }

        function progress () {
            if (step()) {
                animationFrame(progress);
            }   else {
                if (  typeof opts.onFinish ===   'function' ) {
                    opts.onFinish(opts.endPos);
                }
            }
        }

        animationFrame(progress);
    }

    if (   typeof module !==   'undefined' && module.exports ) {
        module.exports = Parabola;
    }   else if (   typeof define ===   'function' && define.amd ) {
        define(   function () {   return Parabola; } );
    }   else {
        window.Parabola = Parabola;
    }

})(window, Math)

window.onload =   function () {
    var btn = document.querySelector(  'button' );
        var target = document.querySelector(  '.dot' );
        var parabola =   new Parabola({
            startPos: {
                left: 100,
                top: 60
            },
            endPos: {
                left: 500,
                top: 200
            },
      duration: 1000,
            onStep (pos) {
                target.style.left = pos.left +   'px' ;
                target.style.top = pos.top +   'px' ;
            },
            onFinish (pos) {
        target.classList.add(  'scaleAnimation' );
                console.log(  'Animation Finished!' );
            }
        });

        // parabola.start();
    btn.addEventListener(  'click' ,   function () {
        target.classList.remove(  'scaleAnimation' );
        parabola.start();
    },   false );
}

fly.js

/*
 * jquery.fly
 * 
 * 抛物线动画
 * @github https://github.com/amibug/fly
 * Copyright (c) 2014 wuyuedong
 * copy from tmall.com
 */
(function ($) {
  $.fly = function (element, options) {
    // 默认值
    var defaults = {
      version: '1.0.0',
      autoPlay: true,
      vertex_Rtop: 20, // 默认顶点高度top值
      speed: 1.2,
      start: {}, // top, left, width, height
      end: {},
      onEnd: $.noop
    };

    var self = this,
      $element = $(element);

    /**
     * 初始化组件,new的时候即调用
     */
    self.init = function (options) {
      this.setOptions(options);
      !!this.settings.autoPlay && this.play();
    };

    /**
     * 设置组件参数
     */
    self.setOptions = function (options) {
      this.settings = $.extend(true, {}, defaults, options);
      var settings = this.settings,
        start = settings.start,
        end = settings.end;

      $element.css({marginTop: '0px', marginLeft: '0px', position: 'fixed'}).appendTo('body');
      // 运动过程中有改变大小
      if (end.width != null && end.height != null) {
        $.extend(true, start, {
          width: $element.width(),
          height: $element.height()
        });
      }
      // 运动轨迹最高点top值
      var vertex_top = Math.min(start.top, end.top) - Math.abs(start.left - end.left) / 3;
      if (vertex_top < settings.vertex_Rtop) {
        // 可能出现起点或者终点就是运动曲线顶点的情况
        vertex_top = Math.min(settings.vertex_Rtop, Math.min(start.top, end.top));
      }

      /**
       * ======================================================
       * 运动轨迹在页面中的top值可以抽象成函数 y = a * x*x + b;
       * a = curvature
       * b = vertex_top
       * ======================================================
       */

      var distance = Math.sqrt(Math.pow(start.top - end.top, 2) + Math.pow(start.left - end.left, 2)),
        // 元素移动次数
        steps = Math.ceil(Math.min(Math.max(Math.log(distance) / 0.05 - 75, 30), 100) / settings.speed),
        ratio = start.top == vertex_top ? 0 : -Math.sqrt((end.top - vertex_top) / (start.top - vertex_top)),
        vertex_left = (ratio * start.left - end.left) / (ratio - 1),
        // 特殊情况,出现顶点left==终点left,将曲率设置为0,做直线运动。
        curvature = end.left == vertex_left ? 0 : (end.top - vertex_top) / Math.pow(end.left - vertex_left, 2);

      $.extend(true, settings, {
        count: -1, // 每次重置为-1
        steps: steps,
        vertex_left: vertex_left,
        vertex_top: vertex_top,
        curvature: curvature
      });
    };

    /**
     * 开始运动,可自己调用
     */
    self.play = function () {
      this.move();
    };

    /**
     * 按step运动
     */
    self.move = function () {
      var settings = this.settings,
        start = settings.start,
        count = settings.count,
        steps = settings.steps,
        end = settings.end;
      // 计算left top值
      var left = start.left + (end.left - start.left) * count / steps,
        top = settings.curvature == 0 ? start.top + (end.top - start.top) * count / steps : settings.curvature * Math.pow(left - settings.vertex_left, 2) + settings.vertex_top;
      // 运动过程中有改变大小
      if (end.width != null && end.height != null) {
        var i = steps / 2,
          width = end.width - (end.width - start.width) * Math.cos(count < i ? 0 : (count - i) / (steps - i) * Math.PI / 2),
          height = end.height - (end.height - start.height) * Math.cos(count < i ? 0 : (count - i) / (steps - i) * Math.PI / 2);
        $element.css({width: width + "px", height: height + "px", "font-size": Math.min(width, height) + "px"});
      }
      $element.css({
        left: left + "px",
        top: top + "px"
      });
      settings.count++;
      // 定时任务
      var time = window.requestAnimationFrame($.proxy(this.move, this));
      if (count == steps) {
        window.cancelAnimationFrame(time);
        // fire callback
        settings.onEnd.apply(this);
      }
    };

    /**
     * 销毁
     */
    self.destroy = function(){
      $element.remove();
    };

    self.init(options);
  };

  // add the plugin to the jQuery.fn object
  $.fn.fly = function (options) {
    return this.each(function () {
      if (undefined == $(this).data('fly')) {
        $(this).data('fly', new $.fly(this, options));
      }
    });
  };
})(jQuery);
<script>
$(function() {
    var offset = $("#end").offset();
    $(".addcar").click(function(event){
        var addcar = $(this);
        var img = addcar.parent().find('img').attr('src');
        var flyer = $('<img class="u-flyer" src="'+img+'">');
        flyer.fly({
            start: {
                left: event.pageX,
                top: event.pageY
            },
            end: {
                left: offset.left+10,
                top: offset.top+10,
                width: 0,
                height: 0
            },
            onEnd: function(){
                $("#msg").show().animate({width: '250px'}, 200).fadeOut(1000);
                addcar.css("cursor","default").removeClass('orange').unbind('click');
                this.destory();
            }
        });
    });
});
</script>

 

  

posted @ 2017-04-12 14:53  surfaces  阅读(616)  评论(0编辑  收藏  举报