解构 Google 地图聪明别针动画

Deconstructing Google maps smarty pins animation

更新

Updated fiddle 简化正在发生的事情:

所以,在拥有我的 ninja moments 之后。我离理解 unknownFunctionAunknownFunctionB

背后的魔法还差得很远

为了每个人(你们两个),这是我到目前为止学到的东西

function unknownFunctionB(e) {
    var t = e.b.x - e.a.x
      , n = e.b.y - e.a.y
      , a = t * t + n * n;
    if (a > 0) {
        if (a == e.lengthSq)
            return;
        var o = Math.sqrt(a)
          , i = (o - e.length) / o
          , s = .5;
        e.b.x -= t * i * .5 * s,
        e.b.y -= n * i * .5 * s
    }
}

在上面的unknownFunctionB中,变量o是红色sitck的长度。

还是不懂

什么是变量 i 以及 (bx,by) 是如何计算的?本质上:

bx = bx - (bx - ax) * 0.5 * 0.5

by = by - (by - ay) * 0.5 * 0.5

unknownFunctionA中那些神奇的数字1.8250.825是什么?


以下无关

我正在尝试解构 smartypins

上使用的标记拖动动画

我已经设法获得了标记移动动画的相关代码,但我正在努力了解它是如何工作的,尤其是 2 个函数(我将其命名为 unknownFunctionAunknownFunctionB )

这是 smartypins 网站上使用的 StickModel class,据我所知未缩小

function unknownFunctionA(e) {
    var t = 1.825
      , n = .825
      , a = t * e.x - n * e.x0
      , o = t * e.y - n * e.y0 - 5;
    e.x0 = e.x,
    e.y0 = e.y,
    e.x = a,
    e.y = o;
}

function unknownFunctionB(e) {
    var t = e.b.x - e.a.x
      , n = e.b.y - e.a.y
      , a = t * t + n * n;
    if (a > 0) {
        if (a == e.lengthSq)
            return;
        var o = Math.sqrt(a)
          , i = (o - e.length) / o
          , s = .5;
        e.b.x -= t * i * .5 * s,
        e.b.y -= n * i * .5 * s
    }
}

function StickModel() {
    this._props = function(e) {
        return {
            length: e,
            lengthSq: e * e,
            a: {
                x: 0,
                y: 0
            },
            b: {
                x: 0,
                y: 0 - e,
                x0: 0,
                y0: 0 - e
            },
            angle: 0
        }
    }
    (60)
}

var radianToDegrees = 180 / Math.PI;

StickModel.prototype = {
    pos: {
        x: 0,
        y: 0
    },
    angle: function() {
        return this._props.angle
    },
    reset: function(e, t) {
        var n = e - this._props.a.x
          , a = t - this._props.a.y;
        this._props.a.x += n,
        this._props.a.y += a,
        this._props.b.x += n,
        this._props.b.y += a,
        this._props.b.x0 += n,
        this._props.b.y0 += a
    },
    move: function(e, t) {
        this._props.a.x = e,
        this._props.a.y = t
    },
    update: function() {
        unknownFunctionA(this._props.b),
        unknownFunctionB(this._props),
        this.pos.x = this._props.a.x,
        this.pos.y = this._props.a.y;
        var e = this._props.b.x - this._props.a.x
          , t = this._props.b.y - this._props.a.y
          , o = Math.atan2(t, e);
        this._props.angle = o * radianToDegrees;
    }
}


StickModel.prototype.constructor = StickModel;

Fiddle link canvas 上的示例实现:http://jsfiddle.net/vff1w82w/3/

同样,一切都按预期进行,我真的很想了解以下内容:

我建议的第一件事是重命名所有这些变量和方法,直到它们开始有意义。我还删除了未使用的代码。

摆动指标

通过为跟随鼠标的 Stick 创建新的位置值,将摆动添加到 Stick 模型中

  • 通过将其新位置乘以 1.825 并减去其先前位置乘以 0.825 的 "echo" 的位置来夸大其运动。有点在寻找它们之间的中间点。氦气使棍子直立。
  • overshooter 减去 undershooter 必须等于 1,否则你的摇杆会出现方向问题。高于 2.1 的超射值往往使其永远不会稳定。

seekerUpdate

根据鼠标位置更新搜索器。

  • distance_to_cover 变量测量总运动的长度。你是对的:hypothenuse(变量 o)。
  • 比率变量计算可以覆盖的距离减去棍子的大小的比率。然后该比率用于限制导引头在两个方向(x 和 y)上的更新调整。这是应该应用多少更新以防止超过目标。
  • 宽松会减慢正确的更新速度。

书中有很多与矢量相关的有趣信息The nature of code

function oscillator(seeker) {
  var overshooter = 1.825;
  var undershooter = .825;
  var helium = -5;
  var new_seeker_x = overshooter * seeker.x - undershooter * seeker.echo_x;
  var new_seeker_y = overshooter * seeker.y - undershooter * seeker.echo_y + helium;
  seeker.echo_x = seeker.x;
  seeker.echo_y = seeker.y;
  seeker.x = new_seeker_x;
  seeker.y = new_seeker_y;
}

function seekerUpdate(stick) {
  var dX = stick.seeker.x - stick.mouse_pos.x;
  var dY = stick.seeker.y - stick.mouse_pos.y;
  var distance_to_cover = Math.sqrt(dX * dX + dY * dY);
  var ratio = (distance_to_cover - stick.length) / distance_to_cover;
  var easing = .25;
  stick.seeker.x -= dX * ratio * easing;
  stick.seeker.y -= dY * ratio * easing;
}

function StickModel() {
  this._props = function(length) {
    return {
      length: length,
      lengthSq: length * length,
      mouse_pos: {
        x: 0,
        y: 0
      },
      seeker: {
        x: 0,
        y: 0 - length,
        echo_x: 0,
        echo_y: 0 - length
      }
    }
  }(60)
}

StickModel.prototype = {
  move: function(x, y) {
    this._props.mouse_pos.x = x;
    this._props.mouse_pos.y = y;
  },
  update: function() {
    oscillator(this._props.seeker);
    seekerUpdate(this._props);
  }
};

StickModel.prototype.constructor = StickModel;

// Canvas to draw stick model coordinates
var canvas = document.getElementById('myCanvas');
var context = canvas.getContext('2d');
canvas.width = window.outerWidth;
canvas.height = window.outerHeight;
var canvasCenterX = Math.floor(canvas.width / 2);
var canvasCenterY = Math.floor(canvas.height / 2);
context.translate(canvasCenterX, canvasCenterY);
var stickModel = new StickModel();
draw();

setInterval(function() {
  stickModel.update();
  draw();
}, 16);

$(window).mousemove(function(e) {
  var mouseX = (e.pageX - canvasCenterX);
  var mouseY = (e.pageY - canvasCenterY);
  stickModel.move(mouseX, mouseY);
  stickModel.update();
  draw();
});

function draw() {
  context.clearRect(-canvas.width, -canvas.height, canvas.width * 2, canvas.height * 2);
  // red line from (ax, ay) to (bx, by)
  context.beginPath();
  context.strokeStyle = "#ff0000";
  context.moveTo(stickModel._props.mouse_pos.x, stickModel._props.mouse_pos.y);
  context.lineTo(stickModel._props.seeker.x, stickModel._props.seeker.y);
  context.fillText('mouse_pos x:' + stickModel._props.mouse_pos.x + ' y: ' + stickModel._props.mouse_pos.y, stickModel._props.mouse_pos.x, stickModel._props.mouse_pos.y);
  context.fillText('seeker x:' + stickModel._props.seeker.x + ' y: ' + stickModel._props.seeker.y, stickModel._props.seeker.x - 30, stickModel._props.seeker.y);
  context.lineWidth = 1;
  context.stroke();
  context.closePath();
  // green line from (ax, ay) to (bx0, by0)
  context.beginPath();
  context.strokeStyle = "#00ff00";
  context.moveTo(stickModel._props.mouse_pos.x, stickModel._props.mouse_pos.y);
  context.lineTo(stickModel._props.seeker.echo_x, stickModel._props.seeker.echo_y);
  context.fillText('echo x:' + stickModel._props.seeker.echo_x + ' y: ' + stickModel._props.seeker.echo_y, stickModel._props.seeker.echo_x, stickModel._props.seeker.echo_y - 20);
  context.lineWidth = 1;
  context.stroke();
  context.closePath();
  // blue line from (bx0, by0) to (bx, by)
  context.beginPath();
  context.strokeStyle = "#0000ff";
  context.moveTo(stickModel._props.seeker.echo_x, stickModel._props.seeker.echo_y);
  context.lineTo(stickModel._props.seeker.x, stickModel._props.seeker.y);
  context.stroke();
  context.closePath();
}
body {
  margin: 0px;
  padding: 0px;
}
canvas {
  display: block;
}
p {
  position: absolute;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
<p>Move your mouse to see the stick (colored red) follow</p>
<canvas id="myCanvas"></canvas>